PageRenderTime 47ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 1ms

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

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