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