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
/**
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
     */
1441 ariadna 64
    public function __construct(?callable $handler = null)
1 efrain 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
    /**
1441 ariadna 119
     * Checks if a middleware exists. The middleware
120
     * should have been added with a name in order to
121
     * use this method.
122
     *
123
     * @param string $name
124
     *
125
     * @return bool
126
     */
127
    public function hasMiddleware(string $name): bool
128
    {
129
        return isset($this->named[$name]);
130
    }
131
 
132
    /**
1 efrain 133
     * Append a middleware to the init step.
134
     *
135
     * @param callable $middleware Middleware function to add.
136
     * @param string   $name       Name of the middleware.
137
     */
138
    public function appendInit(callable $middleware, $name = null)
139
    {
140
        $this->add(self::INIT, $name, $middleware);
141
    }
142
 
143
    /**
144
     * Prepend a middleware to the init step.
145
     *
146
     * @param callable $middleware Middleware function to add.
147
     * @param string   $name       Name of the middleware.
148
     */
149
    public function prependInit(callable $middleware, $name = null)
150
    {
151
        $this->add(self::INIT, $name, $middleware, true);
152
    }
153
 
154
    /**
155
     * Append a middleware to the validate step.
156
     *
157
     * @param callable $middleware Middleware function to add.
158
     * @param string   $name       Name of the middleware.
159
     */
160
    public function appendValidate(callable $middleware, $name = null)
161
    {
162
        $this->add(self::VALIDATE, $name, $middleware);
163
    }
164
 
165
    /**
166
     * Prepend a middleware to the validate step.
167
     *
168
     * @param callable $middleware Middleware function to add.
169
     * @param string   $name       Name of the middleware.
170
     */
171
    public function prependValidate(callable $middleware, $name = null)
172
    {
173
        $this->add(self::VALIDATE, $name, $middleware, true);
174
    }
175
 
176
    /**
177
     * Append a middleware to the build step.
178
     *
179
     * @param callable $middleware Middleware function to add.
180
     * @param string   $name       Name of the middleware.
181
     */
182
    public function appendBuild(callable $middleware, $name = null)
183
    {
184
        $this->add(self::BUILD, $name, $middleware);
185
    }
186
 
187
    /**
188
     * Prepend a middleware to the build step.
189
     *
190
     * @param callable $middleware Middleware function to add.
191
     * @param string   $name       Name of the middleware.
192
     */
193
    public function prependBuild(callable $middleware, $name = null)
194
    {
195
        $this->add(self::BUILD, $name, $middleware, true);
196
    }
197
 
198
    /**
199
     * Append a middleware to the sign step.
200
     *
201
     * @param callable $middleware Middleware function to add.
202
     * @param string   $name       Name of the middleware.
203
     */
204
    public function appendSign(callable $middleware, $name = null)
205
    {
206
        $this->add(self::SIGN, $name, $middleware);
207
    }
208
 
209
    /**
210
     * Prepend a middleware to the sign step.
211
     *
212
     * @param callable $middleware Middleware function to add.
213
     * @param string   $name       Name of the middleware.
214
     */
215
    public function prependSign(callable $middleware, $name = null)
216
    {
217
        $this->add(self::SIGN, $name, $middleware, true);
218
    }
219
 
220
    /**
221
     * Append a middleware to the attempt step.
222
     *
223
     * @param callable $middleware Middleware function to add.
224
     * @param string   $name       Name of the middleware.
225
     */
226
    public function appendAttempt(callable $middleware, $name = null)
227
    {
228
        $this->add(self::ATTEMPT, $name, $middleware);
229
    }
230
 
231
    /**
232
     * Prepend a middleware to the attempt step.
233
     *
234
     * @param callable $middleware Middleware function to add.
235
     * @param string   $name       Name of the middleware.
236
     */
237
    public function prependAttempt(callable $middleware, $name = null)
238
    {
239
        $this->add(self::ATTEMPT, $name, $middleware, true);
240
    }
241
 
242
    /**
243
     * Add a middleware before the given middleware by name.
244
     *
245
     * @param string|callable $findName   Add before this
246
     * @param string          $withName   Optional name to give the middleware
247
     * @param callable        $middleware Middleware to add.
248
     */
249
    public function before($findName, $withName, callable $middleware)
250
    {
251
        $this->splice($findName, $withName, $middleware, true);
252
    }
253
 
254
    /**
255
     * Add a middleware after the given middleware by name.
256
     *
257
     * @param string|callable $findName   Add after this
258
     * @param string          $withName   Optional name to give the middleware
259
     * @param callable        $middleware Middleware to add.
260
     */
261
    public function after($findName, $withName, callable $middleware)
262
    {
263
        $this->splice($findName, $withName, $middleware, false);
264
    }
265
 
266
    /**
267
     * Remove a middleware by name or by instance from the list.
268
     *
269
     * @param string|callable $nameOrInstance Middleware to remove.
270
     */
271
    public function remove($nameOrInstance)
272
    {
273
        if (is_callable($nameOrInstance)) {
274
            $this->removeByInstance($nameOrInstance);
275
        } elseif (is_string($nameOrInstance)) {
276
            $this->removeByName($nameOrInstance);
277
        }
278
    }
279
 
280
    /**
281
     * Interpose a function between each middleware (e.g., allowing for a trace
282
     * through the middleware layers).
283
     *
284
     * The interpose function is a function that accepts a "step" argument as a
285
     * string and a "name" argument string. This function must then return a
286
     * function that accepts the next handler in the list. This function must
287
     * then return a function that accepts a CommandInterface and optional
288
     * RequestInterface and returns a promise that is fulfilled with an
289
     * Aws\ResultInterface or rejected with an Aws\Exception\AwsException
290
     * object.
291
     *
292
     * @param callable|null $fn Pass null to remove any previously set function
293
     */
1441 ariadna 294
    public function interpose(?callable $fn = null)
1 efrain 295
    {
296
        $this->sorted = null;
297
        $this->interposeFn = $fn;
298
    }
299
 
300
    /**
301
     * Compose the middleware and handler into a single callable function.
302
     *
303
     * @return callable
304
     */
305
    public function resolve()
306
    {
307
        if (!($prev = $this->handler)) {
308
            throw new \LogicException('No handler has been specified');
309
        }
310
 
311
        if ($this->sorted === null) {
312
            $this->sortMiddleware();
313
        }
314
 
315
        foreach ($this->sorted as $fn) {
316
            $prev = $fn($prev);
317
        }
318
 
319
        return $prev;
320
    }
321
 
322
    /**
323
     * @return int
324
     */
325
    #[\ReturnTypeWillChange]
326
    public function count()
327
    {
328
        return count($this->steps[self::INIT])
329
            + count($this->steps[self::VALIDATE])
330
            + count($this->steps[self::BUILD])
331
            + count($this->steps[self::SIGN])
332
            + count($this->steps[self::ATTEMPT]);
333
    }
334
 
335
    /**
336
     * Splices a function into the middleware list at a specific position.
337
     *
338
     * @param          $findName
339
     * @param          $withName
340
     * @param callable $middleware
341
     * @param          $before
342
     */
343
    private function splice($findName, $withName, callable $middleware, $before)
344
    {
345
        if (!isset($this->named[$findName])) {
346
            throw new \InvalidArgumentException("$findName not found");
347
        }
348
 
349
        $idx = $this->sorted = null;
350
        $step = $this->named[$findName];
351
 
352
        if ($withName) {
353
            $this->named[$withName] = $step;
354
        }
355
 
356
        foreach ($this->steps[$step] as $i => $tuple) {
357
            if ($tuple[1] === $findName) {
358
                $idx = $i;
359
                break;
360
            }
361
        }
362
 
363
        $replacement = $before
364
            ? [$this->steps[$step][$idx], [$middleware, $withName]]
365
            : [[$middleware, $withName], $this->steps[$step][$idx]];
366
        array_splice($this->steps[$step], $idx, 1, $replacement);
367
    }
368
 
369
    /**
370
     * Provides a debug string for a given callable.
371
     *
372
     * @param array|callable $fn Function to write as a string.
373
     *
374
     * @return string
375
     */
376
    private function debugCallable($fn)
377
    {
378
        if (is_string($fn)) {
379
            return "callable({$fn})";
380
        }
381
 
382
        if (is_array($fn)) {
383
            $ele = is_string($fn[0]) ? $fn[0] : get_class($fn[0]);
384
            return "callable(['{$ele}', '{$fn[1]}'])";
385
        }
386
 
387
        return 'callable(' . spl_object_hash($fn) . ')';
388
    }
389
 
390
    /**
391
     * Sort the middleware, and interpose if needed in the sorted list.
392
     */
393
    private function sortMiddleware()
394
    {
395
        $this->sorted = [];
396
 
397
        if (!$this->interposeFn) {
398
            foreach ($this->steps as $step) {
399
                foreach ($step as $fn) {
400
                    $this->sorted[] = $fn[0];
401
                }
402
            }
403
            return;
404
        }
405
 
406
        $ifn = $this->interposeFn;
407
        // Interpose the interposeFn into the handler stack.
408
        foreach ($this->steps as $stepName => $step) {
409
            foreach ($step as $fn) {
410
                $this->sorted[] = $ifn($stepName, $fn[1]);
411
                $this->sorted[] = $fn[0];
412
            }
413
        }
414
    }
415
 
416
    private function removeByName($name)
417
    {
418
        if (!isset($this->named[$name])) {
419
            return;
420
        }
421
 
422
        $this->sorted = null;
423
        $step = $this->named[$name];
424
        $this->steps[$step] = array_values(
425
            array_filter(
426
                $this->steps[$step],
427
                function ($tuple) use ($name) {
428
                    return $tuple[1] !== $name;
429
                }
430
            )
431
        );
432
    }
433
 
434
    private function removeByInstance(callable $fn)
435
    {
436
        foreach ($this->steps as $k => $step) {
437
            foreach ($step as $j => $tuple) {
438
                if ($tuple[0] === $fn) {
439
                    $this->sorted = null;
440
                    unset($this->named[$this->steps[$k][$j][1]]);
441
                    unset($this->steps[$k][$j]);
442
                }
443
            }
444
        }
445
    }
446
 
447
    /**
448
     * Add a middleware to a step.
449
     *
450
     * @param string   $step       Middleware step.
451
     * @param string   $name       Middleware name.
452
     * @param callable $middleware Middleware function to add.
453
     * @param bool     $prepend    Prepend instead of append.
454
     */
455
    private function add($step, $name, callable $middleware, $prepend = false)
456
    {
457
        $this->sorted = null;
458
 
459
        if ($prepend) {
460
            $this->steps[$step][] = [$middleware, $name];
461
        } else {
462
            array_unshift($this->steps[$step], [$middleware, $name]);
463
        }
464
 
465
        if ($name) {
466
            $this->named[$name] = $step;
467
        }
468
    }
469
}