Skip to content

Commit 0ea1adf

Browse files
committed
minor symfony#28798 [Routing] simplify PhpMatcherDumper by splitting code logic from route data (nicolas-grekas)
This PR was merged into the 4.2-dev branch. Discussion ---------- [Routing] simplify PhpMatcherDumper by splitting code logic from route data | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | no | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | - | License | MIT | Doc PR | - This PR splits all logic that is currently dumped into the matcher in a dedicated trait. This makes the code easier to maintain and prepares for the next step (making the dumper compile routes as a set of PHP arrays instead of code.) This diff is huge because affected fixtures are also huge, but it's negative, removing 1200 lines! Commits ------- 22186c7 [Routing] simplify PhpMatcherDumper by splitting code logic from route data
2 parents 6de1577 + 22186c7 commit 0ea1adf

16 files changed

+1503
-2717
lines changed

src/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php

Lines changed: 73 additions & 376 deletions
Large diffs are not rendered by default.
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
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\Dumper;
13+
14+
use Symfony\Component\Routing\Exception\MethodNotAllowedException;
15+
use Symfony\Component\Routing\Exception\NoConfigurationException;
16+
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
17+
use Symfony\Component\Routing\Matcher\RedirectableUrlMatcherInterface;
18+
19+
/**
20+
* @author Nicolas Grekas <p@tchwork.com>
21+
*
22+
* @internal
23+
*/
24+
trait PhpMatcherTrait
25+
{
26+
private $matchHost = false;
27+
private $staticRoutes = array();
28+
private $regexpList = array();
29+
private $dynamicRoutes = array();
30+
private $checkCondition;
31+
32+
public function match($pathinfo)
33+
{
34+
$allow = $allowSchemes = array();
35+
if ($ret = $this->doMatch($pathinfo, $allow, $allowSchemes)) {
36+
return $ret;
37+
}
38+
if ($allow) {
39+
throw new MethodNotAllowedException(array_keys($allow));
40+
}
41+
if (!$this instanceof RedirectableUrlMatcherInterface) {
42+
throw new ResourceNotFoundException();
43+
}
44+
if (!\in_array($this->context->getMethod(), array('HEAD', 'GET'), true)) {
45+
// no-op
46+
} elseif ($allowSchemes) {
47+
redirect_scheme:
48+
$scheme = $this->context->getScheme();
49+
$this->context->setScheme(key($allowSchemes));
50+
try {
51+
if ($ret = $this->doMatch($pathinfo)) {
52+
return $this->redirect($pathinfo, $ret['_route'], $this->context->getScheme()) + $ret;
53+
}
54+
} finally {
55+
$this->context->setScheme($scheme);
56+
}
57+
} elseif ('/' !== $pathinfo) {
58+
$pathinfo = '/' !== $pathinfo[-1] ? $pathinfo.'/' : substr($pathinfo, 0, -1);
59+
if ($ret = $this->doMatch($pathinfo, $allow, $allowSchemes)) {
60+
return $this->redirect($pathinfo, $ret['_route']) + $ret;
61+
}
62+
if ($allowSchemes) {
63+
goto redirect_scheme;
64+
}
65+
}
66+
67+
throw new ResourceNotFoundException();
68+
}
69+
70+
private function doMatch(string $rawPathinfo, array &$allow = array(), array &$allowSchemes = array()): ?array
71+
{
72+
$allow = $allowSchemes = array();
73+
$pathinfo = rawurldecode($rawPathinfo);
74+
$context = $this->context;
75+
$requestMethod = $canonicalMethod = $context->getMethod();
76+
77+
if ($this->matchHost) {
78+
$host = strtolower($context->getHost());
79+
}
80+
81+
if ('HEAD' === $requestMethod) {
82+
$canonicalMethod = 'GET';
83+
}
84+
85+
foreach ($this->staticRoutes[$pathinfo] ?? array() as list($ret, $requiredHost, $requiredMethods, $requiredSchemes, $condition)) {
86+
if ($condition && !($this->checkCondition)($condition, $context, 0 < $condition ? $request ?? $request = $this->request ?: $this->createRequest($pathinfo) : null)) {
87+
continue;
88+
}
89+
90+
if ($requiredHost) {
91+
if ('#' !== $requiredHost[0] ? $requiredHost !== $host : !preg_match($requiredHost, $host, $hostMatches)) {
92+
continue;
93+
}
94+
if ('#' === $requiredHost[0] && $hostMatches) {
95+
$hostMatches['_route'] = $ret['_route'];
96+
$ret = $this->mergeDefaults($hostMatches, $ret);
97+
}
98+
}
99+
100+
$hasRequiredScheme = !$requiredSchemes || isset($requiredSchemes[$context->getScheme()]);
101+
if ($requiredMethods && !isset($requiredMethods[$canonicalMethod]) && !isset($requiredMethods[$requestMethod])) {
102+
if ($hasRequiredScheme) {
103+
$allow += $requiredMethods;
104+
}
105+
continue;
106+
}
107+
if (!$hasRequiredScheme) {
108+
$allowSchemes += $requiredSchemes;
109+
continue;
110+
}
111+
112+
return $ret;
113+
}
114+
115+
$matchedPathinfo = $this->matchHost ? $host.'.'.$pathinfo : $pathinfo;
116+
117+
foreach ($this->regexpList as $offset => $regex) {
118+
while (preg_match($regex, $matchedPathinfo, $matches)) {
119+
foreach ($this->dynamicRoutes[$m = (int) $matches['MARK']] as list($ret, $vars, $requiredMethods, $requiredSchemes, $condition)) {
120+
if ($condition && !($this->checkCondition)($condition, $context, 0 < $condition ? $request ?? $request = $this->request ?: $this->createRequest($pathinfo) : null)) {
121+
continue;
122+
}
123+
124+
foreach ($vars as $i => $v) {
125+
if (isset($matches[1 + $i])) {
126+
$ret[$v] = $matches[1 + $i];
127+
}
128+
}
129+
130+
$hasRequiredScheme = !$requiredSchemes || isset($requiredSchemes[$context->getScheme()]);
131+
if ($requiredMethods && !isset($requiredMethods[$canonicalMethod]) && !isset($requiredMethods[$requestMethod])) {
132+
if ($hasRequiredScheme) {
133+
$allow += $requiredMethods;
134+
}
135+
continue;
136+
}
137+
if (!$hasRequiredScheme) {
138+
$allowSchemes += $requiredSchemes;
139+
continue;
140+
}
141+
142+
return $ret;
143+
}
144+
145+
$regex = substr_replace($regex, 'F', $m - $offset, 1 + \strlen($m));
146+
$offset += \strlen($m);
147+
}
148+
}
149+
150+
if ('/' === $pathinfo && !$allow && !$allowSchemes) {
151+
throw new NoConfigurationException();
152+
}
153+
154+
return null;
155+
}
156+
}
Lines changed: 3 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
<?php
22

3-
use Symfony\Component\Routing\Exception\MethodNotAllowedException;
4-
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
3+
use Symfony\Component\Routing\Matcher\Dumper\PhpMatcherTrait;
54
use Symfony\Component\Routing\RequestContext;
65

76
/**
@@ -10,26 +9,10 @@
109
*/
1110
class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
1211
{
12+
use PhpMatcherTrait;
13+
1314
public function __construct(RequestContext $context)
1415
{
1516
$this->context = $context;
1617
}
17-
18-
public function match($rawPathinfo)
19-
{
20-
$allow = $allowSchemes = array();
21-
$pathinfo = rawurldecode($rawPathinfo);
22-
$context = $this->context;
23-
$requestMethod = $canonicalMethod = $context->getMethod();
24-
25-
if ('HEAD' === $requestMethod) {
26-
$canonicalMethod = 'GET';
27-
}
28-
29-
if ('/' === $pathinfo && !$allow && !$allowSchemes) {
30-
throw new Symfony\Component\Routing\Exception\NoConfigurationException();
31-
}
32-
33-
throw $allow ? new MethodNotAllowedException(array_keys($allow)) : new ResourceNotFoundException();
34-
}
3518
}

0 commit comments

Comments
 (0)