/vendor/laravel/framework/src/Illuminate/Routing/RouteCollection.php

https://gitlab.com/Pasantias/pasantiasASLG · PHP · 314 lines · 128 code · 41 blank · 145 comment · 9 complexity · 0235495748d6fcf2056b2f1ddfd190ae MD5 · raw file

  1. <?php
  2. namespace Illuminate\Routing;
  3. use Countable;
  4. use ArrayIterator;
  5. use IteratorAggregate;
  6. use Illuminate\Support\Arr;
  7. use Illuminate\Http\Request;
  8. use Illuminate\Http\Response;
  9. use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
  10. use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
  11. class RouteCollection implements Countable, IteratorAggregate
  12. {
  13. /**
  14. * An array of the routes keyed by method.
  15. *
  16. * @var array
  17. */
  18. protected $routes = [];
  19. /**
  20. * An flattened array of all of the routes.
  21. *
  22. * @var array
  23. */
  24. protected $allRoutes = [];
  25. /**
  26. * A look-up table of routes by their names.
  27. *
  28. * @var array
  29. */
  30. protected $nameList = [];
  31. /**
  32. * A look-up table of routes by controller action.
  33. *
  34. * @var array
  35. */
  36. protected $actionList = [];
  37. /**
  38. * Add a Route instance to the collection.
  39. *
  40. * @param \Illuminate\Routing\Route $route
  41. * @return \Illuminate\Routing\Route
  42. */
  43. public function add(Route $route)
  44. {
  45. $this->addToCollections($route);
  46. $this->addLookups($route);
  47. return $route;
  48. }
  49. /**
  50. * Add the given route to the arrays of routes.
  51. *
  52. * @param \Illuminate\Routing\Route $route
  53. * @return void
  54. */
  55. protected function addToCollections($route)
  56. {
  57. $domainAndUri = $route->domain().$route->getUri();
  58. foreach ($route->methods() as $method) {
  59. $this->routes[$method][$domainAndUri] = $route;
  60. }
  61. $this->allRoutes[$method.$domainAndUri] = $route;
  62. }
  63. /**
  64. * Add the route to any look-up tables if necessary.
  65. *
  66. * @param \Illuminate\Routing\Route $route
  67. * @return void
  68. */
  69. protected function addLookups($route)
  70. {
  71. // If the route has a name, we will add it to the name look-up table so that we
  72. // will quickly be able to find any route associate with a name and not have
  73. // to iterate through every route every time we need to perform a look-up.
  74. $action = $route->getAction();
  75. if (isset($action['as'])) {
  76. $this->nameList[$action['as']] = $route;
  77. }
  78. // When the route is routing to a controller we will also store the action that
  79. // is used by the route. This will let us reverse route to controllers while
  80. // processing a request and easily generate URLs to the given controllers.
  81. if (isset($action['controller'])) {
  82. $this->addToActionList($action, $route);
  83. }
  84. }
  85. /**
  86. * Refresh the name look-up table.
  87. *
  88. * This is done in case any names are fluently defined.
  89. *
  90. * @return void
  91. */
  92. public function refreshNameLookups()
  93. {
  94. $this->nameList = [];
  95. foreach ($this->allRoutes as $route) {
  96. if ($route->getName()) {
  97. $this->nameList[$route->getName()] = $route;
  98. }
  99. }
  100. }
  101. /**
  102. * Add a route to the controller action dictionary.
  103. *
  104. * @param array $action
  105. * @param \Illuminate\Routing\Route $route
  106. * @return void
  107. */
  108. protected function addToActionList($action, $route)
  109. {
  110. $this->actionList[trim($action['controller'], '\\')] = $route;
  111. }
  112. /**
  113. * Find the first route matching a given request.
  114. *
  115. * @param \Illuminate\Http\Request $request
  116. * @return \Illuminate\Routing\Route
  117. *
  118. * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
  119. */
  120. public function match(Request $request)
  121. {
  122. $routes = $this->get($request->getMethod());
  123. // First, we will see if we can find a matching route for this current request
  124. // method. If we can, great, we can just return it so that it can be called
  125. // by the consumer. Otherwise we will check for routes with another verb.
  126. $route = $this->check($routes, $request);
  127. if (! is_null($route)) {
  128. return $route->bind($request);
  129. }
  130. // If no route was found we will now check if a matching route is specified by
  131. // another HTTP verb. If it is we will need to throw a MethodNotAllowed and
  132. // inform the user agent of which HTTP verb it should use for this route.
  133. $others = $this->checkForAlternateVerbs($request);
  134. if (count($others) > 0) {
  135. return $this->getRouteForMethods($request, $others);
  136. }
  137. throw new NotFoundHttpException;
  138. }
  139. /**
  140. * Determine if any routes match on another HTTP verb.
  141. *
  142. * @param \Illuminate\Http\Request $request
  143. * @return array
  144. */
  145. protected function checkForAlternateVerbs($request)
  146. {
  147. $methods = array_diff(Router::$verbs, [$request->getMethod()]);
  148. // Here we will spin through all verbs except for the current request verb and
  149. // check to see if any routes respond to them. If they do, we will return a
  150. // proper error response with the correct headers on the response string.
  151. $others = [];
  152. foreach ($methods as $method) {
  153. if (! is_null($this->check($this->get($method), $request, false))) {
  154. $others[] = $method;
  155. }
  156. }
  157. return $others;
  158. }
  159. /**
  160. * Get a route (if necessary) that responds when other available methods are present.
  161. *
  162. * @param \Illuminate\Http\Request $request
  163. * @param array $methods
  164. * @return \Illuminate\Routing\Route
  165. *
  166. * @throws \Symfony\Component\Routing\Exception\MethodNotAllowedHttpException
  167. */
  168. protected function getRouteForMethods($request, array $methods)
  169. {
  170. if ($request->method() == 'OPTIONS') {
  171. return (new Route('OPTIONS', $request->path(), function () use ($methods) {
  172. return new Response('', 200, ['Allow' => implode(',', $methods)]);
  173. }))->bind($request);
  174. }
  175. $this->methodNotAllowed($methods);
  176. }
  177. /**
  178. * Throw a method not allowed HTTP exception.
  179. *
  180. * @param array $others
  181. * @return void
  182. *
  183. * @throws \Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException
  184. */
  185. protected function methodNotAllowed(array $others)
  186. {
  187. throw new MethodNotAllowedHttpException($others);
  188. }
  189. /**
  190. * Determine if a route in the array matches the request.
  191. *
  192. * @param array $routes
  193. * @param \Illuminate\http\Request $request
  194. * @param bool $includingMethod
  195. * @return \Illuminate\Routing\Route|null
  196. */
  197. protected function check(array $routes, $request, $includingMethod = true)
  198. {
  199. return Arr::first($routes, function ($key, $value) use ($request, $includingMethod) {
  200. return $value->matches($request, $includingMethod);
  201. });
  202. }
  203. /**
  204. * Get all of the routes in the collection.
  205. *
  206. * @param string|null $method
  207. * @return array
  208. */
  209. protected function get($method = null)
  210. {
  211. if (is_null($method)) {
  212. return $this->getRoutes();
  213. }
  214. return Arr::get($this->routes, $method, []);
  215. }
  216. /**
  217. * Determine if the route collection contains a given named route.
  218. *
  219. * @param string $name
  220. * @return bool
  221. */
  222. public function hasNamedRoute($name)
  223. {
  224. return ! is_null($this->getByName($name));
  225. }
  226. /**
  227. * Get a route instance by its name.
  228. *
  229. * @param string $name
  230. * @return \Illuminate\Routing\Route|null
  231. */
  232. public function getByName($name)
  233. {
  234. return isset($this->nameList[$name]) ? $this->nameList[$name] : null;
  235. }
  236. /**
  237. * Get a route instance by its controller action.
  238. *
  239. * @param string $action
  240. * @return \Illuminate\Routing\Route|null
  241. */
  242. public function getByAction($action)
  243. {
  244. return isset($this->actionList[$action]) ? $this->actionList[$action] : null;
  245. }
  246. /**
  247. * Get all of the routes in the collection.
  248. *
  249. * @return array
  250. */
  251. public function getRoutes()
  252. {
  253. return array_values($this->allRoutes);
  254. }
  255. /**
  256. * Get an iterator for the items.
  257. *
  258. * @return \ArrayIterator
  259. */
  260. public function getIterator()
  261. {
  262. return new ArrayIterator($this->getRoutes());
  263. }
  264. /**
  265. * Count the number of items in the collection.
  266. *
  267. * @return int
  268. */
  269. public function count()
  270. {
  271. return count($this->getRoutes());
  272. }
  273. }