123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455 |
- <?php
- namespace Aws;
- /**
- * Builds a single handler function from zero or more middleware functions and
- * a handler. The handler function is then used to send command objects and
- * return a promise that is resolved with an AWS result object.
- *
- * The "front" of the list is invoked before the "end" of the list. You can add
- * middleware to the front of the list using one of the "prepend" method, and
- * the end of the list using one of the "append" method. The last function
- * invoked in a handler list is the handler (a function that does not accept a
- * next handler but rather is responsible for returning a promise that is
- * fulfilled with an Aws\ResultInterface object).
- *
- * Handlers are ordered using a "step" that describes the step at which the
- * SDK is when sending a command. The available steps are:
- *
- * - init: The command is being initialized, allowing you to do things like add
- * default options.
- * - validate: The command is being validated before it is serialized
- * - build: The command is being serialized into an HTTP request. A middleware
- * in this step MUST serialize an HTTP request and populate the "@request"
- * parameter of a command with the request such that it is available to
- * subsequent middleware.
- * - sign: The request is being signed and prepared to be sent over the wire.
- *
- * Middleware can be registered with a name to allow you to easily add a
- * middleware before or after another middleware by name. This also allows you
- * to remove a middleware by name (in addition to removing by instance).
- */
- class HandlerList implements \Countable
- {
- const INIT = 'init';
- const VALIDATE = 'validate';
- const BUILD = 'build';
- const SIGN = 'sign';
- const ATTEMPT = 'attempt';
- /** @var callable */
- private $handler;
- /** @var array */
- private $named = [];
- /** @var array */
- private $sorted;
- /** @var callable|null */
- private $interposeFn;
- /** @var array Steps (in reverse order) */
- private $steps = [
- self::ATTEMPT => [],
- self::SIGN => [],
- self::BUILD => [],
- self::VALIDATE => [],
- self::INIT => [],
- ];
- /**
- * @param callable $handler HTTP handler.
- */
- public function __construct(callable $handler = null)
- {
- $this->handler = $handler;
- }
- /**
- * Dumps a string representation of the list.
- *
- * @return string
- */
- public function __toString()
- {
- $str = '';
- $i = 0;
- foreach (array_reverse($this->steps) as $k => $step) {
- foreach (array_reverse($step) as $j => $tuple) {
- $str .= "{$i}) Step: {$k}, ";
- if ($tuple[1]) {
- $str .= "Name: {$tuple[1]}, ";
- }
- $str .= "Function: " . $this->debugCallable($tuple[0]) . "\n";
- $i++;
- }
- }
- if ($this->handler) {
- $str .= "{$i}) Handler: " . $this->debugCallable($this->handler) . "\n";
- }
- return $str;
- }
- /**
- * Set the HTTP handler that actually returns a response.
- *
- * @param callable $handler Function that accepts a request and array of
- * options and returns a Promise.
- */
- public function setHandler(callable $handler)
- {
- $this->handler = $handler;
- }
- /**
- * Returns true if the builder has a handler.
- *
- * @return bool
- */
- public function hasHandler()
- {
- return (bool) $this->handler;
- }
- /**
- * Append a middleware to the init step.
- *
- * @param callable $middleware Middleware function to add.
- * @param string $name Name of the middleware.
- */
- public function appendInit(callable $middleware, $name = null)
- {
- $this->add(self::INIT, $name, $middleware);
- }
- /**
- * Prepend a middleware to the init step.
- *
- * @param callable $middleware Middleware function to add.
- * @param string $name Name of the middleware.
- */
- public function prependInit(callable $middleware, $name = null)
- {
- $this->add(self::INIT, $name, $middleware, true);
- }
- /**
- * Append a middleware to the validate step.
- *
- * @param callable $middleware Middleware function to add.
- * @param string $name Name of the middleware.
- */
- public function appendValidate(callable $middleware, $name = null)
- {
- $this->add(self::VALIDATE, $name, $middleware);
- }
- /**
- * Prepend a middleware to the validate step.
- *
- * @param callable $middleware Middleware function to add.
- * @param string $name Name of the middleware.
- */
- public function prependValidate(callable $middleware, $name = null)
- {
- $this->add(self::VALIDATE, $name, $middleware, true);
- }
- /**
- * Append a middleware to the build step.
- *
- * @param callable $middleware Middleware function to add.
- * @param string $name Name of the middleware.
- */
- public function appendBuild(callable $middleware, $name = null)
- {
- $this->add(self::BUILD, $name, $middleware);
- }
- /**
- * Prepend a middleware to the build step.
- *
- * @param callable $middleware Middleware function to add.
- * @param string $name Name of the middleware.
- */
- public function prependBuild(callable $middleware, $name = null)
- {
- $this->add(self::BUILD, $name, $middleware, true);
- }
- /**
- * Append a middleware to the sign step.
- *
- * @param callable $middleware Middleware function to add.
- * @param string $name Name of the middleware.
- */
- public function appendSign(callable $middleware, $name = null)
- {
- $this->add(self::SIGN, $name, $middleware);
- }
- /**
- * Prepend a middleware to the sign step.
- *
- * @param callable $middleware Middleware function to add.
- * @param string $name Name of the middleware.
- */
- public function prependSign(callable $middleware, $name = null)
- {
- $this->add(self::SIGN, $name, $middleware, true);
- }
- /**
- * Append a middleware to the attempt step.
- *
- * @param callable $middleware Middleware function to add.
- * @param string $name Name of the middleware.
- */
- public function appendAttempt(callable $middleware, $name = null)
- {
- $this->add(self::ATTEMPT, $name, $middleware);
- }
- /**
- * Prepend a middleware to the attempt step.
- *
- * @param callable $middleware Middleware function to add.
- * @param string $name Name of the middleware.
- */
- public function prependAttempt(callable $middleware, $name = null)
- {
- $this->add(self::ATTEMPT, $name, $middleware, true);
- }
- /**
- * Add a middleware before the given middleware by name.
- *
- * @param string|callable $findName Add before this
- * @param string $withName Optional name to give the middleware
- * @param callable $middleware Middleware to add.
- */
- public function before($findName, $withName, callable $middleware)
- {
- $this->splice($findName, $withName, $middleware, true);
- }
- /**
- * Add a middleware after the given middleware by name.
- *
- * @param string|callable $findName Add after this
- * @param string $withName Optional name to give the middleware
- * @param callable $middleware Middleware to add.
- */
- public function after($findName, $withName, callable $middleware)
- {
- $this->splice($findName, $withName, $middleware, false);
- }
- /**
- * Remove a middleware by name or by instance from the list.
- *
- * @param string|callable $nameOrInstance Middleware to remove.
- */
- public function remove($nameOrInstance)
- {
- if (is_callable($nameOrInstance)) {
- $this->removeByInstance($nameOrInstance);
- } elseif (is_string($nameOrInstance)) {
- $this->removeByName($nameOrInstance);
- }
- }
- /**
- * Interpose a function between each middleware (e.g., allowing for a trace
- * through the middleware layers).
- *
- * The interpose function is a function that accepts a "step" argument as a
- * string and a "name" argument string. This function must then return a
- * function that accepts the next handler in the list. This function must
- * then return a function that accepts a CommandInterface and optional
- * RequestInterface and returns a promise that is fulfilled with an
- * Aws\ResultInterface or rejected with an Aws\Exception\AwsException
- * object.
- *
- * @param callable|null $fn Pass null to remove any previously set function
- */
- public function interpose(callable $fn = null)
- {
- $this->sorted = null;
- $this->interposeFn = $fn;
- }
- /**
- * Compose the middleware and handler into a single callable function.
- *
- * @return callable
- */
- public function resolve()
- {
- if (!($prev = $this->handler)) {
- throw new \LogicException('No handler has been specified');
- }
- if ($this->sorted === null) {
- $this->sortMiddleware();
- }
- foreach ($this->sorted as $fn) {
- $prev = $fn($prev);
- }
- return $prev;
- }
- /**
- * @return int
- */
- #[\ReturnTypeWillChange]
- public function count()
- {
- return count($this->steps[self::INIT])
- + count($this->steps[self::VALIDATE])
- + count($this->steps[self::BUILD])
- + count($this->steps[self::SIGN])
- + count($this->steps[self::ATTEMPT]);
- }
- /**
- * Splices a function into the middleware list at a specific position.
- *
- * @param $findName
- * @param $withName
- * @param callable $middleware
- * @param $before
- */
- private function splice($findName, $withName, callable $middleware, $before)
- {
- if (!isset($this->named[$findName])) {
- throw new \InvalidArgumentException("$findName not found");
- }
- $idx = $this->sorted = null;
- $step = $this->named[$findName];
- if ($withName) {
- $this->named[$withName] = $step;
- }
- foreach ($this->steps[$step] as $i => $tuple) {
- if ($tuple[1] === $findName) {
- $idx = $i;
- break;
- }
- }
- $replacement = $before
- ? [$this->steps[$step][$idx], [$middleware, $withName]]
- : [[$middleware, $withName], $this->steps[$step][$idx]];
- array_splice($this->steps[$step], $idx, 1, $replacement);
- }
- /**
- * Provides a debug string for a given callable.
- *
- * @param array|callable $fn Function to write as a string.
- *
- * @return string
- */
- private function debugCallable($fn)
- {
- if (is_string($fn)) {
- return "callable({$fn})";
- }
- if (is_array($fn)) {
- $ele = is_string($fn[0]) ? $fn[0] : get_class($fn[0]);
- return "callable(['{$ele}', '{$fn[1]}'])";
- }
- return 'callable(' . spl_object_hash($fn) . ')';
- }
- /**
- * Sort the middleware, and interpose if needed in the sorted list.
- */
- private function sortMiddleware()
- {
- $this->sorted = [];
- if (!$this->interposeFn) {
- foreach ($this->steps as $step) {
- foreach ($step as $fn) {
- $this->sorted[] = $fn[0];
- }
- }
- return;
- }
- $ifn = $this->interposeFn;
- // Interpose the interposeFn into the handler stack.
- foreach ($this->steps as $stepName => $step) {
- foreach ($step as $fn) {
- $this->sorted[] = $ifn($stepName, $fn[1]);
- $this->sorted[] = $fn[0];
- }
- }
- }
- private function removeByName($name)
- {
- if (!isset($this->named[$name])) {
- return;
- }
- $this->sorted = null;
- $step = $this->named[$name];
- $this->steps[$step] = array_values(
- array_filter(
- $this->steps[$step],
- function ($tuple) use ($name) {
- return $tuple[1] !== $name;
- }
- )
- );
- }
- private function removeByInstance(callable $fn)
- {
- foreach ($this->steps as $k => $step) {
- foreach ($step as $j => $tuple) {
- if ($tuple[0] === $fn) {
- $this->sorted = null;
- unset($this->named[$this->steps[$k][$j][1]]);
- unset($this->steps[$k][$j]);
- }
- }
- }
- }
- /**
- * Add a middleware to a step.
- *
- * @param string $step Middleware step.
- * @param string $name Middleware name.
- * @param callable $middleware Middleware function to add.
- * @param bool $prepend Prepend instead of append.
- */
- private function add($step, $name, callable $middleware, $prepend = false)
- {
- $this->sorted = null;
- if ($prepend) {
- $this->steps[$step][] = [$middleware, $name];
- } else {
- array_unshift($this->steps[$step], [$middleware, $name]);
- }
- if ($name) {
- $this->named[$name] = $step;
- }
- }
- }
|