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
/**
5
 * Builds a single handler function from zero or more middleware functions and
6
 * a handler. The handler function is then used to send command objects and
7
 * return a promise that is resolved with an AWS result object.
8
 *
9
 * The "front" of the list is invoked before the "end" of the list. You can add
10
 * middleware to the front of the list using one of the "prepend" method, and
11
 * the end of the list using one of the "append" method. The last function
12
 * invoked in a handler list is the handler (a function that does not accept a
13
 * next handler but rather is responsible for returning a promise that is
14
 * fulfilled with an Aws\ResultInterface object).
15
 *
16
 * Handlers are ordered using a "step" that describes the step at which the
17
 * SDK is when sending a command. The available steps are:
18
 *
19
 * - init: The command is being initialized, allowing you to do things like add
20
 *   default options.
21
 * - validate: The command is being validated before it is serialized
22
 * - build: The command is being serialized into an HTTP request. A middleware
23
 *   in this step MUST serialize an HTTP request and populate the "@request"
24
 *   parameter of a command with the request such that it is available to
25
 *   subsequent middleware.
26
 * - sign: The request is being signed and prepared to be sent over the wire.
27
 *
28
 * Middleware can be registered with a name to allow you to easily add a
29
 * middleware before or after another middleware by name. This also allows you
30
 * to remove a middleware by name (in addition to removing by instance).
31
 */
32
class HandlerList implements \Countable
33
{
34
    const INIT = 'init';
35
    const VALIDATE = 'validate';
36
    const BUILD = 'build';
37
    const SIGN = 'sign';
38
    const ATTEMPT = 'attempt';
39
 
40
    /** @var callable */
41
    private $handler;
42
 
43
    /** @var array */
44
    private $named = [];
45
 
46
    /** @var array */
47
    private $sorted;
48
 
49
    /** @var callable|null */
50
    private $interposeFn;
51
 
52
    /** @var array Steps (in reverse order) */
53
    private $steps = [
54
        self::ATTEMPT  => [],
55
        self::SIGN     => [],
56
        self::BUILD    => [],
57
        self::VALIDATE => [],
58
        self::INIT     => [],
59
    ];
60
 
61
    /**
62
     * @param callable $handler HTTP handler.
63
     */
64
    public function __construct(callable $handler = null)
65
    {
66
        $this->handler = $handler;
67
    }
68
 
69
    /**
70
     * Dumps a string representation of the list.
71
     *
72
     * @return string
73
     */
74
    public function __toString()
75
    {
76
        $str = '';
77
        $i = 0;
78
 
79
        foreach (array_reverse($this->steps) as $k => $step) {
80
            foreach (array_reverse($step) as $j => $tuple) {
81
                $str .= "{$i}) Step: {$k}, ";
82
                if ($tuple[1]) {
83
                    $str .= "Name: {$tuple[1]}, ";
84
                }
85
                $str .= "Function: " . $this->debugCallable($tuple[0]) . "\n";
86
                $i++;
87
            }
88
        }
89
 
90
        if ($this->handler) {
91
            $str .= "{$i}) Handler: " . $this->debugCallable($this->handler) . "\n";
92
        }
93
 
94
        return $str;
95
    }
96
 
97
    /**
98
     * Set the HTTP handler that actually returns a response.
99
     *
100
     * @param callable $handler Function that accepts a request and array of
101
     *                          options and returns a Promise.
102
     */
103
    public function setHandler(callable $handler)
104
    {
105
        $this->handler = $handler;
106
    }
107
 
108
    /**
109
     * Returns true if the builder has a handler.
110
     *
111
     * @return bool
112
     */
113
    public function hasHandler()
114
    {
115
        return (bool) $this->handler;
116
    }
117
 
118
    /**
119
     * Append a middleware to the init step.
120
     *
121
     * @param callable $middleware Middleware function to add.
122
     * @param string   $name       Name of the middleware.
123
     */
124
    public function appendInit(callable $middleware, $name = null)
125
    {
126
        $this->add(self::INIT, $name, $middleware);
127
    }
128
 
129
    /**
130
     * Prepend a middleware to the init step.
131
     *
132
     * @param callable $middleware Middleware function to add.
133
     * @param string   $name       Name of the middleware.
134
     */
135
    public function prependInit(callable $middleware, $name = null)
136
    {
137
        $this->add(self::INIT, $name, $middleware, true);
138
    }
139
 
140
    /**
141
     * Append a middleware to the validate step.
142
     *
143
     * @param callable $middleware Middleware function to add.
144
     * @param string   $name       Name of the middleware.
145
     */
146
    public function appendValidate(callable $middleware, $name = null)
147
    {
148
        $this->add(self::VALIDATE, $name, $middleware);
149
    }
150
 
151
    /**
152
     * Prepend a middleware to the validate step.
153
     *
154
     * @param callable $middleware Middleware function to add.
155
     * @param string   $name       Name of the middleware.
156
     */
157
    public function prependValidate(callable $middleware, $name = null)
158
    {
159
        $this->add(self::VALIDATE, $name, $middleware, true);
160
    }
161
 
162
    /**
163
     * Append a middleware to the build step.
164
     *
165
     * @param callable $middleware Middleware function to add.
166
     * @param string   $name       Name of the middleware.
167
     */
168
    public function appendBuild(callable $middleware, $name = null)
169
    {
170
        $this->add(self::BUILD, $name, $middleware);
171
    }
172
 
173
    /**
174
     * Prepend a middleware to the build step.
175
     *
176
     * @param callable $middleware Middleware function to add.
177
     * @param string   $name       Name of the middleware.
178
     */
179
    public function prependBuild(callable $middleware, $name = null)
180
    {
181
        $this->add(self::BUILD, $name, $middleware, true);
182
    }
183
 
184
    /**
185
     * Append a middleware to the sign step.
186
     *
187
     * @param callable $middleware Middleware function to add.
188
     * @param string   $name       Name of the middleware.
189
     */
190
    public function appendSign(callable $middleware, $name = null)
191
    {
192
        $this->add(self::SIGN, $name, $middleware);
193
    }
194
 
195
    /**
196
     * Prepend a middleware to the sign step.
197
     *
198
     * @param callable $middleware Middleware function to add.
199
     * @param string   $name       Name of the middleware.
200
     */
201
    public function prependSign(callable $middleware, $name = null)
202
    {
203
        $this->add(self::SIGN, $name, $middleware, true);
204
    }
205
 
206
    /**
207
     * Append a middleware to the attempt step.
208
     *
209
     * @param callable $middleware Middleware function to add.
210
     * @param string   $name       Name of the middleware.
211
     */
212
    public function appendAttempt(callable $middleware, $name = null)
213
    {
214
        $this->add(self::ATTEMPT, $name, $middleware);
215
    }
216
 
217
    /**
218
     * Prepend a middleware to the attempt step.
219
     *
220
     * @param callable $middleware Middleware function to add.
221
     * @param string   $name       Name of the middleware.
222
     */
223
    public function prependAttempt(callable $middleware, $name = null)
224
    {
225
        $this->add(self::ATTEMPT, $name, $middleware, true);
226
    }
227
 
228
    /**
229
     * Add a middleware before the given middleware by name.
230
     *
231
     * @param string|callable $findName   Add before this
232
     * @param string          $withName   Optional name to give the middleware
233
     * @param callable        $middleware Middleware to add.
234
     */
235
    public function before($findName, $withName, callable $middleware)
236
    {
237
        $this->splice($findName, $withName, $middleware, true);
238
    }
239
 
240
    /**
241
     * Add a middleware after the given middleware by name.
242
     *
243
     * @param string|callable $findName   Add after this
244
     * @param string          $withName   Optional name to give the middleware
245
     * @param callable        $middleware Middleware to add.
246
     */
247
    public function after($findName, $withName, callable $middleware)
248
    {
249
        $this->splice($findName, $withName, $middleware, false);
250
    }
251
 
252
    /**
253
     * Remove a middleware by name or by instance from the list.
254
     *
255
     * @param string|callable $nameOrInstance Middleware to remove.
256
     */
257
    public function remove($nameOrInstance)
258
    {
259
        if (is_callable($nameOrInstance)) {
260
            $this->removeByInstance($nameOrInstance);
261
        } elseif (is_string($nameOrInstance)) {
262
            $this->removeByName($nameOrInstance);
263
        }
264
    }
265
 
266
    /**
267
     * Interpose a function between each middleware (e.g., allowing for a trace
268
     * through the middleware layers).
269
     *
270
     * The interpose function is a function that accepts a "step" argument as a
271
     * string and a "name" argument string. This function must then return a
272
     * function that accepts the next handler in the list. This function must
273
     * then return a function that accepts a CommandInterface and optional
274
     * RequestInterface and returns a promise that is fulfilled with an
275
     * Aws\ResultInterface or rejected with an Aws\Exception\AwsException
276
     * object.
277
     *
278
     * @param callable|null $fn Pass null to remove any previously set function
279
     */
280
    public function interpose(callable $fn = null)
281
    {
282
        $this->sorted = null;
283
        $this->interposeFn = $fn;
284
    }
285
 
286
    /**
287
     * Compose the middleware and handler into a single callable function.
288
     *
289
     * @return callable
290
     */
291
    public function resolve()
292
    {
293
        if (!($prev = $this->handler)) {
294
            throw new \LogicException('No handler has been specified');
295
        }
296
 
297
        if ($this->sorted === null) {
298
            $this->sortMiddleware();
299
        }
300
 
301
        foreach ($this->sorted as $fn) {
302
            $prev = $fn($prev);
303
        }
304
 
305
        return $prev;
306
    }
307
 
308
    /**
309
     * @return int
310
     */
311
    #[\ReturnTypeWillChange]
312
    public function count()
313
    {
314
        return count($this->steps[self::INIT])
315
            + count($this->steps[self::VALIDATE])
316
            + count($this->steps[self::BUILD])
317
            + count($this->steps[self::SIGN])
318
            + count($this->steps[self::ATTEMPT]);
319
    }
320
 
321
    /**
322
     * Splices a function into the middleware list at a specific position.
323
     *
324
     * @param          $findName
325
     * @param          $withName
326
     * @param callable $middleware
327
     * @param          $before
328
     */
329
    private function splice($findName, $withName, callable $middleware, $before)
330
    {
331
        if (!isset($this->named[$findName])) {
332
            throw new \InvalidArgumentException("$findName not found");
333
        }
334
 
335
        $idx = $this->sorted = null;
336
        $step = $this->named[$findName];
337
 
338
        if ($withName) {
339
            $this->named[$withName] = $step;
340
        }
341
 
342
        foreach ($this->steps[$step] as $i => $tuple) {
343
            if ($tuple[1] === $findName) {
344
                $idx = $i;
345
                break;
346
            }
347
        }
348
 
349
        $replacement = $before
350
            ? [$this->steps[$step][$idx], [$middleware, $withName]]
351
            : [[$middleware, $withName], $this->steps[$step][$idx]];
352
        array_splice($this->steps[$step], $idx, 1, $replacement);
353
    }
354
 
355
    /**
356
     * Provides a debug string for a given callable.
357
     *
358
     * @param array|callable $fn Function to write as a string.
359
     *
360
     * @return string
361
     */
362
    private function debugCallable($fn)
363
    {
364
        if (is_string($fn)) {
365
            return "callable({$fn})";
366
        }
367
 
368
        if (is_array($fn)) {
369
            $ele = is_string($fn[0]) ? $fn[0] : get_class($fn[0]);
370
            return "callable(['{$ele}', '{$fn[1]}'])";
371
        }
372
 
373
        return 'callable(' . spl_object_hash($fn) . ')';
374
    }
375
 
376
    /**
377
     * Sort the middleware, and interpose if needed in the sorted list.
378
     */
379
    private function sortMiddleware()
380
    {
381
        $this->sorted = [];
382
 
383
        if (!$this->interposeFn) {
384
            foreach ($this->steps as $step) {
385
                foreach ($step as $fn) {
386
                    $this->sorted[] = $fn[0];
387
                }
388
            }
389
            return;
390
        }
391
 
392
        $ifn = $this->interposeFn;
393
        // Interpose the interposeFn into the handler stack.
394
        foreach ($this->steps as $stepName => $step) {
395
            foreach ($step as $fn) {
396
                $this->sorted[] = $ifn($stepName, $fn[1]);
397
                $this->sorted[] = $fn[0];
398
            }
399
        }
400
    }
401
 
402
    private function removeByName($name)
403
    {
404
        if (!isset($this->named[$name])) {
405
            return;
406
        }
407
 
408
        $this->sorted = null;
409
        $step = $this->named[$name];
410
        $this->steps[$step] = array_values(
411
            array_filter(
412
                $this->steps[$step],
413
                function ($tuple) use ($name) {
414
                    return $tuple[1] !== $name;
415
                }
416
            )
417
        );
418
    }
419
 
420
    private function removeByInstance(callable $fn)
421
    {
422
        foreach ($this->steps as $k => $step) {
423
            foreach ($step as $j => $tuple) {
424
                if ($tuple[0] === $fn) {
425
                    $this->sorted = null;
426
                    unset($this->named[$this->steps[$k][$j][1]]);
427
                    unset($this->steps[$k][$j]);
428
                }
429
            }
430
        }
431
    }
432
 
433
    /**
434
     * Add a middleware to a step.
435
     *
436
     * @param string   $step       Middleware step.
437
     * @param string   $name       Middleware name.
438
     * @param callable $middleware Middleware function to add.
439
     * @param bool     $prepend    Prepend instead of append.
440
     */
441
    private function add($step, $name, callable $middleware, $prepend = false)
442
    {
443
        $this->sorted = null;
444
 
445
        if ($prepend) {
446
            $this->steps[$step][] = [$middleware, $name];
447
        } else {
448
            array_unshift($this->steps[$step], [$middleware, $name]);
449
        }
450
 
451
        if ($name) {
452
            $this->named[$name] = $step;
453
        }
454
    }
455
}