UrlMatcher.php 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\Routing\Matcher;
  11. use Symfony\Component\Routing\Exception\MethodNotAllowedException;
  12. use Symfony\Component\Routing\Exception\ResourceNotFoundException;
  13. use Symfony\Component\Routing\Route;
  14. use Symfony\Component\Routing\RouteCollection;
  15. use Symfony\Component\Routing\RequestContext;
  16. /**
  17. * UrlMatcher matches URL based on a set of routes.
  18. *
  19. * @author Fabien Potencier <fabien@symfony.com>
  20. *
  21. * @api
  22. */
  23. class UrlMatcher implements UrlMatcherInterface
  24. {
  25. protected $context;
  26. private $routes;
  27. /**
  28. * Constructor.
  29. *
  30. * @param RouteCollection $routes A RouteCollection instance
  31. * @param RequestContext $context The context
  32. *
  33. * @api
  34. */
  35. public function __construct(RouteCollection $routes, RequestContext $context)
  36. {
  37. $this->routes = $routes;
  38. $this->context = $context;
  39. }
  40. /**
  41. * Sets the request context.
  42. *
  43. * @param RequestContext $context The context
  44. *
  45. * @api
  46. */
  47. public function setContext(RequestContext $context)
  48. {
  49. $this->context = $context;
  50. }
  51. /**
  52. * Gets the request context.
  53. *
  54. * @return RequestContext The context
  55. */
  56. public function getContext()
  57. {
  58. return $this->context;
  59. }
  60. /**
  61. * Tries to match a URL with a set of routes.
  62. *
  63. * @param string $pathinfo The path info to be parsed
  64. *
  65. * @return array An array of parameters
  66. *
  67. * @throws ResourceNotFoundException If the resource could not be found
  68. * @throws MethodNotAllowedException If the resource was found but the request method is not allowed
  69. *
  70. * @api
  71. */
  72. public function match($pathinfo)
  73. {
  74. $this->allow = array();
  75. if ($ret = $this->matchCollection($pathinfo, $this->routes)) {
  76. return $ret;
  77. }
  78. throw 0 < count($this->allow)
  79. ? new MethodNotAllowedException(array_unique(array_map('strtoupper', $this->allow)))
  80. : new ResourceNotFoundException();
  81. }
  82. protected function matchCollection($pathinfo, RouteCollection $routes)
  83. {
  84. $pathinfo = urldecode($pathinfo);
  85. foreach ($routes as $name => $route) {
  86. if ($route instanceof RouteCollection) {
  87. if (false === strpos($route->getPrefix(), '{') && $route->getPrefix() !== substr($pathinfo, 0, strlen($route->getPrefix()))) {
  88. continue;
  89. }
  90. if (!$ret = $this->matchCollection($pathinfo, $route)) {
  91. continue;
  92. }
  93. return $ret;
  94. }
  95. $compiledRoute = $route->compile();
  96. // check the static prefix of the URL first. Only use the more expensive preg_match when it matches
  97. if ('' !== $compiledRoute->getStaticPrefix() && 0 !== strpos($pathinfo, $compiledRoute->getStaticPrefix())) {
  98. continue;
  99. }
  100. if (!preg_match($compiledRoute->getRegex(), $pathinfo, $matches)) {
  101. continue;
  102. }
  103. // check HTTP method requirement
  104. if ($req = $route->getRequirement('_method')) {
  105. // HEAD and GET are equivalent as per RFC
  106. if ('HEAD' === $method = $this->context->getMethod()) {
  107. $method = 'GET';
  108. }
  109. if (!in_array($method, $req = explode('|', strtoupper($req)))) {
  110. $this->allow = array_merge($this->allow, $req);
  111. continue;
  112. }
  113. }
  114. return array_merge($this->mergeDefaults($matches, $route->getDefaults()), array('_route' => $name));
  115. }
  116. }
  117. protected function mergeDefaults($params, $defaults)
  118. {
  119. $parameters = $defaults;
  120. foreach ($params as $key => $value) {
  121. if (!is_int($key)) {
  122. $parameters[$key] = rawurldecode($value);
  123. }
  124. }
  125. return $parameters;
  126. }
  127. }