JOOMLA中国
  • Joomla中国首页
  • 社区
  • 教程
  • 应用市场
  • B计划
Joomla! Framework TM
  • Namespace
  • Class
  • Tree
  • Deprecated

Namespaces

  • Composer
    • Autoload
  • Joomla
    • Application
      • Cli
        • Output
          • Processor
      • Web
    • Data
    • DI
      • Exception
    • Event
    • Filter
    • Input
    • Ldap
    • Registry
      • Format
    • Session
      • Storage
    • String
    • Uri
    • Utilities
  • None
  • PasswordCompat
    • binary
  • PHP
  • Psr
    • Log
  • Symfony
    • Component
      • Yaml
        • Exception
    • Polyfill
      • Util

Classes

  • DataObject
  • DataSet

Interfaces

  • DumpableInterface
  1 <?php
  2 /**
  3  * Part of the Joomla Framework Data Package
  4  *
  5  * @copyright  Copyright (C) 2005 - 2016 Open Source Matters, Inc. All rights reserved.
  6  * @license    GNU General Public License version 2 or later; see LICENSE
  7  */
  8 
  9 namespace Joomla\Data;
 10 
 11 /**
 12  * DataSet is a collection class that allows the developer to operate on a set of DataObject objects as if they were in a
 13  * typical PHP array.
 14  *
 15  * @since  1.0
 16  */
 17 class DataSet implements DumpableInterface, \ArrayAccess, \Countable, \Iterator
 18 {
 19     /**
 20      * The current position of the iterator.
 21      *
 22      * @var    integer
 23      * @since  1.0
 24      */
 25     private $current = false;
 26 
 27     /**
 28      * The iterator objects.
 29      *
 30      * @var    DataObject[]
 31      * @since  1.0
 32      */
 33     private $objects = array();
 34 
 35     /**
 36      * The class constructor.
 37      *
 38      * @param   DataObject[]  $objects  An array of DataObject objects to bind to the data set.
 39      *
 40      * @since   1.0
 41      * @throws  \InvalidArgumentException if an object is not an instance of Data\Object.
 42      */
 43     public function __construct(array $objects = array())
 44     {
 45         // Set the objects.
 46         $this->_initialise($objects);
 47     }
 48 
 49     /**
 50      * The magic call method is used to call object methods using the iterator.
 51      *
 52      * Example: $array = $objectList->foo('bar');
 53      *
 54      * The object list will iterate over its objects and see if each object has a callable 'foo' method.
 55      * If so, it will pass the argument list and assemble any return values. If an object does not have
 56      * a callable method no return value is recorded.
 57      * The keys of the objects and the result array are maintained.
 58      *
 59      * @param   string  $method     The name of the method called.
 60      * @param   array   $arguments  The arguments of the method called.
 61      *
 62      * @return  array   An array of values returned by the methods called on the objects in the data set.
 63      *
 64      * @since   1.0
 65      */
 66     public function __call($method, $arguments = array())
 67     {
 68         $return = array();
 69 
 70         // Iterate through the objects.
 71         foreach ($this->objects as $key => $object)
 72         {
 73             // Create the object callback.
 74             $callback = array($object, $method);
 75 
 76             // Check if the callback is callable.
 77             if (is_callable($callback))
 78             {
 79                 // Call the method for the object.
 80                 $return[$key] = call_user_func_array($callback, $arguments);
 81             }
 82         }
 83 
 84         return $return;
 85     }
 86 
 87     /**
 88      * The magic get method is used to get a list of properties from the objects in the data set.
 89      *
 90      * Example: $array = $dataSet->foo;
 91      *
 92      * This will return a column of the values of the 'foo' property in all the objects
 93      * (or values determined by custom property setters in the individual Data\Object's).
 94      * The result array will contain an entry for each object in the list (compared to __call which may not).
 95      * The keys of the objects and the result array are maintained.
 96      *
 97      * @param   string  $property  The name of the data property.
 98      *
 99      * @return  array  An associative array of the values.
100      *
101      * @since   1.0
102      */
103     public function __get($property)
104     {
105         $return = array();
106 
107         // Iterate through the objects.
108         foreach ($this->objects as $key => $object)
109         {
110             // Get the property.
111             $return[$key] = $object->$property;
112         }
113 
114         return $return;
115     }
116 
117     /**
118      * The magic isset method is used to check the state of an object property using the iterator.
119      *
120      * Example: $array = isset($objectList->foo);
121      *
122      * @param   string  $property  The name of the property.
123      *
124      * @return  boolean  True if the property is set in any of the objects in the data set.
125      *
126      * @since   1.0
127      */
128     public function __isset($property)
129     {
130         $return = array();
131 
132         // Iterate through the objects.
133         foreach ($this->objects as $object)
134         {
135             // Check the property.
136             $return[] = isset($object->$property);
137         }
138 
139         return in_array(true, $return, true) ? true : false;
140     }
141 
142     /**
143      * The magic set method is used to set an object property using the iterator.
144      *
145      * Example: $objectList->foo = 'bar';
146      *
147      * This will set the 'foo' property to 'bar' in all of the objects
148      * (or a value determined by custom property setters in the Data\Object).
149      *
150      * @param   string  $property  The name of the property.
151      * @param   mixed   $value     The value to give the data property.
152      *
153      * @return  void
154      *
155      * @since   1.0
156      */
157     public function __set($property, $value)
158     {
159         // Iterate through the objects.
160         foreach ($this->objects as $object)
161         {
162             // Set the property.
163             $object->$property = $value;
164         }
165     }
166 
167     /**
168      * The magic unset method is used to unset an object property using the iterator.
169      *
170      * Example: unset($objectList->foo);
171      *
172      * This will unset all of the 'foo' properties in the list of Data\Object's.
173      *
174      * @param   string  $property  The name of the property.
175      *
176      * @return  void
177      *
178      * @since   1.0
179      */
180     public function __unset($property)
181     {
182         // Iterate through the objects.
183         foreach ($this->objects as $object)
184         {
185             unset($object->$property);
186         }
187     }
188 
189     /**
190      * Gets an array of keys, existing in objects
191      *
192      * @param   string  $type  Selection type 'all' or 'common'
193      *
194      * @return  array   Array of keys
195      *
196      * @since   1.2.0
197      * @throws  \InvalidArgumentException
198      */
199     public function getObjectsKeys($type = 'all')
200     {
201         $keys = null;
202 
203         if ($type == 'all')
204         {
205             $function = 'array_merge';
206         }
207         elseif ($type == 'common')
208         {
209             $function = 'array_intersect_key';
210         }
211         else
212         {
213             throw new \InvalidArgumentException("Unknown selection type: $type");
214         }
215 
216         foreach ($this->objects as $object)
217         {
218             if (version_compare(PHP_VERSION, '5.4.0', '<'))
219             {
220                 $object_vars = json_decode(json_encode($object->jsonSerialize()), true);
221             }
222             else
223             {
224                 $object_vars = json_decode(json_encode($object), true);
225             }
226 
227             $keys = (is_null($keys)) ? $object_vars : $function($keys, $object_vars);
228         }
229 
230         return array_keys($keys);
231     }
232 
233     /**
234      * Gets all objects as an array
235      *
236      * @param   boolean  $associative  Option to set return mode: associative or numeric array.
237      * @param   string   $k            Unlimited optional property names to extract from objects.
238      *
239      * @return  array    Returns an array according to defined options.
240      *
241      * @since   1.2.0
242      */
243     public function toArray($associative = true, $k = null)
244     {
245         $keys        = func_get_args();
246         $associative = array_shift($keys);
247 
248         if (empty($keys))
249         {
250             $keys = $this->getObjectsKeys();
251         }
252 
253         $return = array();
254 
255         $i = 0;
256 
257         foreach ($this->objects as $key => $object)
258         {
259             $array_item = array();
260 
261             $key = ($associative) ? $key : $i++;
262 
263             $j = 0;
264 
265             foreach ($keys as $property)
266             {
267                 $property_key              = ($associative) ? $property : $j++;
268                 $array_item[$property_key] = (isset($object->$property)) ? $object->$property : null;
269             }
270 
271             $return[$key] = $array_item;
272         }
273 
274         return $return;
275     }
276 
277     /**
278      * Gets the number of data objects in the set.
279      *
280      * @return  integer  The number of objects.
281      *
282      * @since   1.0
283      */
284     public function count()
285     {
286         return count($this->objects);
287     }
288 
289     /**
290      * Clears the objects in the data set.
291      *
292      * @return  DataSet  Returns itself to allow chaining.
293      *
294      * @since   1.0
295      */
296     public function clear()
297     {
298         $this->objects = array();
299         $this->rewind();
300 
301         return $this;
302     }
303 
304     /**
305      * Get the current data object in the set.
306      *
307      * @return  DataObject  The current object, or false if the array is empty or the pointer is beyond the end of the elements.
308      *
309      * @since   1.0
310      */
311     public function current()
312     {
313         return is_scalar($this->current) ? $this->objects[$this->current] : false;
314     }
315 
316     /**
317      * Dumps the data object in the set, recursively if appropriate.
318      *
319      * @param   integer            $depth   The maximum depth of recursion (default = 3).
320      *                                      For example, a depth of 0 will return a stdClass with all the properties in native
321      *                                      form. A depth of 1 will recurse into the first level of properties only.
322      * @param   \SplObjectStorage  $dumped  An array of already serialized objects that is used to avoid infinite loops.
323      *
324      * @return  array  An associative array of the data objects in the set, dumped as a simple PHP stdClass object.
325      *
326      * @see     DataObject::dump()
327      * @since   1.0
328      */
329     public function dump($depth = 3, \SplObjectStorage $dumped = null)
330     {
331         // Check if we should initialise the recursion tracker.
332         if ($dumped === null)
333         {
334             $dumped = new \SplObjectStorage;
335         }
336 
337         // Add this object to the dumped stack.
338         $dumped->attach($this);
339 
340         $objects = array();
341 
342         // Make sure that we have not reached our maximum depth.
343         if ($depth > 0)
344         {
345             // Handle JSON serialization recursively.
346             foreach ($this->objects as $key => $object)
347             {
348                 $objects[$key] = $object->dump($depth, $dumped);
349             }
350         }
351 
352         return $objects;
353     }
354 
355     /**
356      * Gets the data set in a form that can be serialised to JSON format.
357      *
358      * Note that this method will not return an associative array, otherwise it would be encoded into an object.
359      * JSON decoders do not consistently maintain the order of associative keys, whereas they do maintain the order of arrays.
360      *
361      * @param   mixed  $serialized  An array of objects that have already been serialized that is used to infinite loops
362      *                              (null on first call).
363      *
364      * @return  array  An array that can be serialised by json_encode().
365      *
366      * @since   1.0
367      */
368     public function jsonSerialize($serialized = null)
369     {
370         // Check if we should initialise the recursion tracker.
371         if ($serialized === null)
372         {
373             $serialized = array();
374         }
375 
376         // Add this object to the serialized stack.
377         $serialized[] = spl_object_hash($this);
378         $return = array();
379 
380         // Iterate through the objects.
381         foreach ($this->objects as $object)
382         {
383             // Call the method for the object.
384             $return[] = $object->jsonSerialize($serialized);
385         }
386 
387         return $return;
388     }
389 
390     /**
391      * Gets the key of the current object in the iterator.
392      *
393      * @return  scalar  The object key on success; null on failure.
394      *
395      * @since   1.0
396      */
397     public function key()
398     {
399         return $this->current;
400     }
401 
402     /**
403      * Gets the array of keys for all the objects in the iterator (emulates array_keys).
404      *
405      * @return  array  The array of keys
406      *
407      * @since   1.0
408      */
409     public function keys()
410     {
411         return array_keys($this->objects);
412     }
413 
414     /**
415      * Applies a function to every object in the set (emulates array_walk).
416      * 
417      * @param   callable  $funcname  Callback function.  
418      * 
419      * @return  boolean
420      * 
421      * @since   1.2.0
422      * @throws  \InvalidArgumentException
423      */
424     public function walk($funcname)
425     {
426         if (!is_callable($funcname))
427         {
428             $message = __METHOD__ . '() expects parameter 1 to be a valid callback';
429 
430             if (is_string($funcname))
431             {
432                 $message .= sprintf(', function \'%s\' not found or invalid function name', $funcname);
433             }
434 
435             throw new \InvalidArgumentException($message);
436         }
437 
438         foreach ($this->objects as $key => $object)
439         {
440             $funcname($object, $key);
441         }
442 
443         return true;
444     }
445 
446     /**
447      * Advances the iterator to the next object in the iterator.
448      *
449      * @return  void
450      *
451      * @since   1.0
452      */
453     public function next()
454     {
455         // Get the object offsets.
456         $keys = $this->keys();
457 
458         // Check if _current has been set to false but offsetUnset.
459         if ($this->current === false && isset($keys[0]))
460         {
461             // This is a special case where offsetUnset was used in a foreach loop and the first element was unset.
462             $this->current = $keys[0];
463         }
464         else
465         {
466             // Get the current key.
467             $position = array_search($this->current, $keys);
468 
469             // Check if there is an object after the current object.
470             if ($position !== false && isset($keys[$position + 1]))
471             {
472                 // Get the next id.
473                 $this->current = $keys[$position + 1];
474             }
475             else
476             {
477                 // That was the last object or the internal properties have become corrupted.
478                 $this->current = null;
479             }
480         }
481     }
482 
483     /**
484      * Checks whether an offset exists in the iterator.
485      *
486      * @param   mixed  $offset  The object offset.
487      *
488      * @return  boolean  True if the object exists, false otherwise.
489      *
490      * @since   1.0
491      */
492     public function offsetExists($offset)
493     {
494         return isset($this->objects[$offset]);
495     }
496 
497     /**
498      * Gets an offset in the iterator.
499      *
500      * @param   mixed  $offset  The object offset.
501      *
502      * @return  DataObject  The object if it exists, null otherwise.
503      *
504      * @since   1.0
505      */
506     public function offsetGet($offset)
507     {
508         return isset($this->objects[$offset]) ? $this->objects[$offset] : null;
509     }
510 
511     /**
512      * Sets an offset in the iterator.
513      *
514      * @param   mixed       $offset  The object offset.
515      * @param   DataObject  $object  The object object.
516      *
517      * @return  void
518      *
519      * @since   1.0
520      * @throws  \InvalidArgumentException if an object is not an instance of Data\Object.
521      */
522     public function offsetSet($offset, $object)
523     {
524         if (!($object instanceof DataObject))
525         {
526             throw new \InvalidArgumentException(sprintf('%s("%s", *%s*)', __METHOD__, $offset, gettype($object)));
527         }
528 
529         // Set the offset.
530         $this->objects[$offset] = $object;
531     }
532 
533     /**
534      * Unsets an offset in the iterator.
535      *
536      * @param   mixed  $offset  The object offset.
537      *
538      * @return  void
539      *
540      * @since   1.0
541      */
542     public function offsetUnset($offset)
543     {
544         if (!$this->offsetExists($offset))
545         {
546             // Do nothing if the offset does not exist.
547             return;
548         }
549 
550         // Check for special handling of unsetting the current position.
551         if ($offset == $this->current)
552         {
553             // Get the current position.
554             $keys = $this->keys();
555             $position = array_search($this->current, $keys);
556 
557             // Check if there is an object before the current object.
558             if ($position > 0)
559             {
560                 // Move the current position back one.
561                 $this->current = $keys[$position - 1];
562             }
563             else
564             {
565                 // 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.
566                 $this->current = false;
567             }
568         }
569 
570         unset($this->objects[$offset]);
571     }
572 
573     /**
574      * Rewinds the iterator to the first object.
575      *
576      * @return  void
577      *
578      * @since   1.0
579      */
580     public function rewind()
581     {
582         // Set the current position to the first object.
583         if (empty($this->objects))
584         {
585             $this->current = false;
586         }
587         else
588         {
589             $keys = $this->keys();
590             $this->current = array_shift($keys);
591         }
592     }
593 
594     /**
595      * Validates the iterator.
596      *
597      * @return  boolean  True if valid, false otherwise.
598      *
599      * @since   1.0
600      */
601     public function valid()
602     {
603         // Check the current position.
604         if (!is_scalar($this->current) || !isset($this->objects[$this->current]))
605         {
606             return false;
607         }
608 
609         return true;
610     }
611 
612     /**
613      * Initialises the list with an array of objects.
614      *
615      * @param   array  $input  An array of objects.
616      *
617      * @return  void
618      *
619      * @since   1.0
620      * @throws  \InvalidArgumentException if an object is not an instance of Data\DataObject.
621      */
622     private function _initialise(array $input = array())
623     {
624         foreach ($input as $key => $object)
625         {
626             if (!is_null($object))
627             {
628                 $this->offsetSet($key, $object);
629             }
630         }
631 
632         $this->rewind();
633     }
634 }
635 
Joomla! Framework TM API documentation generated by ApiGen 2.8.0
Joomla!® and Joomla! Framework™ are trademarks of Open Source Matters, Inc. in the United States and other countries.