/src/Illuminate/Routing/Route.php

https://bitbucket.org/mikebosire/framework · PHP · 451 lines · 183 code · 69 blank · 199 comment · 9 complexity · 678c9d2bf926cdcf33e011f36aeddfef MD5 · raw file

  1. <?php namespace Illuminate\Routing;
  2. use Illuminate\Http\Response;
  3. use Symfony\Component\HttpFoundation\Request;
  4. use Symfony\Component\Routing\Route as BaseRoute;
  5. class Route extends BaseRoute {
  6. /**
  7. * The router instance.
  8. *
  9. * @var \Illuminate\Routing\Router
  10. */
  11. protected $router;
  12. /**
  13. * The matching parameter array.
  14. *
  15. * @var array
  16. */
  17. protected $parameters;
  18. /**
  19. * The parsed parameter array.
  20. *
  21. * @var array
  22. */
  23. protected $parsedParameters;
  24. /**
  25. * Execute the route and return the response.
  26. *
  27. * @param Symfony\Component\HttpFoundation\Request $request
  28. * @return mixed
  29. */
  30. public function run(Request $request)
  31. {
  32. $this->parsedParameters = null;
  33. // We will only call the router callable if no "before" middlewares returned
  34. // a response. If they do, we will consider that the response to requests
  35. // so that the request "lifecycle" will be easily halted for filtering.
  36. $response = $this->callBeforeFilters($request);
  37. if ( ! isset($response))
  38. {
  39. $response = $this->callCallable();
  40. }
  41. // If the response is from a filter we want to note that so that we can skip
  42. // the "after" filters which should only run when the route method is run
  43. // for the incoming request. Otherwise only app level filters will run.
  44. else
  45. {
  46. $fromFilter = true;
  47. }
  48. $response = $this->router->prepare($response, $request);
  49. // Once we have the "prepared" response, we will iterate through every after
  50. // filter and call each of them with the request and the response so they
  51. // can perform any final work that needs to be done after a route call.
  52. if ( ! isset($fromFilter))
  53. {
  54. $this->callAfterFilters($request, $response);
  55. }
  56. return $response;
  57. }
  58. /**
  59. * Call the callable Closure attached to the route.
  60. *
  61. * @return mixed
  62. */
  63. protected function callCallable()
  64. {
  65. $variables = array_values($this->getParametersWithoutDefaults());
  66. return call_user_func_array($this->getOption('_call'), $variables);
  67. }
  68. /**
  69. * Call all of the before filters on the route.
  70. *
  71. * @param Symfony\Component\HttpFoundation\Request $request
  72. * @return mixed
  73. */
  74. protected function callBeforeFilters(Request $request)
  75. {
  76. $before = $this->getAllBeforeFilters($request);
  77. $response = null;
  78. // Once we have each middlewares, we will simply iterate through them and call
  79. // each one of them with the request. We will set the response variable to
  80. // whatever it may return so that it may override the request processes.
  81. foreach ($before as $filter)
  82. {
  83. $response = $this->callFilter($filter, $request);
  84. if ( ! is_null($response)) return $response;
  85. }
  86. }
  87. /**
  88. * Get all of the before filters to run on the route.
  89. *
  90. * @param Symfony\Component\HttpFoundation\Request $request
  91. * @return array
  92. */
  93. protected function getAllBeforeFilters(Request $request)
  94. {
  95. $before = $this->getBeforeFilters();
  96. return array_merge($before, $this->router->findPatternFilters($request));
  97. }
  98. /**
  99. * Call all of the "after" filters for a route.
  100. *
  101. * @param Symfony\Component\HttpFoundation\Request $request
  102. * @param Symfony\Component\HttpFoundation\Response $response
  103. * @return void
  104. */
  105. protected function callAfterFilters(Request $request, $response)
  106. {
  107. foreach ($this->getAfterFilters() as $filter)
  108. {
  109. $this->callFilter($filter, $request, array($response));
  110. }
  111. }
  112. /**
  113. * Call a given filter with the parameters.
  114. *
  115. * @param string $name
  116. * @param Symfony\Component\HttpFoundation\Request $request
  117. * @param array $params
  118. * @return mixed
  119. */
  120. public function callFilter($name, Request $request, array $params = array())
  121. {
  122. if ( ! $this->router->filtersEnabled()) return;
  123. $merge = array($this->router->getCurrentRoute(), $request);
  124. $params = array_merge($merge, $params);
  125. // Next we will parse the filter name to extract out any parameters and adding
  126. // any parameters specified in a filter name to the end of the lists of our
  127. // parameters, since the ones at the beginning are typically very static.
  128. list($name, $params) = $this->parseFilter($name, $params);
  129. if ( ! is_null($callable = $this->router->getFilter($name)))
  130. {
  131. return call_user_func_array($callable, $params);
  132. }
  133. }
  134. /**
  135. * Parse a filter name and add any parameters to the array.
  136. *
  137. * @param string $name
  138. * @param array $parameters
  139. * @return array
  140. */
  141. protected function parseFilter($name, $parameters = array())
  142. {
  143. if (str_contains($name, ':'))
  144. {
  145. // If the filter name contains a colon, we will assume that the developer
  146. // is passing along some parameters with the name, and we will explode
  147. // out the name and paramters, merging the parameters onto the list.
  148. $segments = explode(':', $name);
  149. $name = $segments[0];
  150. // We will merge the arguments specified in the filter name into the list
  151. // of existing parameters. We'll send them at the end since any values
  152. // at the front are usually static such as request, response, route.
  153. $arguments = explode(',', $segments[1]);
  154. $parameters = array_merge($parameters, $arguments);
  155. }
  156. return array($name, $parameters);
  157. }
  158. /**
  159. * Get a parameter by name from the route.
  160. *
  161. * @param string $name
  162. * @param mixed $default
  163. * @return string
  164. */
  165. public function getParameter($name, $default = null)
  166. {
  167. return array_get($this->getParameters(), $name, $default);
  168. }
  169. /**
  170. * Get the parameters to the callback.
  171. *
  172. * @return array
  173. */
  174. public function getParameters()
  175. {
  176. // If we have already parsed the parameters, we will just return the listing
  177. // the we already parsed, as some of these may have been resolved through
  178. // a binder that uses a database repository and shouldn't be run again.
  179. if (isset($this->parsedParameters))
  180. {
  181. return $this->parsedParameters;
  182. }
  183. $variables = $this->compile()->getVariables();
  184. // To get the parameter array, we need to spin the names of the variables on
  185. // the compiled route and match them to the parameters that we got when a
  186. // route is matched by the router, as routes instances don't have them.
  187. $parameters = array();
  188. foreach ($variables as $variable)
  189. {
  190. $parameters[$variable] = $this->resolveParameter($variable);
  191. }
  192. return $this->parsedParameters = $parameters;
  193. }
  194. /**
  195. * Resolve a parameter value for the route.
  196. *
  197. * @param string $key
  198. * @return mixed
  199. */
  200. protected function resolveParameter($key)
  201. {
  202. $value = $this->parameters[$key];
  203. // If the parameter has a binder, we will call the binder to resolve the real
  204. // value for the parameters. The binders could make a database call to get
  205. // a User object for example or may transform the input in some fashion.
  206. if ($this->router->hasBinder($key))
  207. {
  208. return $this->router->performBinding($key, $value, $this);
  209. }
  210. return $value;
  211. }
  212. /**
  213. * Get the route parameters without missing defaults.
  214. *
  215. * @return array
  216. */
  217. public function getParametersWithoutDefaults()
  218. {
  219. $parameters = $this->getParameters();
  220. foreach ($parameters as $key => $value)
  221. {
  222. // When calling functions using call_user_func_array, we don't want to write
  223. // over any existing default parameters, so we will remove every optional
  224. // parameter from the list that did not get a specified value on route.
  225. if ($this->isMissingDefault($key, $value))
  226. {
  227. unset($parameters[$key]);
  228. }
  229. }
  230. return $parameters;
  231. }
  232. /**
  233. * Determine if a route parameter is really a missing default.
  234. *
  235. * @param string $key
  236. * @param mixed $value
  237. * @return bool
  238. */
  239. protected function isMissingDefault($key, $value)
  240. {
  241. return $this->isOptional($key) and is_null($value);
  242. }
  243. /**
  244. * Determine if a given key is optional.
  245. *
  246. * @param string $key
  247. * @return bool
  248. */
  249. public function isOptional($key)
  250. {
  251. return array_key_exists($key, $this->getDefaults());
  252. }
  253. /**
  254. * Get the keys of the variables on the route.
  255. *
  256. * @return array
  257. */
  258. public function getParameterKeys()
  259. {
  260. return $this->compile()->getVariables();
  261. }
  262. /**
  263. * Force a given parameter to match a regular expression.
  264. *
  265. * @param string $name
  266. * @param string $expression
  267. * @return \Illuminate\Routing\Route
  268. */
  269. public function where($name, $expression)
  270. {
  271. $this->setRequirement($name, $expression);
  272. return $this;
  273. }
  274. /**
  275. * Set the default value for a parameter.
  276. *
  277. * @param string $key
  278. * @param mixed $value
  279. * @return \Illuminate\Routing\Route
  280. */
  281. public function defaults($key, $value)
  282. {
  283. $this->setDefault($key, $value);
  284. return $this;
  285. }
  286. /**
  287. * Set the before filters on the route.
  288. *
  289. * @param dynamic
  290. * @return \Illuminate\Routing\Route
  291. */
  292. public function before()
  293. {
  294. $current = $this->getBeforeFilters();
  295. $before = array_unique(array_merge($current, func_get_args()));
  296. $this->setOption('_before', $before);
  297. return $this;
  298. }
  299. /**
  300. * Set the after filters on the route.
  301. *
  302. * @param dynamic
  303. * @return \Illuminate\Routing\Route
  304. */
  305. public function after()
  306. {
  307. $current = $this->getAfterFilters();
  308. $after = array_unique(array_merge($current, func_get_args()));
  309. $this->setOption('_after', $after);
  310. return $this;
  311. }
  312. /**
  313. * Get the name of the action (if any) used by the route.
  314. *
  315. * @return string
  316. */
  317. public function getAction()
  318. {
  319. return $this->getOption('_uses');
  320. }
  321. /**
  322. * Get the before filters on the route.
  323. *
  324. * @return array
  325. */
  326. public function getBeforeFilters()
  327. {
  328. return $this->getOption('_before') ?: array();
  329. }
  330. /**
  331. * Set the before filters on the route.
  332. *
  333. * @param string $value
  334. * @return void
  335. */
  336. public function setBeforeFilters($value)
  337. {
  338. $filters = is_string($value) ? explode('|', $value) : (array) $value;
  339. $this->setOption('_before', $filters);
  340. }
  341. /**
  342. * Get the after filters on the route.
  343. *
  344. * @return array
  345. */
  346. public function getAfterFilters()
  347. {
  348. return $this->getOption('_after') ?: array();
  349. }
  350. /**
  351. * Set the after filters on the route.
  352. *
  353. * @param string $value
  354. * @return void
  355. */
  356. public function setAfterFilters($value)
  357. {
  358. $filters = is_string($value) ? explode('|', $value) : (array) $value;
  359. $this->setOption('_after', $filters);
  360. }
  361. /**
  362. * Set the matching parameter array on the route.
  363. *
  364. * @param array $parameters
  365. * @return void
  366. */
  367. public function setParameters($parameters)
  368. {
  369. $this->parameters = $parameters;
  370. }
  371. /**
  372. * Set the Router instance on the route.
  373. *
  374. * @param \Illuminate\Routing\Router $router
  375. * @return \Illuminate\Routing\Route
  376. */
  377. public function setRouter(Router $router)
  378. {
  379. $this->router = $router;
  380. return $this;
  381. }
  382. }