Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
namespace Aws;
3
 
4
use GuzzleHttp\Promise;
5
 
6
/**
7
 * Iterator that yields each page of results of a pageable operation.
8
 */
9
class ResultPaginator implements \Iterator
10
{
11
    /** @var AwsClientInterface Client performing operations. */
12
    private $client;
13
 
14
    /** @var string Name of the operation being paginated. */
15
    private $operation;
16
 
17
    /** @var array Args for the operation. */
18
    private $args;
19
 
20
    /** @var array Configuration for the paginator. */
21
    private $config;
22
 
23
    /** @var Result Most recent result from the client. */
24
    private $result;
25
 
26
    /** @var string|array Next token to use for pagination. */
27
    private $nextToken;
28
 
29
    /** @var int Number of operations/requests performed. */
30
    private $requestCount = 0;
31
 
32
    /**
33
     * @param AwsClientInterface $client
34
     * @param string             $operation
35
     * @param array              $args
36
     * @param array              $config
37
     */
38
    public function __construct(
39
        AwsClientInterface $client,
40
        $operation,
41
        array $args,
42
        array $config
43
    ) {
44
        $this->client = $client;
45
        $this->operation = $operation;
46
        $this->args = $args;
47
        $this->config = $config;
48
    }
49
 
50
    /**
51
     * Runs a paginator asynchronously and uses a callback to handle results.
52
     *
53
     * The callback should have the signature: function (Aws\Result $result).
54
     * A non-null return value from the callback will be yielded by the
55
     * promise. This means that you can return promises from the callback that
56
     * will need to be resolved before continuing iteration over the remaining
57
     * items, essentially merging in other promises to the iteration. The last
58
     * non-null value returned by the callback will be the result that fulfills
59
     * the promise to any downstream promises.
60
     *
61
     * @param callable $handleResult Callback for handling each page of results.
62
     *                               The callback accepts the result that was
63
     *                               yielded as a single argument. If the
64
     *                               callback returns a promise, the promise
65
     *                               will be merged into the coroutine.
66
     *
67
     * @return Promise\Promise
68
     */
69
    public function each(callable $handleResult)
70
    {
71
        return Promise\Coroutine::of(function () use ($handleResult) {
72
            $nextToken = null;
73
            do {
74
                $command = $this->createNextCommand($this->args, $nextToken);
75
                $result = (yield $this->client->executeAsync($command));
76
                $nextToken = $this->determineNextToken($result);
77
                $retVal = $handleResult($result);
78
                if ($retVal !== null) {
79
                    yield Promise\Create::promiseFor($retVal);
80
                }
81
            } while ($nextToken);
82
        });
83
    }
84
 
85
    /**
86
     * Returns an iterator that iterates over the values of applying a JMESPath
87
     * search to each result yielded by the iterator as a flat sequence.
88
     *
89
     * @param string $expression JMESPath expression to apply to each result.
90
     *
91
     * @return \Iterator
92
     */
93
    public function search($expression)
94
    {
95
        // Apply JMESPath expression on each result, but as a flat sequence.
96
        return flatmap($this, function (Result $result) use ($expression) {
97
            return (array) $result->search($expression);
98
        });
99
    }
100
 
101
    /**
102
     * @return Result
103
     */
104
    #[\ReturnTypeWillChange]
105
    public function current()
106
    {
107
        return $this->valid() ? $this->result : false;
108
    }
109
 
110
    #[\ReturnTypeWillChange]
111
    public function key()
112
    {
113
        return $this->valid() ? $this->requestCount - 1 : null;
114
    }
115
 
116
    #[\ReturnTypeWillChange]
117
    public function next()
118
    {
119
        $this->result = null;
120
    }
121
 
122
    #[\ReturnTypeWillChange]
123
    public function valid()
124
    {
125
        if ($this->result) {
126
            return true;
127
        }
128
 
129
        if ($this->nextToken || !$this->requestCount) {
130
            //Forward/backward paging can result in a case where the last page's nextforwardtoken
131
            //is the same as the one that came before it.  This can cause an infinite loop.
132
            $hasBidirectionalPaging = $this->config['output_token'] === 'nextForwardToken';
133
            if ($hasBidirectionalPaging && $this->nextToken) {
134
                $tokenKey = $this->config['input_token'];
135
                $previousToken = $this->nextToken[$tokenKey];
136
            }
137
 
138
            $this->result = $this->client->execute(
139
                $this->createNextCommand($this->args, $this->nextToken)
140
            );
141
 
142
            $this->nextToken = $this->determineNextToken($this->result);
143
 
144
            if (isset($previousToken)
145
                && $previousToken === $this->nextToken[$tokenKey]
146
            ) {
147
                return false;
148
            }
149
 
150
            $this->requestCount++;
151
            return true;
152
        }
153
 
154
        return false;
155
    }
156
 
157
    #[\ReturnTypeWillChange]
158
    public function rewind()
159
    {
160
        $this->requestCount = 0;
161
        $this->nextToken = null;
162
        $this->result = null;
163
    }
164
 
165
    private function createNextCommand(array $args, array $nextToken = null)
166
    {
167
        return $this->client->getCommand($this->operation, array_merge($args, ($nextToken ?: [])));
168
    }
169
 
170
    private function determineNextToken(Result $result)
171
    {
172
        if (!$this->config['output_token']) {
173
            return null;
174
        }
175
 
176
        if ($this->config['more_results']
177
            && !$result->search($this->config['more_results'])
178
        ) {
179
            return null;
180
        }
181
 
182
        $nextToken = is_scalar($this->config['output_token'])
183
            ? [$this->config['input_token'] => $this->config['output_token']]
184
            : array_combine($this->config['input_token'], $this->config['output_token']);
185
 
186
        return array_filter(array_map(function ($outputToken) use ($result) {
187
            return $result->search($outputToken);
188
        }, $nextToken));
189
    }
190
}