/src/Guzzle/Service/Resource/ResourceIterator.php
PHP | 321 lines | 134 code | 42 blank | 145 comment | 15 complexity | e4618c1b4d841f52d2c5f4f27e727c04 MD5 | raw file
- <?php
- namespace Guzzle\Service\Resource;
- use Guzzle\Common\AbstractHasDispatcher;
- use Guzzle\Service\Command\CommandInterface;
- /**
- * {@inheritdoc}
- */
- abstract class ResourceIterator extends AbstractHasDispatcher implements ResourceIteratorInterface
- {
- /**
- * @var CommandInterface Command used to send requests
- */
- protected $command;
- /**
- * @var CommandInterface First sent command
- */
- protected $originalCommand;
- /**
- * @var array Currently loaded resources
- */
- protected $resources;
- /**
- * @var int Total number of resources that have been retrieved
- */
- protected $retrievedCount = 0;
- /**
- * @var int Total number of resources that have been iterated
- */
- protected $iteratedCount = 0;
- /**
- * @var string NextToken/Marker for a subsequent request
- */
- protected $nextToken = false;
- /**
- * @var int Maximum number of resources to fetch per request
- */
- protected $pageSize;
- /**
- * @var int Maximum number of resources to retrieve in total
- */
- protected $limit;
- /**
- * @var int Number of requests sent
- */
- protected $requestCount = 0;
- /**
- * @var array Initial data passed to the constructor
- */
- protected $data = array();
- /**
- * @var bool Whether or not the current value is known to be invalid. This
- * is set for example when a senRequest() method returns 0 resources.
- */
- protected $invalid;
- /**
- * {@inheritdoc}
- */
- public static function getAllEvents()
- {
- return array(
- // About to issue another command to get more results
- 'resource_iterator.before_send',
- // Issued another command to get more results
- 'resource_iterator.after_send'
- );
- }
- /**
- * This should only be invoked by a {@see ClientInterface} object.
- *
- * @param CommandInterface $command Initial command used for iteration
- *
- * @param array $data Associative array of additional parameters.
- * You may specify any number of custom options for an iterator. Among
- * these options, you may also specify the following values:
- *
- * limit: Attempt to limit the maximum number of resources to this amount
- * page_size: Attempt to retrieve this number of resources per request
- */
- public function __construct(CommandInterface $command, array $data = array())
- {
- // Clone the command to keep track of the originating command for rewind
- $this->originalCommand = $command;
- // Parse options from the array of options
- $this->data = $data;
- $this->limit = array_key_exists('limit', $data) ? $data['limit'] : 0;
- $this->pageSize = array_key_exists('page_size', $data) ? $data['page_size'] : false;
- }
- /**
- * Get all of the resources as an array (be careful as this could issue a
- * large number of requests if no limit is specified)
- *
- * @return array
- */
- public function toArray()
- {
- return iterator_to_array($this, false);
- }
- /**
- * {@inheritdoc}
- */
- public function setLimit($limit)
- {
- $this->limit = $limit;
- $this->resetState();
- return $this;
- }
- /**
- * {@inheritdoc}
- */
- public function setPageSize($pageSize)
- {
- $this->pageSize = $pageSize;
- $this->resetState();
- return $this;
- }
- /**
- * Get an option from the iterator
- *
- * @param string $key Key of the option to retrieve
- *
- * @return mixed|null Returns NULL if not set or the value if set
- */
- public function get($key)
- {
- return array_key_exists($key, $this->data) ? $this->data[$key] : null;
- }
- /**
- * Set an option on the iterator
- *
- * @param string $key Key of the option to set
- * @param mixed $value Value to set for the option
- *
- * @return ResourceIterator
- */
- public function set($key, $value)
- {
- $this->data[$key] = $value;
- return $this;
- }
- /**
- * Return the current element.
- *
- * @return mixed Returns the current element.
- */
- public function current()
- {
- return $this->resources ? current($this->resources) : false;
- }
- /**
- * Return the key of the current element.
- *
- * @return mixed
- */
- public function key()
- {
- return max(0, $this->iteratedCount - 1);
- }
- /**
- * Return the total number of items that have been retrieved thus far.
- *
- * @return int
- */
- public function count()
- {
- return $this->retrievedCount;
- }
- /**
- * Get the total number of requests sent
- *
- * @return int
- */
- public function getRequestCount()
- {
- return $this->requestCount;
- }
- /**
- * Rewind the Iterator to the first element and send the original command
- */
- public function rewind()
- {
- // Use the original command
- $this->command = clone $this->originalCommand;
- $this->resetState();
- $this->next();
- }
- /**
- * Check if there is a current element after calls to rewind() or next().
- *
- * @return bool Returns TRUE if the current element is valid or FALSE
- */
- public function valid()
- {
- return !$this->invalid && (!$this->resources || $this->current() || $this->nextToken)
- && (!$this->limit || $this->iteratedCount < $this->limit + 1);
- }
- /**
- * Move forward to next element and may trigger subsequent requests
- */
- public function next()
- {
- $this->iteratedCount++;
- // Check if a new set of resources needs to be retrieved
- $sendRequest = false;
- if (!$this->resources) {
- $sendRequest = true;
- } else {
- // iterate over the internal array
- $current = next($this->resources);
- $sendRequest = $current === false && $this->nextToken && (!$this->limit || $this->iteratedCount < $this->limit + 1);
- }
- if ($sendRequest) {
- $this->dispatch('resource_iterator.before_send', array(
- 'iterator' => $this,
- 'resources' => $this->resources
- ));
- // Get a new command object from the original command
- $this->command = clone $this->originalCommand;
- // Send a request and retrieve the newly loaded resources
- $this->resources = $this->sendRequest();
- $this->requestCount++;
- // If no resources were found, then the last request was not needed
- // and iteration must stop
- if (empty($this->resources)) {
- $this->invalid = true;
- } else {
- // Add to the number of retrieved resources
- $this->retrievedCount += count($this->resources);
- // Ensure that we rewind to the beginning of the array
- reset($this->resources);
- }
- $this->dispatch('resource_iterator.after_send', array(
- 'iterator' => $this,
- 'resources' => $this->resources
- ));
- }
- }
- /**
- * Retrieve the NextToken that can be used in other iterators.
- *
- * @return string Returns a NextToken
- */
- public function getNextToken()
- {
- return $this->nextToken;
- }
- /**
- * Returns the value that should be specified for the page size for
- * a request that will maintain any hard limits, but still honor the
- * specified pageSize if the number of items retrieved + pageSize < hard
- * limit
- *
- * @return int Returns the page size of the next request.
- */
- protected function calculatePageSize()
- {
- if ($this->limit && $this->iteratedCount + $this->pageSize > $this->limit) {
- return 1 + ($this->limit - $this->iteratedCount);
- }
- return (int) $this->pageSize;
- }
- /**
- * Reset the internal state of the iterator without triggering a rewind()
- */
- protected function resetState()
- {
- $this->iteratedCount = 0;
- $this->retrievedCount = 0;
- $this->nextToken = false;
- $this->resources = null;
- $this->invalid = false;
- }
- /**
- * Send a request to retrieve the next page of results.
- * Hook for subclasses to implement.
- *
- * @return array Returns the newly loaded resources
- */
- abstract protected function sendRequest();
- }