RevisionSlots.php 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. <?php
  2. /**
  3. * Value object representing the set of slots belonging to a revision.
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License along
  16. * with this program; if not, write to the Free Software Foundation, Inc.,
  17. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  18. * http://www.gnu.org/copyleft/gpl.html
  19. *
  20. * @file
  21. */
  22. namespace MediaWiki\Storage;
  23. use Content;
  24. use LogicException;
  25. use Wikimedia\Assert\Assert;
  26. /**
  27. * Value object representing the set of slots belonging to a revision.
  28. *
  29. * @since 1.31
  30. */
  31. class RevisionSlots {
  32. /** @var SlotRecord[]|callable */
  33. protected $slots;
  34. /**
  35. * @param SlotRecord[]|callable $slots SlotRecords,
  36. * or a callback that returns such a structure.
  37. */
  38. public function __construct( $slots ) {
  39. Assert::parameterType( 'array|callable', $slots, '$slots' );
  40. if ( is_callable( $slots ) ) {
  41. $this->slots = $slots;
  42. } else {
  43. $this->setSlotsInternal( $slots );
  44. }
  45. }
  46. /**
  47. * @param SlotRecord[] $slots
  48. */
  49. private function setSlotsInternal( array $slots ) {
  50. $this->slots = [];
  51. // re-key the slot array
  52. foreach ( $slots as $slot ) {
  53. $role = $slot->getRole();
  54. $this->slots[$role] = $slot;
  55. }
  56. }
  57. /**
  58. * Implemented to defy serialization.
  59. *
  60. * @throws LogicException always
  61. */
  62. public function __sleep() {
  63. throw new LogicException( __CLASS__ . ' is not serializable.' );
  64. }
  65. /**
  66. * Returns the Content of the given slot.
  67. * Call getSlotNames() to get a list of available slots.
  68. *
  69. * Note that for mutable Content objects, each call to this method will return a
  70. * fresh clone.
  71. *
  72. * @param string $role The role name of the desired slot
  73. *
  74. * @throws RevisionAccessException if the slot does not exist or slot data
  75. * could not be lazy-loaded.
  76. * @return Content
  77. */
  78. public function getContent( $role ) {
  79. // Return a copy to be safe. Immutable content objects return $this from copy().
  80. return $this->getSlot( $role )->getContent()->copy();
  81. }
  82. /**
  83. * Returns the SlotRecord of the given slot.
  84. * Call getSlotNames() to get a list of available slots.
  85. *
  86. * @param string $role The role name of the desired slot
  87. *
  88. * @throws RevisionAccessException if the slot does not exist or slot data
  89. * could not be lazy-loaded.
  90. * @return SlotRecord
  91. */
  92. public function getSlot( $role ) {
  93. $slots = $this->getSlots();
  94. if ( isset( $slots[$role] ) ) {
  95. return $slots[$role];
  96. } else {
  97. throw new RevisionAccessException( 'No such slot: ' . $role );
  98. }
  99. }
  100. /**
  101. * Returns whether the given slot is set.
  102. *
  103. * @param string $role The role name of the desired slot
  104. *
  105. * @return bool
  106. */
  107. public function hasSlot( $role ) {
  108. $slots = $this->getSlots();
  109. return isset( $slots[$role] );
  110. }
  111. /**
  112. * Returns the slot names (roles) of all slots present in this revision.
  113. * getContent() will succeed only for the names returned by this method.
  114. *
  115. * @return string[]
  116. */
  117. public function getSlotRoles() {
  118. $slots = $this->getSlots();
  119. return array_keys( $slots );
  120. }
  121. /**
  122. * Computes the total nominal size of the revision's slots, in bogo-bytes.
  123. *
  124. * @warn This is potentially expensive! It may cause all slot's content to be loaded
  125. * and deserialized.
  126. *
  127. * @return int
  128. */
  129. public function computeSize() {
  130. return array_reduce( $this->getSlots(), function ( $accu, SlotRecord $slot ) {
  131. return $accu + $slot->getSize();
  132. }, 0 );
  133. }
  134. /**
  135. * Returns an associative array that maps role names to SlotRecords. Each SlotRecord
  136. * represents the content meta-data of a slot, together they define the content of
  137. * a revision.
  138. *
  139. * @note This may cause the content meta-data for the revision to be lazy-loaded.
  140. *
  141. * @return SlotRecord[] revision slot/content rows, keyed by slot role name.
  142. */
  143. public function getSlots() {
  144. if ( is_callable( $this->slots ) ) {
  145. $slots = call_user_func( $this->slots );
  146. Assert::postcondition(
  147. is_array( $slots ),
  148. 'Slots info callback should return an array of objects'
  149. );
  150. $this->setSlotsInternal( $slots );
  151. }
  152. return $this->slots;
  153. }
  154. /**
  155. * Computes the combined hash of the revisions's slots.
  156. *
  157. * @note For backwards compatibility, the combined hash of a single slot
  158. * is that slot's hash. For consistency, the combined hash of an empty set of slots
  159. * is the hash of the empty string.
  160. *
  161. * @warn This is potentially expensive! It may cause all slot's content to be loaded
  162. * and deserialized, then re-serialized and hashed.
  163. *
  164. * @return string
  165. */
  166. public function computeSha1() {
  167. $slots = $this->getSlots();
  168. ksort( $slots );
  169. if ( empty( $slots ) ) {
  170. return SlotRecord::base36Sha1( '' );
  171. }
  172. return array_reduce( $slots, function ( $accu, SlotRecord $slot ) {
  173. return $accu === null
  174. ? $slot->getSha1()
  175. : SlotRecord::base36Sha1( $accu . $slot->getSha1() );
  176. }, null );
  177. }
  178. }