PageRenderTime 25ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

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

https://gitlab.com/xolotsoft/pumasruiz
PHP | 814 lines | 335 code | 112 blank | 367 comment | 19 complexity | 6d159e734deb2b1e4f13e42a4e795411 MD5 | raw file
  1. <?php namespace Illuminate\Routing;
  2. use Illuminate\Http\Request;
  3. use Illuminate\Routing\Matching\UriValidator;
  4. use Illuminate\Routing\Matching\HostValidator;
  5. use Illuminate\Routing\Matching\MethodValidator;
  6. use Illuminate\Routing\Matching\SchemeValidator;
  7. use Symfony\Component\Routing\Route as SymfonyRoute;
  8. class Route {
  9. /**
  10. * The URI pattern the route responds to.
  11. *
  12. * @var string
  13. */
  14. protected $uri;
  15. /**
  16. * The HTTP methods the route responds to.
  17. *
  18. * @var array
  19. */
  20. protected $methods;
  21. /**
  22. * The route action array.
  23. *
  24. * @var array
  25. */
  26. protected $action;
  27. /**
  28. * The default values for the route.
  29. *
  30. * @var array
  31. */
  32. protected $defaults = array();
  33. /**
  34. * The regular expression requirements.
  35. *
  36. * @var array
  37. */
  38. protected $wheres = array();
  39. /**
  40. * The array of matched parameters.
  41. *
  42. * @var array
  43. */
  44. protected $parameters;
  45. /**
  46. * The parameter names for the route.
  47. *
  48. * @var array|null
  49. */
  50. protected $parameterNames;
  51. /**
  52. * The compiled version of the route.
  53. *
  54. * @var \Symfony\Component\Routing\CompiledRoute
  55. */
  56. protected $compiled;
  57. /**
  58. * The validators used by the routes.
  59. *
  60. * @var array
  61. */
  62. protected static $validators;
  63. /**
  64. * Create a new Route instance.
  65. *
  66. * @param array $methods
  67. * @param string $uri
  68. * @param \Closure|array $action
  69. * @return void
  70. */
  71. public function __construct($methods, $uri, $action)
  72. {
  73. $this->uri = $uri;
  74. $this->methods = (array) $methods;
  75. $this->action = $this->parseAction($action);
  76. if (in_array('GET', $this->methods) && ! in_array('HEAD', $this->methods))
  77. {
  78. $this->methods[] = 'HEAD';
  79. }
  80. if (isset($this->action['prefix']))
  81. {
  82. $this->prefix($this->action['prefix']);
  83. }
  84. }
  85. /**
  86. * Run the route action and return the response.
  87. *
  88. * @return mixed
  89. */
  90. public function run()
  91. {
  92. $parameters = array_filter($this->parameters(), function($p) { return isset($p); });
  93. return call_user_func_array($this->action['uses'], $parameters);
  94. }
  95. /**
  96. * Determine if the route matches given request.
  97. *
  98. * @param \Illuminate\Http\Request $request
  99. * @param bool $includingMethod
  100. * @return bool
  101. */
  102. public function matches(Request $request, $includingMethod = true)
  103. {
  104. $this->compileRoute();
  105. foreach ($this->getValidators() as $validator)
  106. {
  107. if ( ! $includingMethod && $validator instanceof MethodValidator) continue;
  108. if ( ! $validator->matches($this, $request)) return false;
  109. }
  110. return true;
  111. }
  112. /**
  113. * Compile the route into a Symfony CompiledRoute instance.
  114. *
  115. * @return void
  116. */
  117. protected function compileRoute()
  118. {
  119. $optionals = $this->extractOptionalParameters();
  120. $uri = preg_replace('/\{(\w+?)\?\}/', '{$1}', $this->uri);
  121. $this->compiled = with(
  122. new SymfonyRoute($uri, $optionals, $this->wheres, array(), $this->domain() ?: '')
  123. )->compile();
  124. }
  125. /**
  126. * Get the optional parameters for the route.
  127. *
  128. * @return array
  129. */
  130. protected function extractOptionalParameters()
  131. {
  132. preg_match_all('/\{(\w+?)\?\}/', $this->uri, $matches);
  133. return isset($matches[1]) ? array_fill_keys($matches[1], null) : [];
  134. }
  135. /**
  136. * Get the "before" filters for the route.
  137. *
  138. * @return array
  139. */
  140. public function beforeFilters()
  141. {
  142. if ( ! isset($this->action['before'])) return array();
  143. return $this->parseFilters($this->action['before']);
  144. }
  145. /**
  146. * Get the "after" filters for the route.
  147. *
  148. * @return array
  149. */
  150. public function afterFilters()
  151. {
  152. if ( ! isset($this->action['after'])) return array();
  153. return $this->parseFilters($this->action['after']);
  154. }
  155. /**
  156. * Parse the given filter string.
  157. *
  158. * @param string $filters
  159. * @return array
  160. */
  161. public static function parseFilters($filters)
  162. {
  163. return array_build(static::explodeFilters($filters), function($key, $value)
  164. {
  165. return Route::parseFilter($value);
  166. });
  167. }
  168. /**
  169. * Turn the filters into an array if they aren't already.
  170. *
  171. * @param array|string $filters
  172. * @return array
  173. */
  174. protected static function explodeFilters($filters)
  175. {
  176. if (is_array($filters)) return static::explodeArrayFilters($filters);
  177. return array_map('trim', explode('|', $filters));
  178. }
  179. /**
  180. * Flatten out an array of filter declarations.
  181. *
  182. * @param array $filters
  183. * @return array
  184. */
  185. protected static function explodeArrayFilters(array $filters)
  186. {
  187. $results = array();
  188. foreach ($filters as $filter)
  189. {
  190. $results = array_merge($results, array_map('trim', explode('|', $filter)));
  191. }
  192. return $results;
  193. }
  194. /**
  195. * Parse the given filter into name and parameters.
  196. *
  197. * @param string $filter
  198. * @return array
  199. */
  200. public static function parseFilter($filter)
  201. {
  202. if ( ! str_contains($filter, ':')) return array($filter, array());
  203. return static::parseParameterFilter($filter);
  204. }
  205. /**
  206. * Parse a filter with parameters.
  207. *
  208. * @param string $filter
  209. * @return array
  210. */
  211. protected static function parseParameterFilter($filter)
  212. {
  213. list($name, $parameters) = explode(':', $filter, 2);
  214. return array($name, explode(',', $parameters));
  215. }
  216. /**
  217. * Get a given parameter from the route.
  218. *
  219. * @param string $name
  220. * @param mixed $default
  221. * @return string
  222. */
  223. public function getParameter($name, $default = null)
  224. {
  225. return $this->parameter($name, $default);
  226. }
  227. /**
  228. * Get a given parameter from the route.
  229. *
  230. * @param string $name
  231. * @param mixed $default
  232. * @return string
  233. */
  234. public function parameter($name, $default = null)
  235. {
  236. return array_get($this->parameters(), $name, $default);
  237. }
  238. /**
  239. * Set a parameter to the given value.
  240. *
  241. * @param string $name
  242. * @param mixed $value
  243. * @return void
  244. */
  245. public function setParameter($name, $value)
  246. {
  247. $this->parameters();
  248. $this->parameters[$name] = $value;
  249. }
  250. /**
  251. * Unset a parameter on the route if it is set.
  252. *
  253. * @param string $name
  254. * @return void
  255. */
  256. public function forgetParameter($name)
  257. {
  258. $this->parameters();
  259. unset($this->parameters[$name]);
  260. }
  261. /**
  262. * Get the key / value list of parameters for the route.
  263. *
  264. * @return array
  265. *
  266. * @throws \LogicException
  267. */
  268. public function parameters()
  269. {
  270. if (isset($this->parameters))
  271. {
  272. return array_map(function($value)
  273. {
  274. return is_string($value) ? rawurldecode($value) : $value;
  275. }, $this->parameters);
  276. }
  277. throw new \LogicException("Route is not bound.");
  278. }
  279. /**
  280. * Get the key / value list of parameters without null values.
  281. *
  282. * @return array
  283. */
  284. public function parametersWithoutNulls()
  285. {
  286. return array_filter($this->parameters(), function($p) { return ! is_null($p); });
  287. }
  288. /**
  289. * Get all of the parameter names for the route.
  290. *
  291. * @return array
  292. */
  293. public function parameterNames()
  294. {
  295. if (isset($this->parameterNames)) return $this->parameterNames;
  296. return $this->parameterNames = $this->compileParameterNames();
  297. }
  298. /**
  299. * Get the parameter names for the route.
  300. *
  301. * @return array
  302. */
  303. protected function compileParameterNames()
  304. {
  305. preg_match_all('/\{(.*?)\}/', $this->domain().$this->uri, $matches);
  306. return array_map(function($m) { return trim($m, '?'); }, $matches[1]);
  307. }
  308. /**
  309. * Bind the route to a given request for execution.
  310. *
  311. * @param \Illuminate\Http\Request $request
  312. * @return $this
  313. */
  314. public function bind(Request $request)
  315. {
  316. $this->compileRoute();
  317. $this->bindParameters($request);
  318. return $this;
  319. }
  320. /**
  321. * Extract the parameter list from the request.
  322. *
  323. * @param \Illuminate\Http\Request $request
  324. * @return array
  325. */
  326. public function bindParameters(Request $request)
  327. {
  328. // If the route has a regular expression for the host part of the URI, we will
  329. // compile that and get the parameter matches for this domain. We will then
  330. // merge them into this parameters array so that this array is completed.
  331. $params = $this->matchToKeys(
  332. array_slice($this->bindPathParameters($request), 1)
  333. );
  334. // If the route has a regular expression for the host part of the URI, we will
  335. // compile that and get the parameter matches for this domain. We will then
  336. // merge them into this parameters array so that this array is completed.
  337. if ( ! is_null($this->compiled->getHostRegex()))
  338. {
  339. $params = $this->bindHostParameters(
  340. $request, $params
  341. );
  342. }
  343. return $this->parameters = $this->replaceDefaults($params);
  344. }
  345. /**
  346. * Get the parameter matches for the path portion of the URI.
  347. *
  348. * @param \Illuminate\Http\Request $request
  349. * @return array
  350. */
  351. protected function bindPathParameters(Request $request)
  352. {
  353. preg_match($this->compiled->getRegex(), '/'.$request->decodedPath(), $matches);
  354. return $matches;
  355. }
  356. /**
  357. * Extract the parameter list from the host part of the request.
  358. *
  359. * @param \Illuminate\Http\Request $request
  360. * @param array $parameters
  361. * @return array
  362. */
  363. protected function bindHostParameters(Request $request, $parameters)
  364. {
  365. preg_match($this->compiled->getHostRegex(), $request->getHost(), $matches);
  366. return array_merge($this->matchToKeys(array_slice($matches, 1)), $parameters);
  367. }
  368. /**
  369. * Combine a set of parameter matches with the route's keys.
  370. *
  371. * @param array $matches
  372. * @return array
  373. */
  374. protected function matchToKeys(array $matches)
  375. {
  376. if (count($this->parameterNames()) == 0) return array();
  377. $parameters = array_intersect_key($matches, array_flip($this->parameterNames()));
  378. return array_filter($parameters, function($value)
  379. {
  380. return is_string($value) && strlen($value) > 0;
  381. });
  382. }
  383. /**
  384. * Replace null parameters with their defaults.
  385. *
  386. * @param array $parameters
  387. * @return array
  388. */
  389. protected function replaceDefaults(array $parameters)
  390. {
  391. foreach ($parameters as $key => &$value)
  392. {
  393. $value = isset($value) ? $value : array_get($this->defaults, $key);
  394. }
  395. return $parameters;
  396. }
  397. /**
  398. * Parse the route action into a standard array.
  399. *
  400. * @param callable|array $action
  401. * @return array
  402. */
  403. protected function parseAction($action)
  404. {
  405. // If the action is already a Closure instance, we will just set that instance
  406. // as the "uses" property, because there is nothing else we need to do when
  407. // it is available. Otherwise we will need to find it in the action list.
  408. if (is_callable($action))
  409. {
  410. return array('uses' => $action);
  411. }
  412. // If no "uses" property has been set, we will dig through the array to find a
  413. // Closure instance within this list. We will set the first Closure we come
  414. // across into the "uses" property that will get fired off by this route.
  415. elseif ( ! isset($action['uses']))
  416. {
  417. $action['uses'] = $this->findClosure($action);
  418. }
  419. return $action;
  420. }
  421. /**
  422. * Find the Closure in an action array.
  423. *
  424. * @param array $action
  425. * @return \Closure
  426. */
  427. protected function findClosure(array $action)
  428. {
  429. return array_first($action, function($key, $value)
  430. {
  431. return is_callable($value);
  432. });
  433. }
  434. /**
  435. * Get the route validators for the instance.
  436. *
  437. * @return array
  438. */
  439. public static function getValidators()
  440. {
  441. if (isset(static::$validators)) return static::$validators;
  442. // To match the route, we will use a chain of responsibility pattern with the
  443. // validator implementations. We will spin through each one making sure it
  444. // passes and then we will know if the route as a whole matches request.
  445. return static::$validators = array(
  446. new MethodValidator, new SchemeValidator,
  447. new HostValidator, new UriValidator,
  448. );
  449. }
  450. /**
  451. * Add before filters to the route.
  452. *
  453. * @param string $filters
  454. * @return $this
  455. */
  456. public function before($filters)
  457. {
  458. return $this->addFilters('before', $filters);
  459. }
  460. /**
  461. * Add after filters to the route.
  462. *
  463. * @param string $filters
  464. * @return $this
  465. */
  466. public function after($filters)
  467. {
  468. return $this->addFilters('after', $filters);
  469. }
  470. /**
  471. * Add the given filters to the route by type.
  472. *
  473. * @param string $type
  474. * @param string $filters
  475. * @return $this
  476. */
  477. protected function addFilters($type, $filters)
  478. {
  479. $filters = static::explodeFilters($filters);
  480. if (isset($this->action[$type]))
  481. {
  482. $existing = static::explodeFilters($this->action[$type]);
  483. $this->action[$type] = array_merge($existing, $filters);
  484. }
  485. else
  486. {
  487. $this->action[$type] = $filters;
  488. }
  489. return $this;
  490. }
  491. /**
  492. * Set a default value for the route.
  493. *
  494. * @param string $key
  495. * @param mixed $value
  496. * @return $this
  497. */
  498. public function defaults($key, $value)
  499. {
  500. $this->defaults[$key] = $value;
  501. return $this;
  502. }
  503. /**
  504. * Set a regular expression requirement on the route.
  505. *
  506. * @param array|string $name
  507. * @param string $expression
  508. * @return $this
  509. */
  510. public function where($name, $expression = null)
  511. {
  512. foreach ($this->parseWhere($name, $expression) as $name => $expression)
  513. {
  514. $this->wheres[$name] = $expression;
  515. }
  516. return $this;
  517. }
  518. /**
  519. * Parse arguments to the where method into an array.
  520. *
  521. * @param array|string $name
  522. * @param string $expression
  523. * @return array
  524. */
  525. protected function parseWhere($name, $expression)
  526. {
  527. return is_array($name) ? $name : array($name => $expression);
  528. }
  529. /**
  530. * Set a list of regular expression requirements on the route.
  531. *
  532. * @param array $wheres
  533. * @return $this
  534. */
  535. protected function whereArray(array $wheres)
  536. {
  537. foreach ($wheres as $name => $expression)
  538. {
  539. $this->where($name, $expression);
  540. }
  541. return $this;
  542. }
  543. /**
  544. * Add a prefix to the route URI.
  545. *
  546. * @param string $prefix
  547. * @return $this
  548. */
  549. public function prefix($prefix)
  550. {
  551. $this->uri = trim($prefix, '/').'/'.trim($this->uri, '/');
  552. return $this;
  553. }
  554. /**
  555. * Get the URI associated with the route.
  556. *
  557. * @return string
  558. */
  559. public function getPath()
  560. {
  561. return $this->uri();
  562. }
  563. /**
  564. * Get the URI associated with the route.
  565. *
  566. * @return string
  567. */
  568. public function uri()
  569. {
  570. return $this->uri;
  571. }
  572. /**
  573. * Get the HTTP verbs the route responds to.
  574. *
  575. * @return array
  576. */
  577. public function getMethods()
  578. {
  579. return $this->methods();
  580. }
  581. /**
  582. * Get the HTTP verbs the route responds to.
  583. *
  584. * @return array
  585. */
  586. public function methods()
  587. {
  588. return $this->methods;
  589. }
  590. /**
  591. * Determine if the route only responds to HTTP requests.
  592. *
  593. * @return bool
  594. */
  595. public function httpOnly()
  596. {
  597. return in_array('http', $this->action, true);
  598. }
  599. /**
  600. * Determine if the route only responds to HTTPS requests.
  601. *
  602. * @return bool
  603. */
  604. public function httpsOnly()
  605. {
  606. return $this->secure();
  607. }
  608. /**
  609. * Determine if the route only responds to HTTPS requests.
  610. *
  611. * @return bool
  612. */
  613. public function secure()
  614. {
  615. return in_array('https', $this->action, true);
  616. }
  617. /**
  618. * Get the domain defined for the route.
  619. *
  620. * @return string|null
  621. */
  622. public function domain()
  623. {
  624. return isset($this->action['domain']) ? $this->action['domain'] : null;
  625. }
  626. /**
  627. * Get the URI that the route responds to.
  628. *
  629. * @return string
  630. */
  631. public function getUri()
  632. {
  633. return $this->uri;
  634. }
  635. /**
  636. * Set the URI that the route responds to.
  637. *
  638. * @param string $uri
  639. * @return \Illuminate\Routing\Route
  640. */
  641. public function setUri($uri)
  642. {
  643. $this->uri = $uri;
  644. return $this;
  645. }
  646. /**
  647. * Get the prefix of the route instance.
  648. *
  649. * @return string
  650. */
  651. public function getPrefix()
  652. {
  653. return isset($this->action['prefix']) ? $this->action['prefix'] : null;
  654. }
  655. /**
  656. * Get the name of the route instance.
  657. *
  658. * @return string
  659. */
  660. public function getName()
  661. {
  662. return isset($this->action['as']) ? $this->action['as'] : null;
  663. }
  664. /**
  665. * Get the action name for the route.
  666. *
  667. * @return string
  668. */
  669. public function getActionName()
  670. {
  671. return isset($this->action['controller']) ? $this->action['controller'] : 'Closure';
  672. }
  673. /**
  674. * Get the action array for the route.
  675. *
  676. * @return array
  677. */
  678. public function getAction()
  679. {
  680. return $this->action;
  681. }
  682. /**
  683. * Set the action array for the route.
  684. *
  685. * @param array $action
  686. * @return $this
  687. */
  688. public function setAction(array $action)
  689. {
  690. $this->action = $action;
  691. return $this;
  692. }
  693. /**
  694. * Get the compiled version of the route.
  695. *
  696. * @return \Symfony\Component\Routing\CompiledRoute
  697. */
  698. public function getCompiled()
  699. {
  700. return $this->compiled;
  701. }
  702. }