| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190 | <?phpnamespace Aws;use GuzzleHttp\Promise;/** * Iterator that yields each page of results of a pageable operation. */class ResultPaginator implements \Iterator{    /** @var AwsClientInterface Client performing operations. */    private $client;    /** @var string Name of the operation being paginated. */    private $operation;    /** @var array Args for the operation. */    private $args;    /** @var array Configuration for the paginator. */    private $config;    /** @var Result Most recent result from the client. */    private $result;    /** @var string|array Next token to use for pagination. */    private $nextToken;    /** @var int Number of operations/requests performed. */    private $requestCount = 0;    /**     * @param AwsClientInterface $client     * @param string             $operation     * @param array              $args     * @param array              $config     */    public function __construct(        AwsClientInterface $client,        $operation,        array $args,        array $config    ) {        $this->client = $client;        $this->operation = $operation;        $this->args = $args;        $this->config = $config;    }    /**     * Runs a paginator asynchronously and uses a callback to handle results.     *     * The callback should have the signature: function (Aws\Result $result).     * A non-null return value from the callback will be yielded by the     * promise. This means that you can return promises from the callback that     * will need to be resolved before continuing iteration over the remaining     * items, essentially merging in other promises to the iteration. The last     * non-null value returned by the callback will be the result that fulfills     * the promise to any downstream promises.     *     * @param callable $handleResult Callback for handling each page of results.     *                               The callback accepts the result that was     *                               yielded as a single argument. If the     *                               callback returns a promise, the promise     *                               will be merged into the coroutine.     *     * @return Promise\Promise     */    public function each(callable $handleResult)    {        return Promise\Coroutine::of(function () use ($handleResult) {            $nextToken = null;            do {                $command = $this->createNextCommand($this->args, $nextToken);                $result = (yield $this->client->executeAsync($command));                $nextToken = $this->determineNextToken($result);                $retVal = $handleResult($result);                if ($retVal !== null) {                    yield Promise\Create::promiseFor($retVal);                }            } while ($nextToken);        });    }    /**     * Returns an iterator that iterates over the values of applying a JMESPath     * search to each result yielded by the iterator as a flat sequence.     *     * @param string $expression JMESPath expression to apply to each result.     *     * @return \Iterator     */    public function search($expression)    {        // Apply JMESPath expression on each result, but as a flat sequence.        return flatmap($this, function (Result $result) use ($expression) {            return (array) $result->search($expression);        });    }    /**     * @return Result     */    #[\ReturnTypeWillChange]    public function current()    {        return $this->valid() ? $this->result : false;    }    #[\ReturnTypeWillChange]    public function key()    {        return $this->valid() ? $this->requestCount - 1 : null;    }    #[\ReturnTypeWillChange]    public function next()    {        $this->result = null;    }    #[\ReturnTypeWillChange]    public function valid()    {        if ($this->result) {            return true;        }        if ($this->nextToken || !$this->requestCount) {            //Forward/backward paging can result in a case where the last page's nextforwardtoken            //is the same as the one that came before it.  This can cause an infinite loop.            $hasBidirectionalPaging = $this->config['output_token'] === 'nextForwardToken';            if ($hasBidirectionalPaging && $this->nextToken) {                $tokenKey = $this->config['input_token'];                $previousToken = $this->nextToken[$tokenKey];            }            $this->result = $this->client->execute(                $this->createNextCommand($this->args, $this->nextToken)            );            $this->nextToken = $this->determineNextToken($this->result);            if (isset($previousToken)                && $previousToken === $this->nextToken[$tokenKey]            ) {                return false;            }            $this->requestCount++;            return true;        }        return false;    }    #[\ReturnTypeWillChange]    public function rewind()    {        $this->requestCount = 0;        $this->nextToken = null;        $this->result = null;    }    private function createNextCommand(array $args, array $nextToken = null)    {        return $this->client->getCommand($this->operation, array_merge($args, ($nextToken ?: [])));    }    private function determineNextToken(Result $result)    {        if (!$this->config['output_token']) {            return null;        }        if ($this->config['more_results']            && !$result->search($this->config['more_results'])        ) {            return null;        }        $nextToken = is_scalar($this->config['output_token'])            ? [$this->config['input_token'] => $this->config['output_token']]            : array_combine($this->config['input_token'], $this->config['output_token']);        return array_filter(array_map(function ($outputToken) use ($result) {            return $result->search($outputToken);        }, $nextToken));    }}
 |