]> git.immae.eu Git - github/wallabag/wallabag.git/blob - vendor/symfony/routing/Symfony/Component/Routing/Matcher/UrlMatcher.php
db18ec4e7b25938afb893f654b403180fb75dae3
[github/wallabag/wallabag.git] / vendor / symfony / routing / Symfony / Component / Routing / Matcher / UrlMatcher.php
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\RouteCollection;
17 use Symfony\Component\Routing\RequestContext;
18 use Symfony\Component\Routing\Route;
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 const REQUIREMENT_MATCH = 0;
30 const REQUIREMENT_MISMATCH = 1;
31 const ROUTE_MATCH = 2;
32
33 /**
34 * @var RequestContext
35 */
36 protected $context;
37
38 /**
39 * @var array
40 */
41 protected $allow = array();
42
43 /**
44 * @var RouteCollection
45 */
46 protected $routes;
47
48 /**
49 * Constructor.
50 *
51 * @param RouteCollection $routes A RouteCollection instance
52 * @param RequestContext $context The context
53 *
54 * @api
55 */
56 public function __construct(RouteCollection $routes, RequestContext $context)
57 {
58 $this->routes = $routes;
59 $this->context = $context;
60 }
61
62 /**
63 * {@inheritdoc}
64 */
65 public function setContext(RequestContext $context)
66 {
67 $this->context = $context;
68 }
69
70 /**
71 * {@inheritdoc}
72 */
73 public function getContext()
74 {
75 return $this->context;
76 }
77
78 /**
79 * {@inheritdoc}
80 */
81 public function match($pathinfo)
82 {
83 $this->allow = array();
84
85 if ($ret = $this->matchCollection(rawurldecode($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 /**
95 * Tries to match a URL with a set of routes.
96 *
97 * @param string $pathinfo The path info to be parsed
98 * @param RouteCollection $routes The set of routes
99 *
100 * @return array An array of parameters
101 *
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
104 */
105 protected function matchCollection($pathinfo, RouteCollection $routes)
106 {
107 foreach ($routes as $name => $route) {
108 $compiledRoute = $route->compile();
109
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())) {
112 continue;
113 }
114
115 if (!preg_match($compiledRoute->getRegex(), $pathinfo, $matches)) {
116 continue;
117 }
118
119 $hostMatches = array();
120 if ($compiledRoute->getHostRegex() && !preg_match($compiledRoute->getHostRegex(), $this->context->getHost(), $hostMatches)) {
121 continue;
122 }
123
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()) {
128 $method = 'GET';
129 }
130
131 if (!in_array($method, $req = explode('|', strtoupper($req)))) {
132 $this->allow = array_merge($this->allow, $req);
133
134 continue;
135 }
136 }
137
138 $status = $this->handleRouteRequirements($pathinfo, $name, $route);
139
140 if (self::ROUTE_MATCH === $status[0]) {
141 return $status[1];
142 }
143
144 if (self::REQUIREMENT_MISMATCH === $status[0]) {
145 continue;
146 }
147
148 return $this->getAttributes($route, $name, array_replace($matches, $hostMatches));
149 }
150 }
151
152 /**
153 * Returns an array of values to use as request attributes.
154 *
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).
158 *
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
162 *
163 * @return array An array of parameters
164 */
165 protected function getAttributes(Route $route, $name, array $attributes)
166 {
167 $attributes['_route'] = $name;
168
169 return $this->mergeDefaults($attributes, $route->getDefaults());
170 }
171
172 /**
173 * Handles specific route requirements.
174 *
175 * @param string $pathinfo The path
176 * @param string $name The route name
177 * @param Route $route The route
178 *
179 * @return array The first element represents the status, the second contains additional information
180 */
181 protected function handleRouteRequirements($pathinfo, $name, Route $route)
182 {
183 // check HTTP scheme requirement
184 $scheme = $route->getRequirement('_scheme');
185 $status = $scheme && $scheme !== $this->context->getScheme() ? self::REQUIREMENT_MISMATCH : self::REQUIREMENT_MATCH;
186
187 return array($status, null);
188 }
189
190 /**
191 * Get merged default parameters.
192 *
193 * @param array $params The parameters
194 * @param array $defaults The defaults
195 *
196 * @return array Merged default parameters
197 */
198 protected function mergeDefaults($params, $defaults)
199 {
200 foreach ($params as $key => $value) {
201 if (!is_int($key)) {
202 $defaults[$key] = $value;
203 }
204 }
205
206 return $defaults;
207 }
208 }