[CLEANUP] The correct case must be used for standard PHP types in phpdoc
[Packages/TYPO3.CMS.git] / typo3 / sysext / extbase / Classes / Persistence / ObjectStorage.php
1 <?php
2 namespace TYPO3\CMS\Extbase\Persistence;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 /**
18 * The storage for objects. It ensures the uniqueness of an object in the storage. It's a remake of the
19 * SplObjectStorage introduced in PHP 5.3.
20 *
21 * Opposed to the SplObjectStorage the ObjectStorage does not implement the Serializable interface.
22 */
23 class ObjectStorage implements \Countable, \Iterator, \ArrayAccess, ObjectMonitoringInterface
24 {
25 /**
26 * This field is only needed to make debugging easier:
27 * If you call current() on a class that implements Iterator, PHP will return the first field of the object
28 * instead of calling the current() method of the interface.
29 * We use this unusual behavior of PHP to return the warning below in this case.
30 *
31 * @var string
32 */
33 private $warning = 'You should never see this warning. If you do, you probably used PHP array functions like current() on the TYPO3\\CMS\\Extbase\\Persistence\\ObjectStorage. To retrieve the first result, you can use the rewind() and current() methods.';
34
35 /**
36 * An array holding the objects and the stored information. The key of the array items ist the
37 * spl_object_hash of the given object.
38 *
39 * array(
40 * spl_object_hash =>
41 * array(
42 * 'obj' => $object,
43 * 'inf' => $information
44 * )
45 * )
46 *
47 * @var array
48 */
49 protected $storage = [];
50
51 /**
52 * A flag indication if the object storage was modified after reconstitution (eg. by adding a new object)
53 *
54 * @var bool
55 */
56 protected $isModified = false;
57
58 /**
59 * An array holding the internal position the object was added.
60 * The object entry is unsetted when the object gets removed from the objectstorage
61 *
62 * @var array
63 */
64 protected $addedObjectsPositions = [];
65
66 /**
67 * An array holding the internal position the object was added before, when it would
68 * be removed from the objectstorage
69 *
70 * @var array
71 */
72 protected $removedObjectsPositions = [];
73
74 /**
75 * An internal var holding the count of added objects to be stored as position.
76 * It would be resetted, when all objects will be removed from the objectstorage
77 *
78 * @var int
79 */
80 protected $positionCounter = 0;
81
82 /**
83 * Rewinds the iterator to the first storage element.
84 */
85 public function rewind()
86 {
87 reset($this->storage);
88 }
89
90 /**
91 * Checks if the array pointer of the storage points to a valid position.
92 *
93 * @return bool
94 */
95 public function valid()
96 {
97 return current($this->storage) !== false;
98 }
99
100 /**
101 * Returns the index at which the iterator currently is.
102 *
103 * This is different from the SplObjectStorage as the key in this implementation is the object hash (string).
104 *
105 * @return string The index corresponding to the position of the iterator.
106 */
107 public function key()
108 {
109 return key($this->storage);
110 }
111
112 /**
113 * Returns the current storage entry.
114 *
115 * @return object The object at the current iterator position.
116 */
117 public function current()
118 {
119 $item = current($this->storage);
120 return $item['obj'];
121 }
122
123 /**
124 * Moves to the next entry.
125 */
126 public function next()
127 {
128 next($this->storage);
129 }
130
131 /**
132 * Returns the number of objects in the storage.
133 *
134 * @return int The number of objects in the storage.
135 */
136 public function count()
137 {
138 return count($this->storage);
139 }
140
141 /**
142 * Associates data to an object in the storage. offsetSet() is an alias of attach().
143 *
144 * @param object $object The object to add.
145 * @param mixed $information The data to associate with the object.
146 */
147 public function offsetSet($object, $information)
148 {
149 $this->isModified = true;
150 $this->storage[spl_object_hash($object)] = ['obj' => $object, 'inf' => $information];
151
152 $this->positionCounter++;
153 $this->addedObjectsPositions[spl_object_hash($object)] = $this->positionCounter;
154 }
155
156 /**
157 * Checks whether an object exists in the storage.
158 *
159 * @param object $object The object to look for.
160 * @return bool
161 */
162 public function offsetExists($object)
163 {
164 return is_object($object) && isset($this->storage[spl_object_hash($object)]);
165 }
166
167 /**
168 * Removes an object from the storage. offsetUnset() is an alias of detach().
169 *
170 * @param object $object The object to remove.
171 */
172 public function offsetUnset($object)
173 {
174 $this->isModified = true;
175 unset($this->storage[spl_object_hash($object)]);
176
177 if (empty($this->storage)) {
178 $this->positionCounter = 0;
179 }
180
181 $this->removedObjectsPositions[spl_object_hash($object)] = $this->addedObjectsPositions[spl_object_hash($object)];
182 unset($this->addedObjectsPositions[spl_object_hash($object)]);
183 }
184
185 /**
186 * Returns the data associated with an object.
187 *
188 * @param object $object The object to look for.
189 * @return mixed The data associated with an object in the storage.
190 */
191 public function offsetGet($object)
192 {
193 return $this->storage[spl_object_hash($object)]['inf'];
194 }
195
196 /**
197 * Checks if the storage contains a specific object.
198 *
199 * @param object $object The object to look for.
200 * @return bool
201 */
202 public function contains($object)
203 {
204 return $this->offsetExists($object);
205 }
206
207 /**
208 * Adds an object in the storage, and optionaly associate it to some data.
209 *
210 * @param object $object The object to add.
211 * @param mixed $information The data to associate with the object.
212 */
213 public function attach($object, $information = null)
214 {
215 $this->offsetSet($object, $information);
216 }
217
218 /**
219 * Removes an object from the storage.
220 *
221 * @param object $object The object to remove.
222 */
223 public function detach($object)
224 {
225 $this->offsetUnset($object);
226 }
227
228 /**
229 * Returns the data, or info, associated with the object pointed by the current iterator position.
230 *
231 * @return mixed The data associated with the current iterator position.
232 */
233 public function getInfo()
234 {
235 $item = current($this->storage);
236 return $item['inf'];
237 }
238
239 /**
240 * Associates data, or info, with the object currently pointed to by the iterator.
241 *
242 * @param mixed $data
243 */
244 public function setInfo($data)
245 {
246 $this->isModified = true;
247 $key = key($this->storage);
248 $this->storage[$key]['inf'] = $data;
249 }
250
251 /**
252 * Adds all objects-data pairs from a different storage in the current storage.
253 *
254 * @param ObjectStorage $objectStorage
255 */
256 public function addAll(ObjectStorage $objectStorage)
257 {
258 foreach ($objectStorage as $object) {
259 $this->attach($object, $objectStorage->getInfo());
260 }
261 }
262
263 /**
264 * Removes objects contained in another storage from the current storage.
265 *
266 * @param ObjectStorage $objectStorage The storage containing the elements to remove.
267 */
268 public function removeAll(ObjectStorage $objectStorage)
269 {
270 foreach ($objectStorage as $object) {
271 $this->detach($object);
272 }
273 }
274
275 /**
276 * Returns this object storage as an array
277 *
278 * @return array The object storage
279 */
280 public function toArray()
281 {
282 $array = [];
283 $storage = array_values($this->storage);
284 foreach ($storage as $item) {
285 $array[] = $item['obj'];
286 }
287 return $array;
288 }
289
290 /**
291 * Alias of toArray which allows that method to be used from contexts which support
292 * for example dotted paths, e.g. ObjectAccess::getPropertyPath($object, 'children.array.123')
293 * to get exactly the 123rd item in the "children" property which is an ObjectStorage.
294 *
295 * @return array
296 */
297 public function getArray()
298 {
299 return $this->toArray();
300 }
301
302 /**
303 * Dummy method to avoid serialization.
304 *
305 * @throws \RuntimeException
306 */
307 public function serialize()
308 {
309 throw new \RuntimeException('An ObjectStorage instance cannot be serialized.', 1267700868);
310 }
311
312 /**
313 * Dummy method to avoid unserialization.
314 *
315 * @param string $serialized
316 * @throws \RuntimeException
317 */
318 public function unserialize($serialized)
319 {
320 throw new \RuntimeException('A ObjectStorage instance cannot be unserialized.', 1267700870);
321 }
322
323 /**
324 * Register the storage's clean state, e.g. after it has been reconstituted from the database.
325 */
326 public function _memorizeCleanState()
327 {
328 $this->isModified = false;
329 }
330
331 /**
332 * Returns TRUE if the storage was modified after reconstitution.
333 *
334 * @return bool
335 */
336 public function _isDirty()
337 {
338 return $this->isModified;
339 }
340
341 /**
342 * Returns TRUE if an object is added, then removed and added at a different position
343 *
344 * @param mixed $object
345 * @return bool
346 */
347 public function isRelationDirty($object)
348 {
349 return isset($this->addedObjectsPositions[spl_object_hash($object)])
350 && isset($this->removedObjectsPositions[spl_object_hash($object)])
351 && ($this->addedObjectsPositions[spl_object_hash($object)] !== $this->removedObjectsPositions[spl_object_hash($object)]);
352 }
353
354 /**
355 * @param mixed $object
356 * @return int|null
357 */
358 public function getPosition($object)
359 {
360 if (!isset($this->addedObjectsPositions[spl_object_hash($object)])) {
361 return null;
362 }
363
364 return $this->addedObjectsPositions[spl_object_hash($object)];
365 }
366 }