MiddlewareQueue.php 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. <?php
  2. /**
  3. * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
  4. * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  5. *
  6. * Licensed under The MIT License
  7. * For full copyright and license information, please see the LICENSE.txt
  8. * Redistributions of files must retain the above copyright notice.
  9. *
  10. * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  11. * @link https://cakephp.org CakePHP(tm) Project
  12. * @since 3.3.0
  13. * @license https://opensource.org/licenses/mit-license.php MIT License
  14. */
  15. namespace Cake\Http;
  16. use Cake\Core\App;
  17. use Countable;
  18. use LogicException;
  19. use RuntimeException;
  20. /**
  21. * Provides methods for creating and manipulating a "queue" of middleware callables.
  22. * This queue is used to process a request and response via \Cake\Http\Runner.
  23. */
  24. class MiddlewareQueue implements Countable
  25. {
  26. /**
  27. * The queue of middlewares.
  28. *
  29. * @var array
  30. */
  31. protected $queue = [];
  32. /**
  33. * The queue of middleware callables.
  34. *
  35. * @var callable[]
  36. */
  37. protected $callables = [];
  38. /**
  39. * Constructor
  40. *
  41. * @param array $middleware The list of middleware to append.
  42. */
  43. public function __construct(array $middleware = [])
  44. {
  45. $this->queue = $middleware;
  46. }
  47. /**
  48. * Get the middleware at the provided index.
  49. *
  50. * @param int $index The index to fetch.
  51. * @return callable|null Either the callable middleware or null
  52. * if the index is undefined.
  53. */
  54. public function get($index)
  55. {
  56. if (isset($this->callables[$index])) {
  57. return $this->callables[$index];
  58. }
  59. return $this->resolve($index);
  60. }
  61. /**
  62. * Resolve middleware name to callable.
  63. *
  64. * @param int $index The index to fetch.
  65. * @return callable|null Either the callable middleware or null
  66. * if the index is undefined.
  67. */
  68. protected function resolve($index)
  69. {
  70. if (!isset($this->queue[$index])) {
  71. return null;
  72. }
  73. if (is_string($this->queue[$index])) {
  74. $class = $this->queue[$index];
  75. $className = App::className($class, 'Middleware', 'Middleware');
  76. if (!$className || !class_exists($className)) {
  77. throw new RuntimeException(sprintf(
  78. 'Middleware "%s" was not found.',
  79. $class
  80. ));
  81. }
  82. $callable = new $className;
  83. } else {
  84. $callable = $this->queue[$index];
  85. }
  86. return $this->callables[$index] = $callable;
  87. }
  88. /**
  89. * Append a middleware callable to the end of the queue.
  90. *
  91. * @param callable|string|array $middleware The middleware(s) to append.
  92. * @return $this
  93. */
  94. public function add($middleware)
  95. {
  96. if (is_array($middleware)) {
  97. $this->queue = array_merge($this->queue, $middleware);
  98. return $this;
  99. }
  100. $this->queue[] = $middleware;
  101. return $this;
  102. }
  103. /**
  104. * Alias for MiddlewareQueue::add().
  105. *
  106. * @param callable|string|array $middleware The middleware(s) to append.
  107. * @return $this
  108. * @see MiddlewareQueue::add()
  109. */
  110. public function push($middleware)
  111. {
  112. return $this->add($middleware);
  113. }
  114. /**
  115. * Prepend a middleware to the start of the queue.
  116. *
  117. * @param callable|string|array $middleware The middleware(s) to prepend.
  118. * @return $this
  119. */
  120. public function prepend($middleware)
  121. {
  122. if (is_array($middleware)) {
  123. $this->queue = array_merge($middleware, $this->queue);
  124. return $this;
  125. }
  126. array_unshift($this->queue, $middleware);
  127. return $this;
  128. }
  129. /**
  130. * Insert a middleware callable at a specific index.
  131. *
  132. * If the index already exists, the new callable will be inserted,
  133. * and the existing element will be shifted one index greater.
  134. *
  135. * @param int $index The index to insert at.
  136. * @param callable|string $middleware The middleware to insert.
  137. * @return $this
  138. */
  139. public function insertAt($index, $middleware)
  140. {
  141. array_splice($this->queue, $index, 0, [$middleware]);
  142. return $this;
  143. }
  144. /**
  145. * Insert a middleware object before the first matching class.
  146. *
  147. * Finds the index of the first middleware that matches the provided class,
  148. * and inserts the supplied callable before it.
  149. *
  150. * @param string $class The classname to insert the middleware before.
  151. * @param callable|string $middleware The middleware to insert.
  152. * @return $this
  153. * @throws \LogicException If middleware to insert before is not found.
  154. */
  155. public function insertBefore($class, $middleware)
  156. {
  157. $found = false;
  158. $i = null;
  159. foreach ($this->queue as $i => $object) {
  160. if ((is_string($object) && $object === $class)
  161. || is_a($object, $class)
  162. ) {
  163. $found = true;
  164. break;
  165. }
  166. }
  167. if ($found) {
  168. return $this->insertAt($i, $middleware);
  169. }
  170. throw new LogicException(sprintf("No middleware matching '%s' could be found.", $class));
  171. }
  172. /**
  173. * Insert a middleware object after the first matching class.
  174. *
  175. * Finds the index of the first middleware that matches the provided class,
  176. * and inserts the supplied callable after it. If the class is not found,
  177. * this method will behave like add().
  178. *
  179. * @param string $class The classname to insert the middleware before.
  180. * @param callable|string $middleware The middleware to insert.
  181. * @return $this
  182. */
  183. public function insertAfter($class, $middleware)
  184. {
  185. $found = false;
  186. $i = null;
  187. foreach ($this->queue as $i => $object) {
  188. if ((is_string($object) && $object === $class)
  189. || is_a($object, $class)
  190. ) {
  191. $found = true;
  192. break;
  193. }
  194. }
  195. if ($found) {
  196. return $this->insertAt($i + 1, $middleware);
  197. }
  198. return $this->add($middleware);
  199. }
  200. /**
  201. * Get the number of connected middleware layers.
  202. *
  203. * Implement the Countable interface.
  204. *
  205. * @return int
  206. */
  207. public function count()
  208. {
  209. return count($this->queue);
  210. }
  211. }