Merge "Add support for 'hu-formal'"
[lhc/web/wiklou.git] / includes / Storage / RevisionSlots.php
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
23 namespace MediaWiki\Storage;
24
25 use Content;
26 use LogicException;
27 use Wikimedia\Assert\Assert;
28
29 /**
30 * Value object representing the set of slots belonging to a revision.
31 *
32 * @since 1.31
33 */
34 class RevisionSlots {
35
36 /** @var SlotRecord[]|callable */
37 protected $slots;
38
39 /**
40 * @param SlotRecord[]|callable $slots SlotRecords,
41 * or a callback that returns such a structure.
42 */
43 public function __construct( $slots ) {
44 Assert::parameterType( 'array|callable', $slots, '$slots' );
45
46 if ( is_callable( $slots ) ) {
47 $this->slots = $slots;
48 } else {
49 $this->setSlotsInternal( $slots );
50 }
51 }
52
53 /**
54 * @param SlotRecord[] $slots
55 */
56 private function setSlotsInternal( array $slots ) {
57 $this->slots = [];
58
59 // re-key the slot array
60 foreach ( $slots as $slot ) {
61 $role = $slot->getRole();
62 $this->slots[$role] = $slot;
63 }
64 }
65
66 /**
67 * Implemented to defy serialization.
68 *
69 * @throws LogicException always
70 */
71 public function __sleep() {
72 throw new LogicException( __CLASS__ . ' is not serializable.' );
73 }
74
75 /**
76 * Returns the Content of the given slot.
77 * Call getSlotNames() to get a list of available slots.
78 *
79 * Note that for mutable Content objects, each call to this method will return a
80 * fresh clone.
81 *
82 * @param string $role The role name of the desired slot
83 *
84 * @throws RevisionAccessException if the slot does not exist or slot data
85 * could not be lazy-loaded.
86 * @return Content
87 */
88 public function getContent( $role ) {
89 // Return a copy to be safe. Immutable content objects return $this from copy().
90 return $this->getSlot( $role )->getContent()->copy();
91 }
92
93 /**
94 * Returns the SlotRecord of the given slot.
95 * Call getSlotNames() to get a list of available slots.
96 *
97 * @param string $role The role name of the desired slot
98 *
99 * @throws RevisionAccessException if the slot does not exist or slot data
100 * could not be lazy-loaded.
101 * @return SlotRecord
102 */
103 public function getSlot( $role ) {
104 $slots = $this->getSlots();
105
106 if ( isset( $slots[$role] ) ) {
107 return $slots[$role];
108 } else {
109 throw new RevisionAccessException( 'No such slot: ' . $role );
110 }
111 }
112
113 /**
114 * Returns whether the given slot is set.
115 *
116 * @param string $role The role name of the desired slot
117 *
118 * @return bool
119 */
120 public function hasSlot( $role ) {
121 $slots = $this->getSlots();
122
123 return isset( $slots[$role] );
124 }
125
126 /**
127 * Returns the slot names (roles) of all slots present in this revision.
128 * getContent() will succeed only for the names returned by this method.
129 *
130 * @return string[]
131 */
132 public function getSlotRoles() {
133 $slots = $this->getSlots();
134 return array_keys( $slots );
135 }
136
137 /**
138 * Computes the total nominal size of the revision's slots, in bogo-bytes.
139 *
140 * @warn This is potentially expensive! It may cause all slot's content to be loaded
141 * and deserialized.
142 *
143 * @return int
144 */
145 public function computeSize() {
146 return array_reduce( $this->getSlots(), function ( $accu, SlotRecord $slot ) {
147 return $accu + $slot->getSize();
148 }, 0 );
149 }
150
151 /**
152 * Returns an associative array that maps role names to SlotRecords. Each SlotRecord
153 * represents the content meta-data of a slot, together they define the content of
154 * a revision.
155 *
156 * @note This may cause the content meta-data for the revision to be lazy-loaded.
157 *
158 * @return SlotRecord[] revision slot/content rows, keyed by slot role name.
159 */
160 public function getSlots() {
161 if ( is_callable( $this->slots ) ) {
162 $slots = call_user_func( $this->slots );
163
164 Assert::postcondition(
165 is_array( $slots ),
166 'Slots info callback should return an array of objects'
167 );
168
169 $this->setSlotsInternal( $slots );
170 }
171
172 return $this->slots;
173 }
174
175 /**
176 * Computes the combined hash of the revisions's slots.
177 *
178 * @note For backwards compatibility, the combined hash of a single slot
179 * is that slot's hash. For consistency, the combined hash of an empty set of slots
180 * is the hash of the empty string.
181 *
182 * @warn This is potentially expensive! It may cause all slot's content to be loaded
183 * and deserialized, then re-serialized and hashed.
184 *
185 * @return string
186 */
187 public function computeSha1() {
188 $slots = $this->getSlots();
189 ksort( $slots );
190
191 if ( empty( $slots ) ) {
192 return SlotRecord::base36Sha1( '' );
193 }
194
195 return array_reduce( $slots, function ( $accu, SlotRecord $slot ) {
196 return $accu === null
197 ? $slot->getSha1()
198 : SlotRecord::base36Sha1( $accu . $slot->getSha1() );
199 }, null );
200 }
201
202 }