Joomla Platform  13.1
Documentation des API du framework Joomla Platform
 Tout Classes Espaces de nommage Fichiers Fonctions Variables Pages
set.php
Aller à la documentation de ce fichier.
1 <?php
2 /**
3  * @package Joomla.Platform
4  * @subpackage Data
5  *
6  * @copyright Copyright (C) 2005 - 2011 Open Source Matters, Inc. All rights reserved.
7  * @license GNU General Public License version 2 or later; see LICENSE
8  */
9 
10 defined('JPATH_PLATFORM') or die;
11 
12 /**
13  * JDataSet is a collection class that allows the developer to operate on a set of JData objects as if they were in a
14  * typical PHP array.
15  *
16  * @package Joomla.Platform
17  * @subpackage Data
18  * @since 12.3
19  */
20 class JDataSet implements JDataDumpable, ArrayAccess, Countable, Iterator
21 {
22  /**
23  * The current position of the iterator.
24  *
25  * @var integer
26  * @since 12.3
27  */
28  private $_current = false;
29 
30  /**
31  * The iterator objects.
32  *
33  * @var array
34  * @since 12.3
35  */
36  private $_objects = array();
37 
38  /**
39  * The class constructor.
40  *
41  * @param array $objects An array of JData objects to bind to the data set.
42  *
43  * @since 12.3
44  * @throws InvalidArgumentException if an object is not an instance of JData.
45  */
46  public function __construct(array $objects = array())
47  {
48  // Set the objects.
49  $this->_initialise($objects);
50  }
51 
52  /**
53  * The magic call method is used to call object methods using the iterator.
54  *
55  * Example: $array = $objectList->foo('bar');
56  *
57  * The object list will iterate over its objects and see if each object has a callable 'foo' method.
58  * If so, it will pass the argument list and assemble any return values. If an object does not have
59  * a callable method no return value is recorded.
60  * The keys of the objects and the result array are maintained.
61  *
62  * @param string $method The name of the method called.
63  * @param array $arguments The arguments of the method called.
64  *
65  * @return array An array of values returned by the methods called on the objects in the data set.
66  *
67  * @since 12.3
68  */
69  public function __call($method, $arguments = array())
70  {
71  $return = array();
72 
73  // Iterate through the objects.
74  foreach ($this->_objects as $key => $object)
75  {
76  // Create the object callback.
77  $callback = array($object, $method);
78 
79  // Check if the callback is callable.
80  if (is_callable($callback))
81  {
82  // Call the method for the object.
83  $return[$key] = call_user_func_array($callback, $arguments);
84  }
85  }
86 
87  return $return;
88  }
89 
90  /**
91  * The magic get method is used to get a list of properties from the objects in the data set.
92  *
93  * Example: $array = $dataSet->foo;
94  *
95  * This will return a column of the values of the 'foo' property in all the objects
96  * (or values determined by custom property setters in the individual JData's).
97  * The result array will contain an entry for each object in the list (compared to __call which may not).
98  * The keys of the objects and the result array are maintained.
99  *
100  * @param string $property The name of the data property.
101  *
102  * @return array An associative array of the values.
103  *
104  * @since 12.3
105  */
106  public function __get($property)
107  {
108  $return = array();
109 
110  // Iterate through the objects.
111  foreach ($this->_objects as $key => $object)
112  {
113  // Get the property.
114  $return[$key] = $object->$property;
115  }
116 
117  return $return;
118  }
119 
120  /**
121  * The magic isset method is used to check the state of an object property using the iterator.
122  *
123  * Example: $array = isset($objectList->foo);
124  *
125  * @param string $property The name of the property.
126  *
127  * @return boolean True if the property is set in any of the objects in the data set.
128  *
129  * @since 12.3
130  */
131  public function __isset($property)
132  {
133  $return = array();
134 
135  // Iterate through the objects.
136  foreach ($this->_objects as $object)
137  {
138  // Check the property.
139  $return[] = isset($object->$property);
140  }
141 
142  return in_array(true, $return, true) ? true : false;
143  }
144 
145  /**
146  * The magic set method is used to set an object property using the iterator.
147  *
148  * Example: $objectList->foo = 'bar';
149  *
150  * This will set the 'foo' property to 'bar' in all of the objects
151  * (or a value determined by custom property setters in the JData).
152  *
153  * @param string $property The name of the property.
154  * @param mixed $value The value to give the data property.
155  *
156  * @return void
157  *
158  * @since 12.3
159  */
160  public function __set($property, $value)
161  {
162  // Iterate through the objects.
163  foreach ($this->_objects as $object)
164  {
165  // Set the property.
166  $object->$property = $value;
167  }
168  }
169 
170  /**
171  * The magic unset method is used to unset an object property using the iterator.
172  *
173  * Example: unset($objectList->foo);
174  *
175  * This will unset all of the 'foo' properties in the list of JData's.
176  *
177  * @param string $property The name of the property.
178  *
179  * @return void
180  *
181  * @since 12.3
182  */
183  public function __unset($property)
184  {
185  // Iterate through the objects.
186  foreach ($this->_objects as $object)
187  {
188  unset($object->$property);
189  }
190  }
191 
192  /**
193  * Gets the number of data objects in the set.
194  *
195  * @return integer The number of objects.
196  *
197  * @since 12.3
198  */
199  public function count()
200  {
201  return count($this->_objects);
202  }
203 
204  /**
205  * Clears the objects in the data set.
206  *
207  * @return JDataSet Returns itself to allow chaining.
208  *
209  * @since 12.3
210  */
211  public function clear()
212  {
213  $this->_objects = array();
214  $this->rewind();
215 
216  return $this;
217  }
218 
219  /**
220  * Get the current data object in the set.
221  *
222  * @return JData The current object, or false if the array is empty or the pointer is beyond the end of the elements.
223  *
224  * @since 12.3
225  */
226  public function current()
227  {
228  return is_scalar($this->_current) ? $this->_objects[$this->_current] : false;
229  }
230 
231  /**
232  * Dumps the data object in the set, recursively if appropriate.
233  *
234  * @param integer $depth The maximum depth of recursion (default = 3).
235  * For example, a depth of 0 will return a stdClass with all the properties in native
236  * form. A depth of 1 will recurse into the first level of properties only.
237  * @param SplObjectStorage $dumped An array of already serialized objects that is used to avoid infinite loops.
238  *
239  * @return array An associative array of the date objects in the set, dumped as a simple PHP stdClass object.
240  *
241  * @see JData::dump()
242  * @since 12.3
243  */
244  public function dump($depth = 3, SplObjectStorage $dumped = null)
245  {
246  // Check if we should initialise the recursion tracker.
247  if ($dumped === null)
248  {
249  $dumped = new SplObjectStorage;
250  }
251 
252  // Add this object to the dumped stack.
253  $dumped->attach($this);
254 
255  $objects = array();
256 
257  // Make sure that we have not reached our maximum depth.
258  if ($depth > 0)
259  {
260  // Handle JSON serialization recursively.
261  foreach ($this->_objects as $key => $object)
262  {
263  $objects[$key] = $object->dump($depth, $dumped);
264  }
265  }
266 
267  return $objects;
268  }
269 
270  /**
271  * Gets the data set in a form that can be serialised to JSON format.
272  *
273  * Note that this method will not return an associative array, otherwise it would be encoded into an object.
274  * JSON decoders do not consistently maintain the order of associative keys, whereas they do maintain the order of arrays.
275  *
276  * @param mixed $serialized An array of objects that have already been serialized that is used to infinite loops
277  * (null on first call).
278  *
279  * @return array An array that can be serialised by json_encode().
280  *
281  * @since 12.3
282  */
283  public function jsonSerialize($serialized = null)
284  {
285  // Check if we should initialise the recursion tracker.
286  if ($serialized === null)
287  {
288  $serialized = array();
289  }
290 
291  // Add this object to the serialized stack.
292  $serialized[] = spl_object_hash($this);
293  $return = array();
294 
295  // Iterate through the objects.
296  foreach ($this->_objects as $object)
297  {
298  // Call the method for the object.
299  $return[] = $object->jsonSerialize($serialized);
300  }
301 
302  return $return;
303  }
304 
305  /**
306  * Gets the key of the current object in the iterator.
307  *
308  * @return scalar The object key on success; null on failure.
309  *
310  * @since 12.3
311  */
312  public function key()
313  {
314  return $this->_current;
315  }
316 
317  /**
318  * Gets the array of keys for all the objects in the iterator (emulates array_keys).
319  *
320  * @return array The array of keys
321  *
322  * @since 12.3
323  */
324  public function keys()
325  {
326  return array_keys($this->_objects);
327  }
328 
329  /**
330  * Advances the iterator to the next object in the iterator.
331  *
332  * @return void
333  *
334  * @since 12.3
335  */
336  public function next()
337  {
338  // Get the object offsets.
339  $keys = $this->keys();
340 
341  // Check if _current has been set to false but offsetUnset.
342  if ($this->_current === false && isset($keys[0]))
343  {
344  // This is a special case where offsetUnset was used in a foreach loop and the first element was unset.
345  $this->_current = $keys[0];
346  }
347  else
348  {
349  // Get the current key.
350  $position = array_search($this->_current, $keys);
351 
352  // Check if there is an object after the current object.
353  if ($position !== false && isset($keys[$position + 1]))
354  {
355  // Get the next id.
356  $this->_current = $keys[$position + 1];
357  }
358  else
359  {
360  // That was the last object or the internal properties have become corrupted.
361  $this->_current = null;
362  }
363  }
364  }
365 
366  /**
367  * Checks whether an offset exists in the iterator.
368  *
369  * @param mixed $offset The object offset.
370  *
371  * @return boolean True if the object exists, false otherwise.
372  *
373  * @since 12.3
374  */
375  public function offsetExists($offset)
376  {
377  return isset($this->_objects[$offset]);
378  }
379 
380  /**
381  * Gets an offset in the iterator.
382  *
383  * @param mixed $offset The object offset.
384  *
385  * @return JData The object if it exists, null otherwise.
386  *
387  * @since 12.3
388  */
389  public function offsetGet($offset)
390  {
391  return isset($this->_objects[$offset]) ? $this->_objects[$offset] : null;
392  }
393 
394  /**
395  * Sets an offset in the iterator.
396  *
397  * @param mixed $offset The object offset.
398  * @param JData $object The object object.
399  *
400  * @return void
401  *
402  * @since 12.3
403  * @throws InvalidArgumentException if an object is not an instance of JData.
404  */
405  public function offsetSet($offset, $object)
406  {
407  // Check if the object is a JData object.
408  if (!($object instanceof JData))
409  {
410  throw new InvalidArgumentException(sprintf('%s("%s", *%s*)', __METHOD__, $offset, gettype($object)));
411  }
412 
413  // Set the offset.
414  $this->_objects[$offset] = $object;
415  }
416 
417  /**
418  * Unsets an offset in the iterator.
419  *
420  * @param mixed $offset The object offset.
421  *
422  * @return void
423  *
424  * @since 12.3
425  */
426  public function offsetUnset($offset)
427  {
428  if (!$this->offsetExists($offset))
429  {
430  // Do nothing if the offset does not exist.
431  return;
432  }
433 
434  // Check for special handling of unsetting the current position.
435  if ($offset == $this->_current)
436  {
437  // Get the current position.
438  $keys = $this->keys();
439  $position = array_search($this->_current, $keys);
440 
441  // Check if there is an object before the current object.
442  if ($position > 0)
443  {
444  // Move the current position back one.
445  $this->_current = $keys[$position - 1];
446  }
447  else
448  {
449  // We are at the start of the keys AND let's assume we are in a foreach loop and `next` is going to be called.
450  $this->_current = false;
451  }
452  }
453 
454  unset($this->_objects[$offset]);
455  }
456 
457  /**
458  * Rewinds the iterator to the first object.
459  *
460  * @return void
461  *
462  * @since 12.3
463  */
464  public function rewind()
465  {
466  // Set the current position to the first object.
467  if (empty($this->_objects))
468  {
469  $this->_current = false;
470  }
471  else
472  {
473  $keys = $this->keys();
474  $this->_current = array_shift($keys);
475  }
476  }
477 
478  /**
479  * Validates the iterator.
480  *
481  * @return boolean True if valid, false otherwise.
482  *
483  * @since 12.3
484  */
485  public function valid()
486  {
487  // Check the current position.
488  if (!is_scalar($this->_current) || !isset($this->_objects[$this->_current]))
489  {
490  return false;
491  }
492 
493  return true;
494  }
495 
496  /**
497  * Initialises the list with an array of objects.
498  *
499  * @param array $input An array of objects.
500  *
501  * @return void
502  *
503  * @since 12.3
504  * @throws InvalidArgumentException if an object is not an instance of JData.
505  */
506  private function _initialise(array $input = array())
507  {
508  foreach ($input as $key => $object)
509  {
510  if (!is_null($object))
511  {
512  $this->offsetSet($key, $object);
513  }
514  }
515 
516  $this->rewind();
517  }
518 }