comparison vendor/symfony/routing/Matcher/Dumper/PhpMatcherDumper.php @ 14:1fec387a4317

Update Drupal core to 8.5.2 via Composer
author Chris Cannam
date Mon, 23 Apr 2018 09:46:53 +0100
parents 4c8ae668cc8c
children 129ea1e6d783
comparison
equal deleted inserted replaced
13:5fb285c0d0e3 14:1fec387a4317
61 use Symfony\Component\Routing\Exception\MethodNotAllowedException; 61 use Symfony\Component\Routing\Exception\MethodNotAllowedException;
62 use Symfony\Component\Routing\Exception\ResourceNotFoundException; 62 use Symfony\Component\Routing\Exception\ResourceNotFoundException;
63 use Symfony\Component\Routing\RequestContext; 63 use Symfony\Component\Routing\RequestContext;
64 64
65 /** 65 /**
66 * {$options['class']}.
67 *
68 * This class has been auto-generated 66 * This class has been auto-generated
69 * by the Symfony Routing Component. 67 * by the Symfony Routing Component.
70 */ 68 */
71 class {$options['class']} extends {$options['base_class']} 69 class {$options['class']} extends {$options['base_class']}
72 { 70 {
73 /**
74 * Constructor.
75 */
76 public function __construct(RequestContext \$context) 71 public function __construct(RequestContext \$context)
77 { 72 {
78 \$this->context = \$context; 73 \$this->context = \$context;
79 } 74 }
80 75
99 private function generateMatchMethod($supportsRedirections) 94 private function generateMatchMethod($supportsRedirections)
100 { 95 {
101 $code = rtrim($this->compileRoutes($this->getRoutes(), $supportsRedirections), "\n"); 96 $code = rtrim($this->compileRoutes($this->getRoutes(), $supportsRedirections), "\n");
102 97
103 return <<<EOF 98 return <<<EOF
104 public function match(\$pathinfo) 99 public function match(\$rawPathinfo)
105 { 100 {
106 \$allow = array(); 101 \$allow = array();
107 \$pathinfo = rawurldecode(\$pathinfo); 102 \$pathinfo = rawurldecode(\$rawPathinfo);
103 \$trimmedPathinfo = rtrim(\$pathinfo, '/');
108 \$context = \$this->context; 104 \$context = \$this->context;
109 \$request = \$this->request; 105 \$request = \$this->request ?: \$this->createRequest(\$pathinfo);
106 \$requestMethod = \$canonicalMethod = \$context->getMethod();
107
108 if ('HEAD' === \$requestMethod) {
109 \$canonicalMethod = 'GET';
110 }
110 111
111 $code 112 $code
112 113
113 throw 0 < count(\$allow) ? new MethodNotAllowedException(array_unique(\$allow)) : new ResourceNotFoundException(); 114 throw 0 < count(\$allow) ? new MethodNotAllowedException(array_unique(\$allow)) : new ResourceNotFoundException();
114 } 115 }
124 * @return string PHP code 125 * @return string PHP code
125 */ 126 */
126 private function compileRoutes(RouteCollection $routes, $supportsRedirections) 127 private function compileRoutes(RouteCollection $routes, $supportsRedirections)
127 { 128 {
128 $fetchedHost = false; 129 $fetchedHost = false;
129
130 $groups = $this->groupRoutesByHostRegex($routes); 130 $groups = $this->groupRoutesByHostRegex($routes);
131 $code = ''; 131 $code = '';
132 132
133 foreach ($groups as $collection) { 133 foreach ($groups as $collection) {
134 if (null !== $regex = $collection->getAttribute('host_regex')) { 134 if (null !== $regex = $collection->getAttribute('host_regex')) {
135 if (!$fetchedHost) { 135 if (!$fetchedHost) {
136 $code .= " \$host = \$this->context->getHost();\n\n"; 136 $code .= " \$host = \$context->getHost();\n\n";
137 $fetchedHost = true; 137 $fetchedHost = true;
138 } 138 }
139 139
140 $code .= sprintf(" if (preg_match(%s, \$host, \$hostMatches)) {\n", var_export($regex, true)); 140 $code .= sprintf(" if (preg_match(%s, \$host, \$hostMatches)) {\n", var_export($regex, true));
141 } 141 }
142 142
143 $tree = $this->buildPrefixTree($collection); 143 $tree = $this->buildStaticPrefixCollection($collection);
144 $groupCode = $this->compilePrefixRoutes($tree, $supportsRedirections); 144 $groupCode = $this->compileStaticPrefixRoutes($tree, $supportsRedirections);
145 145
146 if (null !== $regex) { 146 if (null !== $regex) {
147 // apply extra indention at each line (except empty ones) 147 // apply extra indention at each line (except empty ones)
148 $groupCode = preg_replace('/^.{2,}$/m', ' $0', $groupCode); 148 $groupCode = preg_replace('/^.{2,}$/m', ' $0', $groupCode);
149 $code .= $groupCode; 149 $code .= $groupCode;
151 } else { 151 } else {
152 $code .= $groupCode; 152 $code .= $groupCode;
153 } 153 }
154 } 154 }
155 155
156 // used to display the Welcome Page in apps that don't define a homepage
157 $code .= " if ('/' === \$pathinfo && !\$allow) {\n";
158 $code .= " throw new Symfony\Component\Routing\Exception\NoConfigurationException();\n";
159 $code .= " }\n";
160
156 return $code; 161 return $code;
157 } 162 }
158 163
159 /** 164 private function buildStaticPrefixCollection(DumperCollection $collection)
160 * Generates PHP code recursively to match a tree of routes. 165 {
161 * 166 $prefixCollection = new StaticPrefixCollection();
162 * @param DumperPrefixCollection $collection A DumperPrefixCollection instance 167
168 foreach ($collection as $dumperRoute) {
169 $prefix = $dumperRoute->getRoute()->compile()->getStaticPrefix();
170 $prefixCollection->addRoute($prefix, $dumperRoute);
171 }
172
173 $prefixCollection->optimizeGroups();
174
175 return $prefixCollection;
176 }
177
178 /**
179 * Generates PHP code to match a tree of routes.
180 *
181 * @param StaticPrefixCollection $collection A StaticPrefixCollection instance
163 * @param bool $supportsRedirections Whether redirections are supported by the base class 182 * @param bool $supportsRedirections Whether redirections are supported by the base class
164 * @param string $parentPrefix Prefix of the parent collection 183 * @param string $ifOrElseIf either "if" or "elseif" to influence chaining
165 * 184 *
166 * @return string PHP code 185 * @return string PHP code
167 */ 186 */
168 private function compilePrefixRoutes(DumperPrefixCollection $collection, $supportsRedirections, $parentPrefix = '') 187 private function compileStaticPrefixRoutes(StaticPrefixCollection $collection, $supportsRedirections, $ifOrElseIf = 'if')
169 { 188 {
170 $code = ''; 189 $code = '';
171 $prefix = $collection->getPrefix(); 190 $prefix = $collection->getPrefix();
172 $optimizable = 1 < strlen($prefix) && 1 < count($collection->all()); 191
173 $optimizedPrefix = $parentPrefix; 192 if (!empty($prefix) && '/' !== $prefix) {
174 193 $code .= sprintf(" %s (0 === strpos(\$pathinfo, %s)) {\n", $ifOrElseIf, var_export($prefix, true));
175 if ($optimizable) { 194 }
176 $optimizedPrefix = $prefix; 195
177 196 $ifOrElseIf = 'if';
178 $code .= sprintf(" if (0 === strpos(\$pathinfo, %s)) {\n", var_export($prefix, true)); 197
179 } 198 foreach ($collection->getItems() as $route) {
180 199 if ($route instanceof StaticPrefixCollection) {
181 foreach ($collection as $route) { 200 $code .= $this->compileStaticPrefixRoutes($route, $supportsRedirections, $ifOrElseIf);
182 if ($route instanceof DumperCollection) { 201 $ifOrElseIf = 'elseif';
183 $code .= $this->compilePrefixRoutes($route, $supportsRedirections, $optimizedPrefix);
184 } else { 202 } else {
185 $code .= $this->compileRoute($route->getRoute(), $route->getName(), $supportsRedirections, $optimizedPrefix)."\n"; 203 $code .= $this->compileRoute($route[1]->getRoute(), $route[1]->getName(), $supportsRedirections, $prefix)."\n";
186 } 204 $ifOrElseIf = 'if';
187 } 205 }
188 206 }
189 if ($optimizable) { 207
208 if (!empty($prefix) && '/' !== $prefix) {
190 $code .= " }\n\n"; 209 $code .= " }\n\n";
191 // apply extra indention at each line (except empty ones) 210 // apply extra indention at each line (except empty ones)
192 $code = preg_replace('/^.{2,}$/m', ' $0', $code); 211 $code = preg_replace('/^.{2,}$/m', ' $0', $code);
193 } 212 }
194 213
215 $hasTrailingSlash = false; 234 $hasTrailingSlash = false;
216 $matches = false; 235 $matches = false;
217 $hostMatches = false; 236 $hostMatches = false;
218 $methods = $route->getMethods(); 237 $methods = $route->getMethods();
219 238
220 // GET and HEAD are equivalent 239 $supportsTrailingSlash = $supportsRedirections && (!$methods || in_array('GET', $methods));
221 if (in_array('GET', $methods) && !in_array('HEAD', $methods)) {
222 $methods[] = 'HEAD';
223 }
224
225 $supportsTrailingSlash = $supportsRedirections && (!$methods || in_array('HEAD', $methods));
226 $regex = $compiledRoute->getRegex(); 240 $regex = $compiledRoute->getRegex();
227 241
228 if (!count($compiledRoute->getPathVariables()) && false !== preg_match('#^(.)\^(?P<url>.*?)\$\1#'.(substr($regex, -1) === 'u' ? 'u' : ''), $regex, $m)) { 242 if (!count($compiledRoute->getPathVariables()) && false !== preg_match('#^(.)\^(?P<url>.*?)\$\1#'.('u' === substr($regex, -1) ? 'u' : ''), $regex, $m)) {
229 if ($supportsTrailingSlash && substr($m['url'], -1) === '/') { 243 if ($supportsTrailingSlash && '/' === substr($m['url'], -1)) {
230 $conditions[] = sprintf("rtrim(\$pathinfo, '/') === %s", var_export(rtrim(str_replace('\\', '', $m['url']), '/'), true)); 244 $conditions[] = sprintf('%s === $trimmedPathinfo', var_export(rtrim(str_replace('\\', '', $m['url']), '/'), true));
231 $hasTrailingSlash = true; 245 $hasTrailingSlash = true;
232 } else { 246 } else {
233 $conditions[] = sprintf('$pathinfo === %s', var_export(str_replace('\\', '', $m['url']), true)); 247 $conditions[] = sprintf('%s === $pathinfo', var_export(str_replace('\\', '', $m['url']), true));
234 } 248 }
235 } else { 249 } else {
236 if ($compiledRoute->getStaticPrefix() && $compiledRoute->getStaticPrefix() !== $parentPrefix) { 250 if ($compiledRoute->getStaticPrefix() && $compiledRoute->getStaticPrefix() !== $parentPrefix) {
237 $conditions[] = sprintf('0 === strpos($pathinfo, %s)', var_export($compiledRoute->getStaticPrefix(), true)); 251 $conditions[] = sprintf('0 === strpos($pathinfo, %s)', var_export($compiledRoute->getStaticPrefix(), true));
238 } 252 }
261 if ($conditions) { 275 if ($conditions) {
262 276
263 EOF; 277 EOF;
264 278
265 $gotoname = 'not_'.preg_replace('/[^A-Za-z0-9_]/', '', $name); 279 $gotoname = 'not_'.preg_replace('/[^A-Za-z0-9_]/', '', $name);
266 if ($methods) { 280
267 if (1 === count($methods)) { 281 // the offset where the return value is appended below, with indendation
268 $code .= <<<EOF 282 $retOffset = 12 + strlen($code);
269 if (\$this->context->getMethod() != '$methods[0]') {
270 \$allow[] = '$methods[0]';
271 goto $gotoname;
272 }
273
274
275 EOF;
276 } else {
277 $methods = implode("', '", $methods);
278 $code .= <<<EOF
279 if (!in_array(\$this->context->getMethod(), array('$methods'))) {
280 \$allow = array_merge(\$allow, array('$methods'));
281 goto $gotoname;
282 }
283
284
285 EOF;
286 }
287 }
288
289 if ($hasTrailingSlash) {
290 $code .= <<<EOF
291 if (substr(\$pathinfo, -1) !== '/') {
292 return \$this->redirect(\$pathinfo.'/', '$name');
293 }
294
295
296 EOF;
297 }
298
299 if ($schemes = $route->getSchemes()) {
300 if (!$supportsRedirections) {
301 throw new \LogicException('The "schemes" requirement is only supported for URL matchers that implement RedirectableUrlMatcherInterface.');
302 }
303 $schemes = str_replace("\n", '', var_export(array_flip($schemes), true));
304 $code .= <<<EOF
305 \$requiredSchemes = $schemes;
306 if (!isset(\$requiredSchemes[\$this->context->getScheme()])) {
307 return \$this->redirect(\$pathinfo, '$name', key(\$requiredSchemes));
308 }
309
310
311 EOF;
312 }
313 283
314 // optimize parameters array 284 // optimize parameters array
315 if ($matches || $hostMatches) { 285 if ($matches || $hostMatches) {
316 $vars = array(); 286 $vars = array();
317 if ($hostMatches) { 287 if ($hostMatches) {
321 $vars[] = '$matches'; 291 $vars[] = '$matches';
322 } 292 }
323 $vars[] = "array('_route' => '$name')"; 293 $vars[] = "array('_route' => '$name')";
324 294
325 $code .= sprintf( 295 $code .= sprintf(
326 " return \$this->mergeDefaults(array_replace(%s), %s);\n", 296 " \$ret = \$this->mergeDefaults(array_replace(%s), %s);\n",
327 implode(', ', $vars), 297 implode(', ', $vars),
328 str_replace("\n", '', var_export($route->getDefaults(), true)) 298 str_replace("\n", '', var_export($route->getDefaults(), true))
329 ); 299 );
330 } elseif ($route->getDefaults()) { 300 } elseif ($route->getDefaults()) {
331 $code .= sprintf(" return %s;\n", str_replace("\n", '', var_export(array_replace($route->getDefaults(), array('_route' => $name)), true))); 301 $code .= sprintf(" \$ret = %s;\n", str_replace("\n", '', var_export(array_replace($route->getDefaults(), array('_route' => $name)), true)));
332 } else { 302 } else {
333 $code .= sprintf(" return array('_route' => '%s');\n", $name); 303 $code .= sprintf(" \$ret = array('_route' => '%s');\n", $name);
304 }
305
306 if ($hasTrailingSlash) {
307 $code .= <<<EOF
308 if ('/' === substr(\$pathinfo, -1)) {
309 // no-op
310 } elseif ('GET' !== \$canonicalMethod) {
311 goto $gotoname;
312 } else {
313 return array_replace(\$ret, \$this->redirect(\$rawPathinfo.'/', '$name'));
314 }
315
316
317 EOF;
318 }
319
320 if ($methods) {
321 $methodVariable = in_array('GET', $methods) ? '$canonicalMethod' : '$requestMethod';
322 $methods = implode("', '", $methods);
323 }
324
325 if ($schemes = $route->getSchemes()) {
326 if (!$supportsRedirections) {
327 throw new \LogicException('The "schemes" requirement is only supported for URL matchers that implement RedirectableUrlMatcherInterface.');
328 }
329 $schemes = str_replace("\n", '', var_export(array_flip($schemes), true));
330 if ($methods) {
331 $code .= <<<EOF
332 \$requiredSchemes = $schemes;
333 \$hasRequiredScheme = isset(\$requiredSchemes[\$context->getScheme()]);
334 if (!in_array($methodVariable, array('$methods'))) {
335 if (\$hasRequiredScheme) {
336 \$allow = array_merge(\$allow, array('$methods'));
337 }
338 goto $gotoname;
339 }
340 if (!\$hasRequiredScheme) {
341 if ('GET' !== \$canonicalMethod) {
342 goto $gotoname;
343 }
344
345 return array_replace(\$ret, \$this->redirect(\$rawPathinfo, '$name', key(\$requiredSchemes)));
346 }
347
348
349 EOF;
350 } else {
351 $code .= <<<EOF
352 \$requiredSchemes = $schemes;
353 if (!isset(\$requiredSchemes[\$context->getScheme()])) {
354 if ('GET' !== \$canonicalMethod) {
355 goto $gotoname;
356 }
357
358 return array_replace(\$ret, \$this->redirect(\$rawPathinfo, '$name', key(\$requiredSchemes)));
359 }
360
361
362 EOF;
363 }
364 } elseif ($methods) {
365 $code .= <<<EOF
366 if (!in_array($methodVariable, array('$methods'))) {
367 \$allow = array_merge(\$allow, array('$methods'));
368 goto $gotoname;
369 }
370
371
372 EOF;
373 }
374
375 if ($hasTrailingSlash || $schemes || $methods) {
376 $code .= " return \$ret;\n";
377 } else {
378 $code = substr_replace($code, 'return', $retOffset, 6);
334 } 379 }
335 $code .= " }\n"; 380 $code .= " }\n";
336 381
337 if ($methods) { 382 if ($hasTrailingSlash || $schemes || $methods) {
338 $code .= " $gotoname:\n"; 383 $code .= " $gotoname:\n";
339 } 384 }
340 385
341 return $code; 386 return $code;
342 } 387 }
351 * @return DumperCollection A collection with routes grouped by host regex in sub-collections 396 * @return DumperCollection A collection with routes grouped by host regex in sub-collections
352 */ 397 */
353 private function groupRoutesByHostRegex(RouteCollection $routes) 398 private function groupRoutesByHostRegex(RouteCollection $routes)
354 { 399 {
355 $groups = new DumperCollection(); 400 $groups = new DumperCollection();
356
357 $currentGroup = new DumperCollection(); 401 $currentGroup = new DumperCollection();
358 $currentGroup->setAttribute('host_regex', null); 402 $currentGroup->setAttribute('host_regex', null);
359 $groups->add($currentGroup); 403 $groups->add($currentGroup);
360 404
361 foreach ($routes as $name => $route) { 405 foreach ($routes as $name => $route) {
369 } 413 }
370 414
371 return $groups; 415 return $groups;
372 } 416 }
373 417
374 /**
375 * Organizes the routes into a prefix tree.
376 *
377 * Routes order is preserved such that traversing the tree will traverse the
378 * routes in the origin order.
379 *
380 * @param DumperCollection $collection A collection of routes
381 *
382 * @return DumperPrefixCollection
383 */
384 private function buildPrefixTree(DumperCollection $collection)
385 {
386 $tree = new DumperPrefixCollection();
387 $current = $tree;
388
389 foreach ($collection as $route) {
390 $current = $current->addPrefixRoute($route);
391 }
392
393 $tree->mergeSlashNodes();
394
395 return $tree;
396 }
397
398 private function getExpressionLanguage() 418 private function getExpressionLanguage()
399 { 419 {
400 if (null === $this->expressionLanguage) { 420 if (null === $this->expressionLanguage) {
401 if (!class_exists('Symfony\Component\ExpressionLanguage\ExpressionLanguage')) { 421 if (!class_exists('Symfony\Component\ExpressionLanguage\ExpressionLanguage')) {
402 throw new \RuntimeException('Unable to use expressions as the Symfony ExpressionLanguage component is not installed.'); 422 throw new \RuntimeException('Unable to use expressions as the Symfony ExpressionLanguage component is not installed.');