diff options
Diffstat (limited to 'apps/files_external/3rdparty/php-opencloud/lib/OpenCloud/Common/Collection.php')
-rw-r--r-- | apps/files_external/3rdparty/php-opencloud/lib/OpenCloud/Common/Collection.php | 320 |
1 files changed, 320 insertions, 0 deletions
diff --git a/apps/files_external/3rdparty/php-opencloud/lib/OpenCloud/Common/Collection.php b/apps/files_external/3rdparty/php-opencloud/lib/OpenCloud/Common/Collection.php new file mode 100644 index 00000000000..e1bf80376e0 --- /dev/null +++ b/apps/files_external/3rdparty/php-opencloud/lib/OpenCloud/Common/Collection.php @@ -0,0 +1,320 @@ +<?php + +namespace OpenCloud\Common; + +/** + * Provides an abstraction for working with ordered sets of objects + * + * Collection objects are used whenever there are multiples; for example, + * multiple objects in a container, or multiple servers in a service. + * + * @since 1.0 + * @author Glen Campbell <glen.campbell@rackspace.com> + * @author Jamie Hannaford <jamie.hannaford@rackspace.com> + */ +class Collection extends Base +{ + + private $service; + private $itemclass; + private $itemlist = array(); + private $pointer = 0; + private $sortkey; + private $next_page_class; + private $next_page_callback; + private $next_page_url; + + /** + * A Collection is an array of objects + * + * Some assumptions: + * * The `Collection` class assumes that there exists on its service + * a factory method with the same name of the class. For example, if + * you create a Collection of class `Foobar`, it will attempt to call + * the method `parent::Foobar()` to create instances of that class. + * * It assumes that the factory method can take an array of values, and + * it passes that to the method. + * + * @param Service $service - the service associated with the collection + * @param string $itemclass - the Class of each item in the collection + * (assumed to be the name of the factory method) + * @param array $arr - the input array + */ + public function __construct($service, $itemclass, $array) + { + $this->service = $service; + + $this->getLogger()->info( + 'Collection:service={class}, class={itemClass}, array={array}', + array( + 'class' => get_class($service), + 'itemClass' => $itemclass, + 'array' => print_r($array, true) + ) + ); + + $this->next_page_class = $itemclass; + + if (false !== ($classNamePos = strrpos($itemclass, '\\'))) { + $this->itemclass = substr($itemclass, $classNamePos + 1); + } else { + $this->itemclass = $itemclass; + } + + if (!is_array($array)) { + throw new Exceptions\CollectionError( + Lang::translate('Cannot create a Collection without an array') + ); + } + + // save the array of items + $this->setItemList($array); + } + + /** + * Set the entire data array. + * + * @param array $array + */ + public function setItemList(array $array) + { + $this->itemlist = $array; + } + + /** + * Retrieve the entire data array. + * + * @return array + */ + public function getItemList() + { + return $this->itemlist; + } + + /** + * Returns the number of items in the collection + * + * For most services, this is the total number of items. If the Collection + * is paginated, however, this only returns the count of items in the + * current page of data. + * + * @return int + */ + public function count() + { + return count($this->itemlist); + } + + /** + * Pseudonym for count() + * + * @codeCoverageIgnore + */ + public function size() + { + return $this->count(); + } + + /** + * Retrieves the service associated with the Collection + * + * @return Service + */ + public function service() + { + return $this->service; + } + + /** + * Resets the pointer to the beginning, but does NOT return the first item + * + * @api + * @return void + */ + public function reset() + { + $this->pointer = 0; + } + + /** + * Resets the collection pointer back to the first item in the page + * and returns it + * + * This is useful if you're only interested in the first item in the page. + * + * @api + * @return Base the first item in the set + */ + public function first() + { + $this->reset(); + return $this->next(); + } + + /** + * Returns the next item in the page + * + * @api + * @return Base the next item or FALSE if at the end of the page + */ + public function next() + { + if ($this->pointer >= $this->count()) { + return false; + } + + $service = $this->service(); + + if (method_exists($service, $this->itemclass)) { + return $service->{$this->itemclass}($this->itemlist[$this->pointer++]); + } elseif (method_exists($service, 'resource')) { + return $service->resource($this->itemclass, $this->itemlist[$this->pointer++]); + } + // @codeCoverageIgnoreStart + return false; + // @codeCoverageIgnoreEnd + } + + /** + * sorts the collection on a specified key + * + * Note: only top-level keys can be used as the sort key. Note that this + * only sorts the data in the current page of the Collection (for + * multi-page data). + * + * @api + * @param string $keyname the name of the field to use as the sort key + * @return void + */ + public function sort($keyname = 'id') + { + $this->sortkey = $keyname; + usort($this->itemlist, array($this, 'sortCompare')); + } + + /** + * selects only specified items from the Collection + * + * This provides a simple form of filtering on Collections. For each item + * in the collection, it calls the callback function, passing it the item. + * If the callback returns `TRUE`, then the item is retained; if it returns + * `FALSE`, then the item is deleted from the collection. + * + * Note that this should not supersede server-side filtering; the + * `Collection::Select()` method requires that *all* of the data for the + * Collection be retrieved from the server before the filtering is + * performed; this can be very inefficient, especially for large data + * sets. This method is mostly useful on smaller-sized sets. + * + * Example: + * <code> + * $services = $connection->ServiceList(); + * $services->Select(function($item){ return $item->region=='ORD';}); + * // now the $services Collection only has items from the ORD region + * </code> + * + * `Select()` is *destructive*; that is, it actually removes entries from + * the collection. For example, if you use `Select()` to find items with + * the ID > 10, then use it again to find items that are <= 10, it will + * return an empty list. + * + * @api + * @param callable $testfunc a callback function that is passed each item + * in turn. Note that `Select()` performs an explicit test for + * `FALSE`, so functions like `strpos()` need to be cast into a + * boolean value (and not just return the integer). + * @returns void + * @throws DomainError if callback doesn't return a boolean value + */ + public function select($testfunc) + { + foreach ($this->getItemList() as $index => $item) { + $test = call_user_func($testfunc, $item); + if (!is_bool($test)) { + throw new Exceptions\DomainError( + Lang::translate('Callback function for Collection::Select() did not return boolean') + ); + } + if ($test === false) { + unset($this->itemlist[$index]); + } + } + } + + /** + * returns the Collection object for the next page of results, or + * FALSE if there are no more pages + * + * Generally, the structure for a multi-page collection will look like + * this: + * + * $coll = $obj->Collection(); + * do { + * while($item = $coll->Next()) { + * // do something with the item + * } + * } while ($coll = $coll->NextPage()); + * + * @api + * @return Collection if there are more pages of results, otherwise FALSE + */ + public function nextPage() + { + if (isset($this->next_page_url)) { + return call_user_func( + $this->next_page_callback, + $this->next_page_class, + $this->next_page_url + ); + } + // @codeCoverageIgnoreStart + return false; + // @codeCoverageIgnoreEnd + } + + /** + * for paginated collection, sets the callback function and URL for + * the next page + * + * The callback function should have the signature: + * + * function Whatever($class, $url, $parent) + * + * and the `$url` should be the URL of the next page of results + * + * @param callable $callback the name of the function (or array of + * object, function name) + * @param string $url the URL of the next page of results + * @return void + */ + public function setNextPageCallback($callback, $url) + { + $this->next_page_callback = $callback; + $this->next_page_url = $url; + } + + /** + * Compares two values of sort keys + */ + private function sortCompare($a, $b) + { + $key = $this->sortkey; + + // handle strings with strcmp() + if (is_string($a->$key)) { + return strcmp($a->$key, $b->$key); + } + + // handle others with logical comparisons + if ($a->$key == $b->$key) { + return 0; + } + + if ($a->$key < $b->$key) { + return -1; + } else { + return 1; + } + } + +} |