Proyectos de Subversion Moodle

Rev

Rev 1 | | Comparar con el anterior | 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;
1441 ariadna 48
        MetricsBuilder::appendMetricsCaptureMiddleware(
49
            $this->client->getHandlerList(),
50
            MetricsBuilder::PAGINATOR
51
        );
1 efrain 52
    }
53
 
54
    /**
55
     * Runs a paginator asynchronously and uses a callback to handle results.
56
     *
57
     * The callback should have the signature: function (Aws\Result $result).
58
     * A non-null return value from the callback will be yielded by the
59
     * promise. This means that you can return promises from the callback that
60
     * will need to be resolved before continuing iteration over the remaining
61
     * items, essentially merging in other promises to the iteration. The last
62
     * non-null value returned by the callback will be the result that fulfills
63
     * the promise to any downstream promises.
64
     *
65
     * @param callable $handleResult Callback for handling each page of results.
66
     *                               The callback accepts the result that was
67
     *                               yielded as a single argument. If the
68
     *                               callback returns a promise, the promise
69
     *                               will be merged into the coroutine.
70
     *
71
     * @return Promise\Promise
72
     */
73
    public function each(callable $handleResult)
74
    {
75
        return Promise\Coroutine::of(function () use ($handleResult) {
76
            $nextToken = null;
77
            do {
78
                $command = $this->createNextCommand($this->args, $nextToken);
79
                $result = (yield $this->client->executeAsync($command));
80
                $nextToken = $this->determineNextToken($result);
81
                $retVal = $handleResult($result);
82
                if ($retVal !== null) {
83
                    yield Promise\Create::promiseFor($retVal);
84
                }
85
            } while ($nextToken);
86
        });
87
    }
88
 
89
    /**
90
     * Returns an iterator that iterates over the values of applying a JMESPath
91
     * search to each result yielded by the iterator as a flat sequence.
92
     *
93
     * @param string $expression JMESPath expression to apply to each result.
94
     *
95
     * @return \Iterator
96
     */
97
    public function search($expression)
98
    {
99
        // Apply JMESPath expression on each result, but as a flat sequence.
100
        return flatmap($this, function (Result $result) use ($expression) {
101
            return (array) $result->search($expression);
102
        });
103
    }
104
 
105
    /**
106
     * @return Result
107
     */
108
    #[\ReturnTypeWillChange]
109
    public function current()
110
    {
111
        return $this->valid() ? $this->result : false;
112
    }
113
 
1441 ariadna 114
    /**
115
     * @return mixed
116
     */
1 efrain 117
    #[\ReturnTypeWillChange]
118
    public function key()
119
    {
120
        return $this->valid() ? $this->requestCount - 1 : null;
121
    }
122
 
1441 ariadna 123
    /**
124
     * @return void
125
     */
1 efrain 126
    #[\ReturnTypeWillChange]
127
    public function next()
128
    {
129
        $this->result = null;
130
    }
131
 
1441 ariadna 132
    /**
133
     * @return bool
134
     */
1 efrain 135
    #[\ReturnTypeWillChange]
136
    public function valid()
137
    {
138
        if ($this->result) {
139
            return true;
140
        }
141
 
142
        if ($this->nextToken || !$this->requestCount) {
143
            //Forward/backward paging can result in a case where the last page's nextforwardtoken
144
            //is the same as the one that came before it.  This can cause an infinite loop.
145
            $hasBidirectionalPaging = $this->config['output_token'] === 'nextForwardToken';
146
            if ($hasBidirectionalPaging && $this->nextToken) {
147
                $tokenKey = $this->config['input_token'];
148
                $previousToken = $this->nextToken[$tokenKey];
149
            }
150
 
151
            $this->result = $this->client->execute(
152
                $this->createNextCommand($this->args, $this->nextToken)
153
            );
154
 
155
            $this->nextToken = $this->determineNextToken($this->result);
156
 
157
            if (isset($previousToken)
158
                && $previousToken === $this->nextToken[$tokenKey]
159
            ) {
160
                return false;
161
            }
162
 
163
            $this->requestCount++;
164
            return true;
165
        }
166
 
167
        return false;
168
    }
169
 
1441 ariadna 170
    /**
171
     * @return void
172
     */
1 efrain 173
    #[\ReturnTypeWillChange]
174
    public function rewind()
175
    {
176
        $this->requestCount = 0;
177
        $this->nextToken = null;
178
        $this->result = null;
179
    }
180
 
1441 ariadna 181
    private function createNextCommand(array $args, ?array $nextToken = null)
1 efrain 182
    {
183
        return $this->client->getCommand($this->operation, array_merge($args, ($nextToken ?: [])));
184
    }
185
 
186
    private function determineNextToken(Result $result)
187
    {
188
        if (!$this->config['output_token']) {
189
            return null;
190
        }
191
 
192
        if ($this->config['more_results']
193
            && !$result->search($this->config['more_results'])
194
        ) {
195
            return null;
196
        }
197
 
198
        $nextToken = is_scalar($this->config['output_token'])
199
            ? [$this->config['input_token'] => $this->config['output_token']]
200
            : array_combine($this->config['input_token'], $this->config['output_token']);
201
 
202
        return array_filter(array_map(function ($outputToken) use ($result) {
203
            return $result->search($outputToken);
204
        }, $nextToken));
205
    }
206
}