8000 [Routing] support scheme requirement without redirectable dumped matcher · symfony/symfony@7154ec1 · GitHub
[go: up one dir, main page]

Skip to content

Commit 7154ec1

Browse files
committed
[Routing] support scheme requirement without redirectable dumped matcher
1 parent 308e12c commit 7154ec1

23 files changed

+292
-234
lines changed

src/Symfony/Bundle/FrameworkBundle/Tests/Routing/RedirectableUrlMatcherTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ public function testSchemeRedirect()
5353
'scheme' => 'https',
5454
'httpPort' => $context->getHttpPort(),
5555
'httpsPort' => $context->getHttpsPort(),
56-
'_route' => 'foo',
56+
'_route' => null,
5757
),
5858
$matcher->match('/foo')
5959
);

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

Lines changed: 20 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ private function generateMatchMethod(): string
111111

112112
$code = <<<EOF
113113
{
114-
\$allow = array();
114+
\$allow = \$allowSchemes = array();
115115
\$pathinfo = rawurldecode(\$rawPathinfo);
116116
\$context = \$this->context;
117117
\$requestMethod = \$canonicalMethod = \$context->getMethod();
@@ -128,21 +128,26 @@ private function generateMatchMethod(): string
128128
return <<<'EOF'
129129
public function match($pathinfo)
130130
{
131-
$allow = array();
132-
if ($ret = $this->doMatch($pathinfo, $allow)) {
131+
$allow = $allowSchemes = array();
132+
if ($ret = $this->doMatch($pathinfo, $allow, $allowSchemes)) {
133133
return $ret;
134134
}
135-
if ('/' !== $pathinfo && in_array($this->context->getMethod(), array('HEAD', 'GET'), true)) {
136-
$pathinfo = '/' !== $pathinfo[-1] ? $pathinfo.'/' : substr($pathinfo, 0, -1);
137-
if ($ret = $this->doMatch($pathinfo)) {
138-
return $this->redirect($pathinfo, $ret['_route']) + $ret;
135+
if (in_array($this->context->getMethod(), array('HEAD', 'GET'), true)) {
136+
if ($allowSchemes) {
137+
return $this->redirect($pathinfo, null, key($allowSchemes));
138+
}
139+
if ('/' !== $pathinfo) {
140+
$pathinfo = '/' !== $pathinfo[-1] ? $pathinfo.'/' : substr($pathinfo, 0, -1);
141+
if ($ret = $this->doMatch($pathinfo)) {
142+
return $this->redirect($pathinfo, $ret['_route']) + $ret;
143+
}
139144
}
140145
}
141146
142147
throw $allow ? new MethodNotAllowedException(array_keys($allow)) : new ResourceNotFoundException();
143148
}
144149
145-
private function doMatch(string $rawPathinfo, array &$allow = array()): ?array
150+
private function doMatch(string $rawPathinfo, array &$allow = array(), array &$allowSchemes = array()): ?array
146151

147152
EOF
148153
.$code."\n return null;\n }";
@@ -238,9 +243,6 @@ private function compileStaticRoutes(array $staticRoutes, bool $matchHost): stri
238243
}
239244

240245
if (!$route->getCondition()) {
241-
if (!$this->supportsRedirections && $route->getSchemes()) {
242-
throw new \LogicException('The "schemes" requirement is only supported for URL matchers that implement RedirectableUrlMatcherInterface.');
243-
}
244246
$default .= sprintf(
245247
"%s => array(%s, %s, %s, %s),\n",
246248
self::export($url),
@@ -535,8 +537,8 @@ private function compileSwitchDefault(bool $hasVars, bool $matchHost): string
535537
} else {
536538
$code = '';
537539
}
538-
if ($this->supportsRedirections) {
539-
$code .= <<<EOF
540+
541+
$code .= <<<EOF
540542
541543
\$hasRequiredScheme = !\$requiredSchemes || isset(\$requiredSchemes[\$context->getScheme()]);
542544
if (\$requiredMethods && !isset(\$requiredMethods[\$canonicalMethod]) && !isset(\$requiredMethods[\$requestMethod])) {
@@ -546,28 +548,13 @@ private function compileSwitchDefault(bool $hasVars, bool $matchHost): string
546548
break;
547549
}
548550
if (!\$hasRequiredScheme) {
549-
if ('GET' !== \$canonicalMethod) {
550-
break;
551-
}
552-
553-
return \$this->redirect(\$rawPathinfo, \$ret['_route'], key(\$requiredSchemes)) + \$ret;
554-
}
555-
556-
return \$ret;
557-
558-
EOF;
559-
} else {
560-
$code .= <<<EOF
561-
562-
if (\$requiredMethods && !isset(\$requiredMethods[\$canonicalMethod]) && !isset(\$requiredMethods[\$requestMethod])) {
563-
\$allow += \$requiredMethods;
551+
\$allowSchemes += \$requiredSchemes;
564552
break;
565553
}
566554
567555
return \$ret;
568556
569557
EOF;
570-
}
571558

572559
return $code;
573560
}
@@ -647,9 +634,6 @@ private function compileRoute(Route $route, string $name, bool $checkHost): stri
647634
}
648635

649636
if ($schemes = $route->getSchemes()) {
650-
if (!$this->supportsRedirections) {
651-
throw new \LogicException('The "schemes" requirement is only supported for URL matchers that implement RedirectableUrlMatcherInterface.');
652-
}
653637
$schemes = self::export(array_flip($schemes));
654638
if ($methods) {
655639
$code .= <<<EOF
@@ -662,11 +646,8 @@ private function compileRoute(Route $route, string $name, bool $checkHost): stri
662646
goto $gotoname;
663647
}
664648
if (!\$hasRequiredScheme) {
665-
if ('GET' !== \$canonicalMethod) {
666-
goto $gotoname;
667-
}
668-
669-
return \$this->redirect(\$rawPathinfo, '$name', key(\$requiredSchemes)) + \$ret;
649+
\$allowSchemes += \$requiredSchemes;
650+
goto $gotoname;
670651
}
671652
672653
@@ -675,11 +656,8 @@ private function compileRoute(Route $route, string $name, bool $checkHost): stri
675656
$code .= <<<EOF
676657
\$requiredSchemes = $schemes;
677658
if (!isset(\$requiredSchemes[\$context->getScheme()])) {
678-
if ('GET' !== \$canonicalMethod) {
679-
goto $gotoname;
680-
}
681-
682-
return \$this->redirect(\$rawPathinfo, '$name', key(\$requiredSchemes)) + \$ret;
659+
\$allowSchemes += \$requiredSchemes;
660+
goto $gotoname;
683661
}
684662
685663

src/Symfony/Component/Routing/Matcher/RedirectableUrlMatcher.php

Lines changed: 9 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
namespace Symfony\Component\Routing\Matcher;
1313

1414
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
15-
use Symfony\Component\Routing\Route;
1615

1716
/**
1817
* @author Fabien Potencier <fabien@symfony.com>
@@ -27,7 +26,15 @@ public function match($pathinfo)
2726
try {
2827
return parent::match($pathinfo);
2928
} catch (ResourceNotFoundException $e) {
30-
if ('/' === $pathinfo || !\in_array($this->context->getMethod(), array('HEAD', 'GET'), true)) {
29+
if (!\in_array($this->context->getMethod(), array('HEAD', 'GET'), true)) {
30+
throw $e;
31+
}
32+
33+
if ($this->allowSchemes) {
34+
return $this->redirect($pathinfo, null, current($this->allowSchemes));
35+
}
36+
37+
if ('/' === $pathinfo) {
3138
throw $e;
3239
}
3340

@@ -41,24 +48,4 @@ public function match($pathinfo)
4148
}
4249
}
4350
}
44-
45-
/**
46-
* {@inheritdoc}
47-
*/
48-
protected function handleRouteRequirements($pathinfo, $name, Route $route)
49-
{
50-
// expression condition
51-
if ($route->getCondition() && !$this->getExpressionLanguage()->evaluate($route->getCondition(), array('context' => $this->context, 'request' => $this->request ?: $this->createRequest($pathinfo)))) {
52-
return array(self::REQUIREMENT_MISMATCH, null);
53-
}
54-
55-
// check HTTP scheme requirement
56-
$scheme = $this->context->getScheme();
57-
$schemes = $route->getSchemes();
58-
if ($schemes && !$route->hasScheme($scheme)) {
59-
return array(self::ROUTE_MATCH, $this->redirect($pathinfo, $name, current($schemes)));
60-
}
61-
62-
return array(self::REQUIREMENT_MATCH, null);
63-
}
6451
}

src/Symfony/Component/Routing/Matcher/UrlMatcher.php

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,19 @@ class UrlMatcher implements UrlMatcherInterface, RequestMatcherInterface
3333
const ROUTE_MATCH = 2;
3434

3535
protected $context;
36+
37+
/**
38+
* Collects HTTP methods that would be allowed for the request.
39+
*/
3640
protected $allow = array();
41+
42+
/**
43+
* Collects URI schemes that would be allowed for the request.
44+
*
45+
* @internal
46+
*/
47+
protected $allowSchemes = array();
48+
3749
protected $routes;
3850
protected $request;
3951
protected $expressionLanguage;
@@ -70,7 +82,7 @@ public function getContext()
7082
*/
7183
public function match($pathinfo)
7284
{
73-
$this->allow = array();
85+
$this->allow = $this->allowSchemes = array();
7486

7587
if ($ret = $this->matchCollection(rawurldecode($pathinfo), $this->routes)) {
7688
return $ret;
@@ -141,22 +153,28 @@ protected function matchCollection($pathinfo, RouteCollection $routes)
141153
continue;
142154
}
143155

144-
// check HTTP method requirement
156+
$hasRequiredScheme = !$route->getSchemes() || $route->hasScheme($this->context->getScheme());
145157
if ($requiredMethods = $route->getMethods()) {
146158
// HEAD and GET are equivalent as per RFC
147159
if ('HEAD' === $method = $this->context->getMethod()) {
148160
$method = 'GET';
149161
}
150162

151163
if (!in_array($method, $requiredMethods)) {
152-
if (self::REQUIREMENT_MATCH === $status[0]) {
164+
if ($hasRequiredScheme) {
153165
$this->allow = array_merge($this->allow, $requiredMethods);
154166
}
155167

156168
continue;
157169
}
158170
}
159171

172+
if (!$hasRequiredScheme) {
173+
$this->allowSchemes = array_merge($this->allowSchemes, $route->getSchemes());
174+
175+
continue;
176+
}
177+
160178
return $this->getAttributes($route, $name, array_replace($matches, $hostMatches, isset($status[1]) ? $status[1] : array()));
161179
}
162180
}
@@ -197,11 +215,7 @@ protected function handleRouteRequirements($pathinfo, $name, Route $route)
197215
return array(self::REQUIREMENT_MISMATCH, null);
198216
}
199217

200-
// check HTTP scheme requirement
201-
$scheme = $this->context->getScheme();
202-
$status = $route->getSchemes() && !$route->hasScheme($scheme) ? self::REQUIREMENT_MISMATCH : self::REQUIREMENT_MATCH;
203-
204-
return array($status, null);
218+
return array(self::REQUIREMENT_MATCH, null);
205219
}
206220

207221
/**

src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher0.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public function __construct(RequestContext $context)
1717

1818
public function match($rawPathinfo)
1919
{
20-
$allow = array();
20+
$allow = $allowSchemes = array();
2121
$pathinfo = rawurldecode($rawPathinfo);
2222
$context = $this->context;
2323
$requestMethod = $canonicalMethod = $context->getMethod();

src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher1.php

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public function __construct(RequestContext $context)
1717

1818
public function match($rawPathinfo)
1919
{
20-
$allow = array();
20+
$allow = $allowSchemes = array();
2121
$pathinfo = rawurldecode($rawPathinfo);
2222
$context = $this->context;
2323
$requestMethod = $canonicalMethod = $context->getMethod();
@@ -64,8 +64,15 @@ public function match($rawPathinfo)
6464
}
6565
}
6666

67+
$hasRequiredScheme = !$requiredSchemes || isset($requiredSchemes[$context->getScheme()]);
6768
if ($requiredMethods && !isset($requiredMethods[$canonicalMethod]) && !isset($requiredMethods[$requestMethod])) {
68-
$allow += $requiredMethods;
69+
if ($hasRequiredScheme) {
70+
$allow += $requiredMethods;
71+
}
72+
break;
73+
}
74+
if (!$hasRequiredScheme) {
75+
$allowSchemes += $requiredSchemes;
6976
break;
7077
}
7178

@@ -209,8 +216,15 @@ public function match($rawPathinfo)
209216
}
210217
}
211218

219+
$hasRequiredScheme = !$requiredSchemes || isset($requiredSchemes[$context->getScheme()]);
212220
if ($requiredMethods && !isset($requiredMethods[$canonicalMethod]) && !isset($requiredMethods[$requestMethod])) {
213-
$allow += $requiredMethods;
221+
if ($hasRequiredScheme) {
222+
$allow += $requiredMethods;
223+
}
224+
break;
225+
}
226+
if (!$hasRequiredScheme) {
227+
$allowSchemes += $requiredSchemes;
214228
break;
215229
}
216230

src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher10.php

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public function __construct(RequestContext $context)
1717

1818
public function match($rawPathinfo)
1919
{
20-
$allow = array();
20+
$allow = $allowSchemes = array();
2121
$pathinfo = rawurldecode($rawPathinfo);
2222
$context = $this->context;
2323
$requestMethod = $canonicalMethod = $context->getMethod();
@@ -2799,8 +2799,15 @@ public function match($rawPathinfo)
27992799
}
28002800
}
28012801

2802+
$hasRequiredScheme = !$requiredSchemes || isset($requiredSchemes[$context->getScheme()]);
28022803
if ($requiredMethods && !isset($requiredMethods[$canonicalMethod]) && !isset($requiredMethods[$requestMethod])) {
2803-
$allow += $requiredMethods;
2804+
if ($hasRequiredScheme) {
2805+
$allow += $requiredMethods;
2806+
}
2807+
break;
2808+
}
2809+
if (!$hasRequiredScheme) {
2810+
$allowSchemes += $requiredSchemes;
28042811
break;
28052812
}
28062813

src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher11.php

Lines changed: 15 additions & 13 deletions
Original file line numberDiff l 341A ine numberDiff line change
@@ -17,23 +17,28 @@ public function __construct(RequestContext $context)
1717

1818
public function match($pathinfo)
1919
{
20-
$allow = array();
21-
if ($ret = $this->doMatch($pathinfo, $allow)) {
20+
$allow = $allowSchemes = array();
21+
if ($ret = $this->doMatch($pathinfo, $allow, $allowSchemes)) {
2222
return $ret;
2323
}
24-
if ('/' !== $pathinfo && in_array($this->context->getMethod(), array('HEAD', 'GET'), true)) {
25-
$pathinfo = '/' !== $pathinfo[-1] ? $pathinfo.'/' : substr($pathinfo, 0, -1);
26-
if ($ret = $this->doMatch($pathinfo)) {
27-
return $this->redirect($pathinfo, $ret['_route']) + $ret;
24+
if (in_array($this->context->getMethod(), array('HEAD', 'GET'), true)) {
25+
if ($allowSchemes) {
26+
return $this->redirect($pathinfo, null, key($allowSchemes));
27+
}
28+
if ('/' !== $pathinfo) {
29+
$pathinfo = '/' !== $pathinfo[-1] ? $pathinfo.'/' : substr($pathinfo, 0, -1);
30+
if ($ret = $this->doMatch($pathinfo)) {
31+
return $this->redirect($pathinfo, $ret['_route']) + $ret;
32+
}
2833
}
2934
}
3035

3136
throw $allow ? new MethodNotAllowedException(array_keys($allow)) : new ResourceNotFoundException();
3237
}
3338

34-
private function doMatch(string $rawPathinfo, array &$allow = array()): ?array
39+
private function doMatch(string $rawPathinfo, array &$allow = array(), array &$allowSchemes = array()): ?array
3540
{
36-
$allow = array();
41+
$allow = $allowSchemes = array();
3742
$pathinfo = rawurldecode($rawPathinfo);
3843
$context = $this->context;
3944
$requestMethod = $canonicalMethod = $context->getMethod();
@@ -113,11 +118,8 @@ private function doMatch(string $rawPathinfo, array &$allow = array()): ?array
113118
break;
114119
}
115120
if (!$hasRequiredScheme) {
116-
if ('GET' !== $canonicalMethod) {
117-
break;
118-
}
119-
120-
return $this->redirect($rawPathinfo, $ret['_route'], key($requiredSchemes)) + $ret;
121+
$allowSchemes += $requiredSchemes;
122+
break;
121123
}
122124

123125
return $ret;

0 commit comments

Comments
 (0)
0