vendor/aws/aws-sdk-php/src/HandlerList.php line 284

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