Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 8c36250

Browse filesBrowse files
Tobionnicolas-grekas
authored andcommitted
[Routing] support scheme requirement without redirectable dumped matcher
1 parent 308e12c commit 8c36250
Copy full SHA for 8c36250

22 files changed

+297
-238
lines changed

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php
+24-47Lines changed: 24 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ class PhpMatcherDumper extends MatcherDumper
2929
{
3030
private $expressionLanguage;
3131
private $signalingException;
32-
private $supportsRedirections;
3332

3433
/**
3534
* @var ExpressionFunctionProviderInterface[]
@@ -57,7 +56,7 @@ public function dump(array $options = array())
5756

5857
// trailing slash support is only enabled if we know how to redirect the user
5958
$interfaces = class_implements($options['base_class']);
60-
$this->supportsRedirections = isset($interfaces[RedirectableUrlMatcherInterface::class]);
59+
$supportsRedirections = isset($interfaces[RedirectableUrlMatcherInterface::class]);
6160

6261
return <<<EOF
6362
<?php
@@ -77,7 +76,7 @@ public function __construct(RequestContext \$context)
7776
\$this->context = \$context;
7877
}
7978
80-
{$this->generateMatchMethod()}
79+
{$this->generateMatchMethod($supportsRedirections)}
8180
}
8281
8382
EOF;
@@ -91,7 +90,7 @@ public function addExpressionLanguageProvider(ExpressionFunctionProviderInterfac
9190
/**
9291
* Generates the code for the match method implementing UrlMatcherInterface.
9392
*/
94-
private function generateMatchMethod(): string
93+
private function generateMatchMethod(bool $supportsRedirections): string
9594
{
9695
// Group hosts by same-suffix, re-order when possible
9796
$matchHost = false;
@@ -111,7 +110,7 @@ private function generateMatchMethod(): string
111110

112111
$code = <<<EOF
113112
{
114-
\$allow = array();
113+
\$allow = \$allowSchemes = array();
115114
\$pathinfo = rawurldecode(\$rawPathinfo);
116115
\$context = \$this->context;
117116
\$requestMethod = \$canonicalMethod = \$context->getMethod();
@@ -124,25 +123,30 @@ private function generateMatchMethod(): string
124123
125124
EOF;
126125

127-
if ($this->supportsRedirections) {
126+
if ($supportsRedirections) {
128127
return <<<'EOF'
129128
public function match($pathinfo)
130129
{
131-
$allow = array();
132-
if ($ret = $this->doMatch($pathinfo, $allow)) {
130+
$allow = $allowSchemes = array();
131+
if ($ret = $this->doMatch($pathinfo, $allow, $allowSchemes)) {
133132
return $ret;
134133
}
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;
134+
if (in_array($this->context->getMethod(), array('HEAD', 'GET'), true)) {
135+
if ($allowSchemes) {
136+
return $this->redirect($pathinfo, current($allowSchemes), key($allowSchemes));
137+
}
138+
if ('/' !== $pathinfo) {
139+
$pathinfo = '/' !== $pathinfo[-1] ? $pathinfo.'/' : substr($pathinfo, 0, -1);
140+
if ($ret = $this->doMatch($pathinfo)) {
141+
return $this->redirect($pathinfo, $ret['_route']) + $ret;
142+
}
139143
}
140144
}
141145
142146
throw $allow ? new MethodNotAllowedException(array_keys($allow)) : new ResourceNotFoundException();
143147
}
144148
145-
private function doMatch(string $rawPathinfo, array &$allow = array()): ?array
149+
private function doMatch(string $rawPathinfo, array &$allow = array(), array &$allowSchemes = array()): ?array
146150

147151
EOF
148152
.$code."\n return null;\n }";
@@ -238,9 +242,6 @@ private function compileStaticRoutes(array $staticRoutes, bool $matchHost): stri
238242
}
239243

240244
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-
}
244245
$default .= sprintf(
245246
"%s => array(%s, %s, %s, %s),\n",
246247
self::export($url),
@@ -535,8 +536,8 @@ private function compileSwitchDefault(bool $hasVars, bool $matchHost): string
535536
} else {
536537
$code = '';
537538
}
538-
if ($this->supportsRedirections) {
539-
$code .= <<<EOF
539+
540+
$code .= <<<EOF
540541
541542
\$hasRequiredScheme = !\$requiredSchemes || isset(\$requiredSchemes[\$context->getScheme()]);
542543
if (\$requiredMethods && !isset(\$requiredMethods[\$canonicalMethod]) && !isset(\$requiredMethods[\$requestMethod])) {
@@ -546,28 +547,13 @@ private function compileSwitchDefault(bool $hasVars, bool $matchHost): string
546547
break;
547548
}
548549
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;
550+
\$allowSchemes += array(key(\$requiredSchemes) => \$ret['_route']);
564551
break;
565552
}
566553
567554
return \$ret;
568555
569556
EOF;
570-
}
571557

572558
return $code;
573559
}
@@ -647,9 +633,6 @@ private function compileRoute(Route $route, string $name, bool $checkHost): stri
647633
}
648634

649635
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-
}
653636
$schemes = self::export(array_flip($schemes));
654637
if ($methods) {
655638
$code .= <<<EOF
@@ -662,11 +645,8 @@ private function compileRoute(Route $route, string $name, bool $checkHost): stri
662645
goto $gotoname;
663646
}
664647
if (!\$hasRequiredScheme) {
665-
if ('GET' !== \$canonicalMethod) {
666-
goto $gotoname;
667-
}
668-
669-
return \$this->redirect(\$rawPathinfo, '$name', key(\$requiredSchemes)) + \$ret;
648+
\$allowSchemes += array(key(\$requiredSchemes) => \$ret['_route']);
649+
goto $gotoname;
670650
}
671651
672652
@@ -675,11 +655,8 @@ private function compileRoute(Route $route, string $name, bool $checkHost): stri
675655
$code .= <<<EOF
676656
\$requiredSchemes = $schemes;
677657
if (!isset(\$requiredSchemes[\$context->getScheme()])) {
678-
if ('GET' !== \$canonicalMethod) {
679-
goto $gotoname;
680-
}
681-
682-
return \$this->redirect(\$rawPathinfo, '$name', key(\$requiredSchemes)) + \$ret;
658+
\$allowSchemes += array(key(\$requiredSchemes) => \$ret['_route']);
659+
goto $gotoname;
683660
}
684661
685662

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/Routing/Matcher/RedirectableUrlMatcher.php
+9-22Lines 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, current($this->allowSchemes), key($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

Copy file name to clipboardExpand all lines: src/Symfony/Component/Routing/Matcher/UrlMatcher.php
+25-9Lines changed: 25 additions & 9 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,23 +153,31 @@ 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

160-
return $this->getAttributes($route, $name, array_replace($matches, $hostMatches, isset($status[1]) ? $status[1] : array()));
172+
$ret = $this->getAttributes($route, $name, array_replace($matches, $hostMatches, isset($status[1]) ? $status[1] : array()));
173+
174+
if (!$hasRequiredScheme) {
175+
$this->allowSchemes += array(current($route->getSchemes()) => $ret['_route'] ?? null);
176+
177+
continue;
178+
}
179+
180+
return $ret;
161181
}
162182
}
163183

@@ -197,11 +217,7 @@ protected function handleRouteRequirements($pathinfo, $name, Route $route)
197217
return array(self::REQUIREMENT_MISMATCH, null);
198218
}
199219

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);
220+
return array(self::REQUIREMENT_MATCH, null);
205221
}
206222

207223
/**

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher0.php
+1-1Lines 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

Copy file name to clipboardExpand all lines: src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher1.php
+17-3Lines 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 += array(key($requiredSchemes) => $ret['_route']);
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 += array(key($requiredSchemes) => $ret['_route']);
214228
break;
215229
}
216230

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher10.php
+9-2Lines 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 += array(key($requiredSchemes) => $ret['_route']);
28042811
break;
28052812
}
28062813

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.