4 * This file is part of the Symfony package.
6 * (c) Fabien Potencier <fabien@symfony.com>
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
12 namespace Symfony\Component\Routing\Matcher
;
14 use Symfony\Component\Routing\Exception\MethodNotAllowedException
;
15 use Symfony\Component\Routing\Exception\ResourceNotFoundException
;
16 use Symfony\Component\Routing\RouteCollection
;
17 use Symfony\Component\Routing\RequestContext
;
18 use Symfony\Component\Routing\Route
;
21 * UrlMatcher matches URL based on a set of routes.
23 * @author Fabien Potencier <fabien@symfony.com>
27 class UrlMatcher
implements UrlMatcherInterface
29 const REQUIREMENT_MATCH
= 0;
30 const REQUIREMENT_MISMATCH
= 1;
31 const ROUTE_MATCH
= 2;
41 protected $allow = array();
44 * @var RouteCollection
51 * @param RouteCollection $routes A RouteCollection instance
52 * @param RequestContext $context The context
56 public function __construct(RouteCollection
$routes, RequestContext
$context)
58 $this->routes
= $routes;
59 $this->context
= $context;
65 public function setContext(RequestContext
$context)
67 $this->context
= $context;
73 public function getContext()
75 return $this->context
;
81 public function match($pathinfo)
83 $this->allow
= array();
85 if ($ret = $this->matchCollection(rawurldecode($pathinfo), $this->routes
)) {
89 throw 0 < count($this->allow
)
90 ? new MethodNotAllowedException(array_unique(array_map('strtoupper', $this->allow
)))
91 : new ResourceNotFoundException();
95 * Tries to match a URL with a set of routes.
97 * @param string $pathinfo The path info to be parsed
98 * @param RouteCollection $routes The set of routes
100 * @return array An array of parameters
102 * @throws ResourceNotFoundException If the resource could not be found
103 * @throws MethodNotAllowedException If the resource was found but the request method is not allowed
105 protected function matchCollection($pathinfo, RouteCollection
$routes)
107 foreach ($routes as $name => $route) {
108 $compiledRoute = $route->compile();
110 // check the static prefix of the URL first. Only use the more expensive preg_match when it matches
111 if ('' !== $compiledRoute->getStaticPrefix() && 0 !== strpos($pathinfo, $compiledRoute->getStaticPrefix())) {
115 if (!preg_match($compiledRoute->getRegex(), $pathinfo, $matches)) {
119 $hostMatches = array();
120 if ($compiledRoute->getHostRegex() && !preg_match($compiledRoute->getHostRegex(), $this->context
->getHost(), $hostMatches)) {
124 // check HTTP method requirement
125 if ($req = $route->getRequirement('_method')) {
126 // HEAD and GET are equivalent as per RFC
127 if ('HEAD' === $method = $this->context
->getMethod()) {
131 if (!in_array($method, $req = explode('|', strtoupper($req)))) {
132 $this->allow
= array_merge($this->allow
, $req);
138 $status = $this->handleRouteRequirements($pathinfo, $name, $route);
140 if (self
::ROUTE_MATCH
=== $status[0]) {
144 if (self
::REQUIREMENT_MISMATCH
=== $status[0]) {
148 return $this->getAttributes($route, $name, array_replace($matches, $hostMatches));
153 * Returns an array of values to use as request attributes.
155 * As this method requires the Route object, it is not available
156 * in matchers that do not have access to the matched Route instance
157 * (like the PHP and Apache matcher dumpers).
159 * @param Route $route The route we are matching against
160 * @param string $name The name of the route
161 * @param array $attributes An array of attributes from the matcher
163 * @return array An array of parameters
165 protected function getAttributes(Route
$route, $name, array $attributes)
167 $attributes['_route'] = $name;
169 return $this->mergeDefaults($attributes, $route->getDefaults());
173 * Handles specific route requirements.
175 * @param string $pathinfo The path
176 * @param string $name The route name
177 * @param Route $route The route
179 * @return array The first element represents the status, the second contains additional information
181 protected function handleRouteRequirements($pathinfo, $name, Route
$route)
183 // check HTTP scheme requirement
184 $scheme = $route->getRequirement('_scheme');
185 $status = $scheme && $scheme !== $this->context
->getScheme() ? self
::REQUIREMENT_MISMATCH
: self
::REQUIREMENT_MATCH
;
187 return array($status, null);
191 * Get merged default parameters.
193 * @param array $params The parameters
194 * @param array $defaults The defaults
196 * @return array Merged default parameters
198 protected function mergeDefaults($params, $defaults)
200 foreach ($params as $key => $value) {
202 $defaults[$key] = $value;