Skip to content

Commit 5b3e320

Browse files
committed
[Routing] support scheme requirement without redirectable dumped matcher
1 parent be1a3b4 commit 5b3e320

21 files changed

+229
-220
lines changed

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

Lines changed: 34 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,27 @@ 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+
140+
if ('/' !== $pathinfo) {
141+
$pathinfo = '/' !== $pathinfo[-1] ? $pathinfo.'/' : substr($pathinfo, 0, -1);
142+
if ($ret = $this->doMatch($pathinfo)) {
143+
return $this->redirect($pathinfo, $ret['_route']) + $ret;
144+
}
139145
}
140146
}
141147
142148
throw $allow ? new MethodNotAllowedException(array_keys($allow)) : new ResourceNotFoundException();
143149
}
144150
145-
private function doMatch(string $rawPathinfo, array &$allow = array()): ?array
151+
private function doMatch(string $rawPathinfo, array &$allow = array(), array &$allowSchemes = array()): ?array
146152

147153
EOF
148154
.$code."\n return null;\n }";
@@ -238,9 +244,6 @@ private function compileStaticRoutes(array $staticRoutes, bool $matchHost): stri
238244
}
239245

240246
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-
}
244247
$default .= sprintf(
245248
"%s => array(%s, %s, %s, %s),\n",
246249
self::export($url),
@@ -535,24 +538,20 @@ private function compileSwitchDefault(bool $hasVars, bool $matchHost): string
535538
} else {
536539
$code = '';
537540
}
538-
if ($this->supportsRedirections) {
539-
$code .= <<<EOF
540541

541-
if (\$requiredSchemes && !isset(\$requiredSchemes[\$context->getScheme()])) {
542-
if ('GET' !== \$canonicalMethod) {
543-
\$allow['GET'] = 'GET';
544-
break;
545-
}
542+
$code .= <<<EOF
546543
547-
return \$this->redirect(\$rawPathinfo, \$ret['_route'], key(\$requiredSchemes)) + \$ret;
544+
if (\$requiredMethods && !isset(\$requiredMethods[\$canonicalMethod]) && !isset(\$requiredMethods[\$requestMethod])) {
545+
\$allow += \$requiredMethods;
546+
break;
548547
}
549548
550549
EOF;
551-
}
550+
552551
$code .= <<<EOF
553552
554-
if (\$requiredMethods && !isset(\$requiredMethods[\$canonicalMethod]) && !isset(\$requiredMethods[\$requestMethod])) {
555-
\$allow += \$requiredMethods;
553+
if (\$requiredSchemes && !isset(\$requiredSchemes[\$context->getScheme()])) {
554+
\$allowSchemes += \$requiredSchemes;
556555
break;
557556
}
558557
@@ -575,7 +574,6 @@ private function compileRoute(Route $route, string $name, bool $checkHost): stri
575574
$conditions = array();
576575
$matches = (bool) $compiledRoute->getPathVariables();
577576
$hostMatches = (bool) $compiledRoute->getHostVariables();
578-
$methods = array_flip($route->getMethods());
579577

580578
if ($route->getCondition()) {
581579
$expression = $this->getExpressionLanguage()->compile($route->getCondition(), array('context', 'request'));
@@ -632,33 +630,27 @@ private function compileRoute(Route $route, string $name, bool $checkHost): stri
632630
$code .= sprintf(" \$ret = array('_route' => '%s');\n", $name);
633631
}
634632

635-
if ($schemes = $route->getSchemes()) {
636-
if (!$this->supportsRedirections) {
637-
throw new \LogicException('The "schemes" requirement is only supported for URL matchers that implement RedirectableUrlMatcherInterface.');
638-
}
639-
$schemes = self::export(array_flip($schemes));
640-
$code .= <<<EOF
641-
\$requiredSchemes = $schemes;
642-
if (!isset(\$requiredSchemes[\$context->getScheme()])) {
643-
if ('GET' !== \$canonicalMethod) {
644-
\$allow['GET'] = 'GET';
645-
goto $gotoname;
646-
}
633+
if ($methods = $route->getMethods()) {
634+
$methods = array_flip($methods);
635+
$methodVariable = isset($methods['GET']) ? '$canonicalMethod' : '$requestMethod';
636+
$methods = self::export($methods);
647637

648-
return \$this->redirect(\$rawPathinfo, '$name', key(\$requiredSchemes)) + \$ret;
638+
$code .= <<<EOF
639+
if (!isset((\$a = {$methods})[{$methodVariable}])) {
640+
\$allow += \$a;
641+
goto $gotoname;
649642
}
650643
651644
652645
EOF;
653646
}
654647

655-
if ($methods) {
656-
$methodVariable = isset($methods['GET']) ? '$canonicalMethod' : '$requestMethod';
657-
$methods = self::export($methods);
658-
648+
if ($schemes = $route->getSchemes()) {
649+
$schemes = self::export(array_flip($schemes));
659650
$code .= <<<EOF
660-
if (!isset((\$a = {$methods})[{$methodVariable}])) {
661-
\$allow += \$a;
651+
\$requiredSchemes = $schemes;
652+
if (!isset(\$requiredSchemes[\$context->getScheme()])) {
653+
\$allowSchemes += \$requiredSchemes;
662654
goto $gotoname;
663655
}
664656

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

Lines changed: 9 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,15 @@ public function match($pathinfo)
2727
try {
2828
return parent::match($pathinfo);
2929
} catch (ResourceNotFoundException $e) {
30-
if ('/' === $pathinfo || !\in_array($this->context->getMethod(), array('HEAD', 'GET'), true)) {
30+
if (!\in_array($this->context->getMethod(), array('HEAD', 'GET'), true)) {
31+
throw $e;
32+
}
33+
34+
if ($this->allowSchemes) {
35+
return $this->redirect($pathinfo, null, current($this->allowSchemes));
36+
}
37+
38+
if ('/' === $pathinfo) {
3139
throw $e;
3240
}
3341

@@ -41,24 +49,4 @@ public function match($pathinfo)
4149
}
4250
}
4351
}
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-
}
6452
}

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

Lines changed: 22 additions & 6 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;
@@ -149,12 +161,20 @@ protected function matchCollection($pathinfo, RouteCollection $routes)
149161
}
150162
}
151163

164+
// check custom requirements
152165
$status = $this->handleRouteRequirements($pathinfo, $name, $route);
153166

154167
if (self::REQUIREMENT_MISMATCH === $status[0]) {
155168
continue;
156169
}
157170

171+
// check URI scheme requirement
172+
if ($route->getSchemes() && !$route->hasScheme($this->context->getScheme())) {
173+
$this->allowSchemes = array_merge($this->allowSchemes, $route->getSchemes());
174+
175+
continue;
176+
}
177+
158178
return $this->getAttributes($route, $name, array_replace($matches, $hostMatches, isset($status[1]) ? $status[1] : array()));
159179
}
160180
}
@@ -195,11 +215,7 @@ protected function handleRouteRequirements($pathinfo, $name, Route $route)
195215
return array(self::REQUIREMENT_MISMATCH, null);
196216
}
197217

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

205221
/**

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: 11 additions & 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();
@@ -69,6 +69,11 @@ public function match($rawPathinfo)
6969
break;
7070
}
7171

72+
if ($requiredSchemes && !isset($requiredSchemes[$context->getScheme()])) {
73+
$allowSchemes += $requiredSchemes;
74+
break;
75+
}
76+
7277
return $ret;
7378
}
7479

@@ -214,6 +219,11 @@ public function match($rawPathinfo)
214219
break;
215220
}
216221

222+
if ($requiredSchemes && !isset($requiredSchemes[$context->getScheme()])) {
223+
$allowSchemes += $requiredSchemes;
224+
break;
225+
}
226+
217227
return $ret;
218228
}
219229

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

Lines changed: 6 additions & 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();
@@ -2804,6 +2804,11 @@ public function match($rawPathinfo)
28042804
break;
28052805
}
28062806

2807+
if ($requiredSchemes && !isset($requiredSchemes[$context->getScheme()])) {
2808+
$allowSchemes += $requiredSchemes;
2809+
break;
2810+
}
2811+
28072812
return $ret;
28082813
}
28092814

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

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -17,23 +17,29 @@ 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+
29+
if ('/' !== $pathinfo) {
30+
$pathinfo = '/' !== $pathinfo[-1] ? $pathinfo.'/' : substr($pathinfo, 0, -1);
31+
if ($ret = $this->doMatch($pathinfo)) {
32+
return $this->redirect($pathinfo, $ret['_route']) + $ret;
33+
}
2834
}
2935
}
3036

3137
throw $allow ? new MethodNotAllowedException(array_keys($allow)) : new ResourceNotFoundException();
3238
}
3339

34-
private function doMatch(string $rawPathinfo, array &$allow = array()): ?array
40+
private function doMatch(string $rawPathinfo, array &$allow = array(), array &$allowSchemes = array()): ?array
3541
{
36-
$allow = array();
42+
$allow = $allowSchemes = array();
3743
$pathinfo = rawurldecode($rawPathinfo);
3844
$context = $this->context;
3945
$requestMethod = $canonicalMethod = $context->getMethod();
@@ -105,20 +111,16 @@ private function doMatch(string $rawPathinfo, array &$allow = array()): ?array
105111
}
106112
}
107113

108-
if ($requiredSchemes && !isset($requiredSchemes[$context->getScheme()])) {
109-
if ('GET' !== $canonicalMethod) {
110-
$allow['GET'] = 'GET';
111-
break;
112-
}
113-
114-
return $this->redirect($rawPathinfo, $ret['_route'], key($requiredSchemes)) + $ret;
115-
}
116-
117114
if ($requiredMethods && !isset($requiredMethods[$canonicalMethod]) && !isset($requiredMethods[$requestMethod])) {
118115
$allow += $requiredMethods;
119116
break;
120117
}
121118

119+
if ($requiredSchemes && !isset($requiredSchemes[$context->getScheme()])) {
120+
$allowSchemes += $requiredSchemes;
121+
break;
122+
}
123+
122124
return $ret;
123125
}
124126

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

Lines changed: 6 additions & 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();
@@ -74,6 +74,11 @@ public function match($rawPathinfo)
7474
break;
7575
}
7676

77+
if ($requiredSchemes && !isset($requiredSchemes[$context->getScheme()])) {
78+
$allowSchemes += $requiredSchemes;
79+
break;
80+
}
81+
7782
return $ret;
7883
}
7984

0 commit comments

Comments
 (0)