3c70da18104b32a9060ff0794569ba071886fe44
[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 * The storage for objects. It ensures the uniqueness of an object in the storage. It's a remake of the
18 * SplObjectStorage introduced in PHP 5.3.
19 *
20 * Opposed to the SplObjectStorage the ObjectStorage does not implement the Serializable interface.
21 */
22 class ObjectStorage implements \Countable, \Iterator, \ArrayAccess, ObjectMonitoringInterface {
23
24 /**
25 * This field is only needed to make debugging easier:
26 * If you call current() on a class that implements Iterator, PHP will return the first field of the object
27 * instead of calling the current() method of the interface.
28 * We use this unusual behavior of PHP to return the warning below in this case.
29 *
30 * @var string
31 */
32 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.';
33
34 /**
35 * An array holding the objects and the stored information. The key of the array items ist the
36 * spl_object_hash of the given object.
37 *
38 * array(
39 * spl_object_hash =>
40 * array(
41 * 'obj' => $object,
42 * 'inf' => $information
43 * )
44 * )
45 *
46 * @var array
47 */
48 protected $storage = array();
49
50 /**
51 * A flag indication if the object storage was modified after reconstitution (eg. by adding a new object)
52 *
53 * @var bool
54 */
55 protected $isModified = FALSE;
56
57 /**
58 * An array holding the internal position the object was added.
59 * The object entry is unsetted when the object gets removed from the objectstorage
60 *
61 * @var array
62 */
63 protected $addedObjectsPositions = array();
64
65 /**
66 * An array holding the internal position the object was added before, when it would
67 * be removed from the objectstorage
68 *
69 * @var array
70 */
71 protected $removedObjectsPositions = array();
72
73 /**
74 * An internal var holding the count of added objects to be stored as position.
75 * It would be resetted, when all objects will be removed from the objectstorage
76 *
77 * @var int
78 */
79 protected $positionCounter = 0;
80
81 /**
82 * Rewinds the iterator to the first storage element.
83 *
84 * @return void
85 */
86 public function rewind() {
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 return current($this->storage) !== FALSE;
97 }
98
99 /**
100 * Returns the index at which the iterator currently is.
101 *
102 * This is different from the SplObjectStorage as the key in this implementation is the object hash (string).
103 *
104 * @return string The index corresponding to the position of the iterator.
105 */
106 public function key() {
107 return key($this->storage);
108 }
109
110 /**
111 * Returns the current storage entry.
112 *
113 * @return object The object at the current iterator position.
114 */
115 public function current() {
116 $item = current($this->storage);
117 return $item['obj'];
118 }
119
120 /**
121 * Moves to the next entry.
122 *
123 * @return void
124 */
125 public function next() {
126 next($this->storage);
127 }
128
129 /**
130 * Returns the number of objects in the storage.
131 *
132 * @return int The number of objects in the storage.
133 */
134 public function count() {
135 return count($this->storage);
136 }
137
138 /**
139 * Associates data to an object in the storage. offsetSet() is an alias of attach().
140 *
141 * @param object $object The object to add.
142 * @param mixed $information The data to associate with the object.
143 * @return void
144 */
145 public function offsetSet($object, $information) {
146 $this->isModified = TRUE;
147 $this->storage[spl_object_hash($object)] = array('obj' => $object, 'inf' => $information);
148
149 $this->positionCounter++;
150 $this->addedObjectsPositions[spl_object_hash($object)] = $this->positionCounter;
151 }
152
153 /**
154 * Checks whether an object exists in the storage.
155 *
156 * @param object $object The object to look for.
157 * @return bool
158 */
159 public function offsetExists($object) {
160 return isset($this->storage[spl_object_hash($object)]);
161 }
162
163 /**
164 * Removes an object from the storage. offsetUnset() is an alias of detach().
165 *
166 * @param object $object The object to remove.
167 * @return void
168 */
169 public function offsetUnset($object) {
170 $this->isModified = TRUE;
171 unset($this->storage[spl_object_hash($object)]);
172
173 if (empty($this->storage)) {
174 $this->positionCounter = 0;
175 }
176
177 $this->removedObjectsPositions[spl_object_hash($object)] = $this->addedObjectsPositions[spl_object_hash($object)];
178 unset($this->addedObjectsPositions[spl_object_hash($object)]);
179 }
180
181 /**
182 * Returns the data associated with an object.
183 *
184 * @param object $object The object to look for.
185 * @return mixed The data associated with an object in the storage.
186 */
187 public function offsetGet($object) {
188 return $this->storage[spl_object_hash($object)]['inf'];
189 }
190
191 /**
192 * Checks if the storage contains a specific object.
193 *
194 * @param object $object The object to look for.
195 * @return bool
196 */
197 public function contains($object) {
198 return $this->offsetExists($object);
199 }
200
201 /**
202 * Adds an object in the storage, and optionaly associate it to some data.
203 *
204 * @param object $object The object to add.
205 * @param mixed $information The data to associate with the object.
206 * @return void
207 */
208 public function attach($object, $information = NULL) {
209 $this->offsetSet($object, $information);
210 }
211
212 /**
213 * Removes an object from the storage.
214 *
215 * @param object $object The object to remove.
216 * @return void
217 */
218 public function detach($object) {
219 $this->offsetUnset($object);
220 }
221
222 /**
223 * Returns the data, or info, associated with the object pointed by the current iterator position.
224 *
225 * @return mixed The data associated with the current iterator position.
226 */
227 public function getInfo() {
228 $item = current($this->storage);
229 return $item['inf'];
230 }
231
232 /**
233 * Associates data, or info, with the object currently pointed to by the iterator.
234 *
235 * @param mixed $data
236 * @return void
237 */
238 public function setInfo($data) {
239 $this->isModified = TRUE;
240 $key = key($this->storage);
241 $this->storage[$key]['inf'] = $data;
242 }
243
244 /**
245 * Adds all objects-data pairs from a different storage in the current storage.
246 *
247 * @param ObjectStorage $objectStorage
248 * @return void
249 */
250 public function addAll(ObjectStorage $objectStorage) {
251 foreach ($objectStorage as $object) {
252 $this->attach($object, $objectStorage->getInfo());
253 }
254 }
255
256 /**
257 * Removes objects contained in another storage from the current storage.
258 *
259 * @param ObjectStorage $objectStorage The storage containing the elements to remove.
260 * @return void
261 */
262 public function removeAll(ObjectStorage $objectStorage) {
263 foreach ($objectStorage as $object) {
264 $this->detach($object);
265 }
266 }
267
268 /**
269 * Returns this object storage as an array
270 *
271 * @return array The object storage
272 */
273 public function toArray() {
274 $array = array();
275 $storage = array_values($this->storage);
276 foreach ($storage as $item) {
277 $array[] = $item['obj'];
278 }
279 return $array;
280 }
281
282 /**
283 * Dummy method to avoid serialization.
284 *
285 * @throws \RuntimeException
286 * @return void
287 */
288 public function serialize() {
289 throw new \RuntimeException('An ObjectStorage instance cannot be serialized.', 1267700868);
290 }
291
292 /**
293 * Dummy method to avoid unserialization.
294 *
295 * @param string $serialized
296 * @throws \RuntimeException
297 * @return void
298 */
299 public function unserialize($serialized) {
300 throw new \RuntimeException('A ObjectStorage instance cannot be unserialized.', 1267700870);
301 }
302
303 /**
304 * Register the storage's clean state, e.g. after it has been reconstituted from the database.
305 *
306 * @return void
307 */
308 public function _memorizeCleanState() {
309 $this->isModified = FALSE;
310 }
311
312 /**
313 * Returns TRUE if the storage was modified after reconstitution.
314 *
315 * @return bool
316 */
317 public function _isDirty() {
318 return $this->isModified;
319 }
320
321 /**
322 * Returns TRUE if an object is added, then removed and added at a different position
323 *
324 * @param mixed $object
325 * @return bool
326 */
327 public function isRelationDirty($object) {
328 return (isset($this->addedObjectsPositions[spl_object_hash($object)])
329 && isset($this->removedObjectsPositions[spl_object_hash($object)])
330 && ($this->addedObjectsPositions[spl_object_hash($object)] !== $this->removedObjectsPositions[spl_object_hash($object)]));
331 }
332
333 /**
334 * @param mixed $object
335 * @return int|NULL
336 */
337 public function getPosition($object) {
338 if (!isset($this->addedObjectsPositions[spl_object_hash($object)])) {
339 return NULL;
340 }
341
342 return $this->addedObjectsPositions[spl_object_hash($object)];
343 }
344 }