No Description

HandlerStack.php 7.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. <?php
  2. namespace GuzzleHttp;
  3. use Psr\Http\Message\RequestInterface;
  4. require __DIR__ . '/functions.php';
  5. /**
  6. * Creates a composed Guzzle handler function by stacking middlewares on top of
  7. * an HTTP handler function.
  8. */
  9. class HandlerStack
  10. {
  11. /** @var callable */
  12. private $handler;
  13. /** @var array */
  14. private $stack = [];
  15. /** @var callable|null */
  16. private $cached;
  17. /**
  18. * Creates a default handler stack that can be used by clients.
  19. *
  20. * The returned handler will wrap the provided handler or use the most
  21. * appropriate default handler for you system. The returned HandlerStack has
  22. * support for cookies, redirects, HTTP error exceptions, and preparing a body
  23. * before sending.
  24. *
  25. * The returned handler stack can be passed to a client in the "handler"
  26. * option.
  27. *
  28. * @param callable $handler HTTP handler function to use with the stack. If no
  29. * handler is provided, the best handler for your
  30. * system will be utilized.
  31. *
  32. * @return HandlerStack
  33. */
  34. public static function create(callable $handler = null)
  35. {
  36. $stack = new self($handler ?: choose_handler());
  37. $stack->push(Middleware::httpErrors(), 'http_errors');
  38. $stack->push(Middleware::redirect(), 'allow_redirects');
  39. $stack->push(Middleware::cookies(), 'cookies');
  40. $stack->push(Middleware::prepareBody(), 'prepare_body');
  41. return $stack;
  42. }
  43. /**
  44. * @param callable $handler Underlying HTTP handler.
  45. */
  46. public function __construct(callable $handler = null)
  47. {
  48. $this->handler = $handler;
  49. }
  50. /**
  51. * Invokes the handler stack as a composed handler
  52. *
  53. * @param RequestInterface $request
  54. * @param array $options
  55. */
  56. public function __invoke(RequestInterface $request, array $options)
  57. {
  58. if (!$this->cached) {
  59. $this->cached = $this->resolve();
  60. }
  61. $handler = $this->cached;
  62. return $handler($request, $options);
  63. }
  64. /**
  65. * Dumps a string representation of the stack.
  66. *
  67. * @return string
  68. */
  69. public function __toString()
  70. {
  71. $depth = 0;
  72. $stack = [];
  73. if ($this->handler) {
  74. $stack[] = "0) Handler: " . $this->debugCallable($this->handler);
  75. }
  76. $result = '';
  77. foreach (array_reverse($this->stack) as $tuple) {
  78. $depth++;
  79. $str = "{$depth}) Name: '{$tuple[1]}', ";
  80. $str .= "Function: " . $this->debugCallable($tuple[0]);
  81. $result = "> {$str}\n{$result}";
  82. $stack[] = $str;
  83. }
  84. foreach (array_keys($stack) as $k) {
  85. $result .= "< {$stack[$k]}\n";
  86. }
  87. return $result;
  88. }
  89. /**
  90. * Set the HTTP handler that actually returns a promise.
  91. *
  92. * @param callable $handler Accepts a request and array of options and
  93. * returns a Promise.
  94. */
  95. public function setHandler(callable $handler)
  96. {
  97. $this->handler = $handler;
  98. $this->cached = null;
  99. }
  100. /**
  101. * Returns true if the builder has a handler.
  102. *
  103. * @return bool
  104. */
  105. public function hasHandler()
  106. {
  107. return (bool) $this->handler;
  108. }
  109. /**
  110. * Unshift a middleware to the bottom of the stack.
  111. *
  112. * @param callable $middleware Middleware function
  113. * @param string $name Name to register for this middleware.
  114. */
  115. public function unshift(callable $middleware, $name = null)
  116. {
  117. array_unshift($this->stack, [$middleware, $name]);
  118. $this->cached = null;
  119. }
  120. /**
  121. * Push a middleware to the top of the stack.
  122. *
  123. * @param callable $middleware Middleware function
  124. * @param string $name Name to register for this middleware.
  125. */
  126. public function push(callable $middleware, $name = '')
  127. {
  128. $this->stack[] = [$middleware, $name];
  129. $this->cached = null;
  130. }
  131. /**
  132. * Add a middleware before another middleware by name.
  133. *
  134. * @param string $findName Middleware to find
  135. * @param callable $middleware Middleware function
  136. * @param string $withName Name to register for this middleware.
  137. */
  138. public function before($findName, callable $middleware, $withName = '')
  139. {
  140. $this->splice($findName, $withName, $middleware, true);
  141. }
  142. /**
  143. * Add a middleware after another middleware by name.
  144. *
  145. * @param string $findName Middleware to find
  146. * @param callable $middleware Middleware function
  147. * @param string $withName Name to register for this middleware.
  148. */
  149. public function after($findName, callable $middleware, $withName = '')
  150. {
  151. $this->splice($findName, $withName, $middleware, false);
  152. }
  153. /**
  154. * Remove a middleware by instance or name from the stack.
  155. *
  156. * @param callable|string $remove Middleware to remove by instance or name.
  157. */
  158. public function remove($remove)
  159. {
  160. $this->cached = null;
  161. $idx = is_callable($remove) ? 0 : 1;
  162. $this->stack = array_values(array_filter(
  163. $this->stack,
  164. function ($tuple) use ($idx, $remove) {
  165. return $tuple[$idx] !== $remove;
  166. }
  167. ));
  168. }
  169. /**
  170. * Compose the middleware and handler into a single callable function.
  171. *
  172. * @return callable
  173. */
  174. public function resolve()
  175. {
  176. if (!($prev = $this->handler)) {
  177. throw new \LogicException('No handler has been specified');
  178. }
  179. foreach (array_reverse($this->stack) as $fn) {
  180. $prev = $fn[0]($prev);
  181. }
  182. return $prev;
  183. }
  184. /**
  185. * @param $name
  186. * @return int
  187. */
  188. private function findByName($name)
  189. {
  190. foreach ($this->stack as $k => $v) {
  191. if ($v[1] === $name) {
  192. return $k;
  193. }
  194. }
  195. throw new \InvalidArgumentException("Middleware not found: $name");
  196. }
  197. /**
  198. * Splices a function into the middleware list at a specific position.
  199. *
  200. * @param $findName
  201. * @param $withName
  202. * @param callable $middleware
  203. * @param $before
  204. */
  205. private function splice($findName, $withName, callable $middleware, $before)
  206. {
  207. $this->cached = null;
  208. $idx = $this->findByName($findName);
  209. $tuple = [$middleware, $withName];
  210. if ($before) {
  211. if ($idx === 0) {
  212. array_unshift($this->stack, $tuple);
  213. } else {
  214. $replacement = [$tuple, $this->stack[$idx]];
  215. array_splice($this->stack, $idx, 1, $replacement);
  216. }
  217. } elseif ($idx === count($this->stack) - 1) {
  218. $this->stack[] = $tuple;
  219. } else {
  220. $replacement = [$this->stack[$idx], $tuple];
  221. array_splice($this->stack, $idx, 1, $replacement);
  222. }
  223. }
  224. /**
  225. * Provides a debug string for a given callable.
  226. *
  227. * @param array|callable $fn Function to write as a string.
  228. *
  229. * @return string
  230. */
  231. private function debugCallable($fn)
  232. {
  233. if (is_string($fn)) {
  234. return "callable({$fn})";
  235. }
  236. if (is_array($fn)) {
  237. return is_string($fn[0])
  238. ? "callable({$fn[0]}::{$fn[1]})"
  239. : "callable(['" . get_class($fn[0]) . "', '{$fn[1]}'])";
  240. }
  241. return 'callable(' . spl_object_hash($fn) . ')';
  242. }
  243. }