@@ -107,12 +107,10 @@ private function generateMatchMethod(bool $supportsRedirections): string
107
107
$ code = rtrim ($ this ->compileRoutes ($ routes , $ supportsRedirections , $ matchHost ), "\n" );
108
108
$ fetchHost = $ matchHost ? " \$host = strtolower( \$context->getHost()); \n" : '' ;
109
109
110
- return <<<EOF
111
- public function match( \$rawPathinfo)
110
+ $ code = <<<EOF
112
111
{
113
112
\$allow = array();
114
113
\$pathinfo = rawurldecode( \$rawPathinfo);
115
- \$trimmedPathinfo = rtrim( \$pathinfo, '/');
116
114
\$context = \$this->context;
117
115
\$requestMethod = \$canonicalMethod = \$context->getMethod();
118
116
{$ fetchHost }
@@ -125,6 +123,36 @@ public function match(\$rawPathinfo)
125
123
throw \$allow ? new MethodNotAllowedException(array_keys( \$allow)) : new ResourceNotFoundException();
126
124
}
127
125
EOF ;
126
+
127
+ if ($ supportsRedirections ) {
128
+ return <<<'EOF'
129
+ public function match($pathinfo)
130
+ {
131
+ try {
132
+ return $this->doMatch($pathinfo);
133
+ } catch (ResourceNotFoundException $e) {
134
+ if (!in_array($this->context->getMethod(), array('HEAD', 'GET'), true)) {
135
+ throw $e;
136
+ }
137
+
138
+ try {
139
+ $pathinfo = '/' !== $pathinfo[-1] ? $pathinfo.'/' : substr($pathinfo, 0, -1);
140
+ $ret = $this->doMatch($pathinfo);
141
+
142
+ return $this->redirect($pathinfo, $ret['_route']) + $ret;
143
+ } catch (ResourceNotFoundException $e2) {
144
+ throw $e;
145
+ }
146
+ }
147
+ }
148
+
149
+ private function doMatch($rawPathinfo)
150
+
151
+ EOF
152
+ .$ code ;
153
+ }
154
+
155
+ return " public function match( \$rawPathinfo) \n" .$ code ;
128
156
}
129
157
130
158
/**
@@ -172,15 +200,9 @@ private function groupStaticRoutes(RouteCollection $collection, bool $supportsRe
172
200
$ compiledRoute = $ route ->compile ();
173
201
$ hostRegex = $ compiledRoute ->getHostRegex ();
174
202
$ regex = $ compiledRoute ->getRegex ();
175
- if ($ hasTrailingSlash = $ supportsRedirections && $ pos = strpos ($ regex , '/$ ' )) {
176
- $ regex = substr_replace ($ regex , '/?$ ' , $ pos , 2 );
177
- }
178
203
if (!$ compiledRoute ->getPathVariables ()) {
179
204
$ host = !$ compiledRoute ->getHostVariables () ? $ route ->getHost () : '' ;
180
205
$ url = $ route ->getPath ();
181
- if ($ hasTrailingSlash ) {
182
- $ url = rtrim ($ url , '/ ' );
183
- }
184
206
foreach ($ dynamicRegex as list ($ hostRx , $ rx )) {
185
207
if (preg_match ($ rx , $ url ) && (!$ host || !$ hostRx || preg_match ($ hostRx , $ host ))) {
186
208
$ dynamicRegex [] = array ($ hostRegex , $ regex );
@@ -189,7 +211,7 @@ private function groupStaticRoutes(RouteCollection $collection, bool $supportsRe
189
211
}
190
212
}
191
213
192
- $ staticRoutes [$ url ][$ name ] = array ( $ hasTrailingSlash , $ route) ;
214
+ $ staticRoutes [$ url ][$ name ] = $ route ;
193
215
} else {
194
216
$ dynamicRegex [] = array ($ hostRegex , $ regex );
195
217
$ dynamicRoutes ->add ($ name , $ route );
@@ -213,54 +235,50 @@ private function compileStaticRoutes(array $staticRoutes, bool $supportsRedirect
213
235
return '' ;
214
236
}
215
237
$ code = $ default = '' ;
216
- $ checkTrailingSlash = false ;
217
238
218
239
foreach ($ staticRoutes as $ url => $ routes ) {
219
240
if (1 === count ($ routes )) {
220
- foreach ($ routes as $ name => list ( $ hasTrailingSlash , $ route) ) {
241
+ foreach ($ routes as $ name => $ route ) {
221
242
}
222
243
223
244
if (!$ route ->getCondition ()) {
224
245
if (!$ supportsRedirections && $ route ->getSchemes ()) {
225
246
throw new \LogicException ('The "schemes" requirement is only supported for URL matchers that implement RedirectableUrlMatcherInterface. ' );
226
247
}
227
- $ checkTrailingSlash = $ checkTrailingSlash || $ hasTrailingSlash ;
228
248
$ default .= sprintf (
229
249
"%s => array(%s, %s, %s, %s), \n" ,
230
250
self ::export ($ url ),
231
251
self ::export (array ('_route ' => $ name ) + $ route ->getDefaults ()),
232
252
self ::export (!$ route ->compile ()->getHostVariables () ? $ route ->getHost () : $ route ->compile ()->getHostRegex () ?: null ),
233
253
self ::export (array_flip ($ route ->getMethods ()) ?: null ),
234
- self ::export (array_flip ($ route ->getSchemes ()) ?: null ).( $ hasTrailingSlash ? ' , true ' : '' )
254
+ self ::export (array_flip ($ route ->getSchemes ()) ?: null )
235
255
);
236
256
continue ;
237
257
}
238
258
}
239
259
240
260
$ code .= sprintf (" case %s: \n" , self ::export ($ url ));
241
- foreach ($ routes as $ name => list ( $ hasTrailingSlash , $ route) ) {
242
- $ code .= $ this ->compileRoute ($ route , $ name , $ supportsRedirections , $ hasTrailingSlash , true );
261
+ foreach ($ routes as $ name => $ route ) {
262
+ $ code .= $ this ->compileRoute ($ route , $ name , $ supportsRedirections , true );
243
263
}
244
264
$ code .= " break; \n" ;
245
265
}
246
266
247
- $ matchedPathinfo = $ supportsRedirections ? '$trimmedPathinfo ' : '$pathinfo ' ;
248
-
249
267
if ($ default ) {
250
268
$ code .= <<<EOF
251
269
default:
252
270
\$routes = array(
253
271
{$ this ->indent ($ default , 4 )} );
254
272
255
- if (!isset( \$routes[ { $ matchedPathinfo } ])) {
273
+ if (!isset( \$routes[ \$ pathinfo ])) {
256
274
break;
257
275
}
258
- list( \$ret, \$requiredHost, \$requiredMethods, \$requiredSchemes) = \$routes[ { $ matchedPathinfo } ];
259
- {$ this ->compileSwitchDefault (false , $ matchedPathinfo , $ matchHost , $ supportsRedirections, $ checkTrailingSlash )}
276
+ list( \$ret, \$requiredHost, \$requiredMethods, \$requiredSchemes) = \$routes[ \$ pathinfo ];
277
+ {$ this ->compileSwitchDefault (false , $ matchHost , $ supportsRedirections )}
260
278
EOF ;
261
279
}
262
280
263
- return sprintf (" switch (%s ) { \n%s } \n\n" , $ matchedPathinfo , $ this ->indent ($ code ));
281
+ return sprintf (" switch ( \$ pathinfo ) { \n%s } \n\n" , $ this ->indent ($ code ));
264
282
}
265
283
266
284
/**
@@ -294,7 +312,6 @@ private function compileDynamicRoutes(RouteCollection $collection, bool $support
294
312
'mark ' => 0 ,
295
313
'markTail ' => 0 ,
296
314
'supportsRedirections ' => $ supportsRedirections ,
297
- 'checkTrailingSlash ' => false ,
298
315
'hostVars ' => array (),
299
316
'vars ' => array (),
300
317
);
@@ -392,7 +409,7 @@ private function compileDynamicRoutes(RouteCollection $collection, bool $support
392
409
{$ this ->indent ($ state ->default , 4 )} );
393
410
394
411
list( \$ret, \$vars, \$requiredMethods, \$requiredSchemes) = \$routes[ \$m];
395
- {$ this ->compileSwitchDefault (true , ' $m ' , $ matchHost , $ supportsRedirections, $ state -> checkTrailingSlash )}
412
+ {$ this ->compileSwitchDefault (true , $ matchHost , $ supportsRedirections )}
396
413
EOF ;
397
414
}
398
415
@@ -448,32 +465,28 @@ private function compileStaticPrefixCollection(StaticPrefixCollection $tree, \st
448
465
449
466
list ($ name , $ regex , $ vars , $ route ) = $ route ;
450
467
$ compiledRoute = $ route ->compile ();
451
- $ hasTrailingSlash = $ state ->supportsRedirections && '' !== $ regex && '/ ' === $ regex [-1 ];
452
468
453
469
if ($ compiledRoute ->getRegex () === $ prevRegex ) {
454
- $ state ->switch = substr_replace ($ state ->switch , $ this ->compileRoute ($ route , $ name , $ state ->supportsRedirections , $ hasTrailingSlash , false )."\n" , -19 , 0 );
470
+ $ state ->switch = substr_replace ($ state ->switch , $ this ->compileRoute ($ route , $ name , $ state ->supportsRedirections , false )."\n" , -19 , 0 );
455
471
continue ;
456
472
}
457
473
458
- $ methods = array_flip ($ route ->getMethods ());
459
- $ hasTrailingSlash = $ hasTrailingSlash && (!$ methods || isset ($ methods ['GET ' ]));
460
- $ state ->mark += 3 + $ state ->markTail + $ hasTrailingSlash + strlen ($ regex ) - $ prefixLen ;
474
+ $ state ->mark += 3 + $ state ->markTail + strlen ($ regex ) - $ prefixLen ;
461
475
$ state ->markTail = 2 + strlen ($ state ->mark );
462
- $ rx = sprintf ('|%s(*:%s) ' , substr ($ regex , $ prefixLen ).( $ hasTrailingSlash ? ' ? ' : '' ) , $ state ->mark );
476
+ $ rx = sprintf ('|%s(*:%s) ' , substr ($ regex , $ prefixLen ), $ state ->mark );
463
477
$ code .= "\n . " .self ::export ($ rx );
464
478
$ state ->regex .= $ rx ;
465
479
$ vars = array_merge ($ state ->hostVars , $ vars );
466
480
467
481
if (!$ route ->getCondition () && (!is_array ($ next = $ routes [1 + $ i ] ?? null ) || $ regex !== $ next [1 ])) {
468
482
$ prevRegex = null ;
469
- $ state ->checkTrailingSlash = $ state ->checkTrailingSlash || $ hasTrailingSlash ;
470
483
$ state ->default .= sprintf (
471
484
"%s => array(%s, %s, %s, %s), \n" ,
472
485
$ state ->mark ,
473
486
self ::export (array ('_route ' => $ name ) + $ route ->getDefaults ()),
474
487
self ::export ($ vars ),
475
- self ::export ($ methods ?: null ),
476
- self ::export (array_flip ($ route ->getSchemes ()) ?: null ).( $ hasTrailingSlash ? ' , true ' : '' )
488
+ self ::export (array_flip ( $ route -> getMethods ()) ?: null ),
489
+ self ::export (array_flip ($ route ->getSchemes ()) ?: null )
477
490
);
478
491
} else {
479
492
$ prevRegex = $ compiledRoute ->getRegex ();
@@ -485,7 +498,7 @@ private function compileStaticPrefixCollection(StaticPrefixCollection $tree, \st
485
498
486
499
$ state ->switch .= <<<EOF
487
500
case {$ state ->mark }:
488
- {$ combine }{$ this ->compileRoute ($ route , $ name , $ state ->supportsRedirections , $ hasTrailingSlash , false )}
501
+ {$ combine }{$ this ->compileRoute ($ route , $ name , $ state ->supportsRedirections , false )}
489
502
break;
490
503
491
504
EOF ;
@@ -498,7 +511,7 @@ private function compileStaticPrefixCollection(StaticPrefixCollection $tree, \st
498
511
/**
499
512
* A simple helper to compiles the switch's "default" for both static and dynamic routes.
500
513
*/
501
- private function compileSwitchDefault (bool $ hasVars , string $ routesKey , bool $ matchHost , bool $ supportsRedirections, bool $ checkTrailingSlash ): string
514
+ private function compileSwitchDefault (bool $ hasVars , bool $ matchHost , bool $ supportsRedirections ): string
502
515
{
503
516
if ($ hasVars ) {
504
517
$ code = <<<EOF
@@ -527,20 +540,6 @@ private function compileSwitchDefault(bool $hasVars, string $routesKey, bool $ma
527
540
} else {
528
541
$ code = '' ;
529
542
}
530
- if ($ supportsRedirections && $ checkTrailingSlash ) {
531
- $ code .= <<<EOF
532
-
533
- if (empty( \$routes[ {$ routesKey }][4]) || '/' === \$pathinfo[-1]) {
534
- // no-op
535
- } elseif ('GET' !== \$canonicalMethod) {
536
- \$allow['GET'] = 'GET';
537
- break;
538
- } else {
539
- return array_replace( \$ret, \$this->redirect( \$rawPathinfo.'/', \$ret['_route']));
540
- }
541
-
542
- EOF ;
543
- }
544
543
if ($ supportsRedirections ) {
545
544
$ code .= <<<EOF
546
545
@@ -574,20 +573,14 @@ private function compileSwitchDefault(bool $hasVars, string $routesKey, bool $ma
574
573
*
575
574
* @throws \LogicException
576
575
*/
577
- private function compileRoute (Route $ route , string $ name , bool $ supportsRedirections , bool $ hasTrailingSlash , bool $ checkHost ): string
576
+ private function compileRoute (Route $ route , string $ name , bool $ supportsRedirections , bool $ checkHost ): string
578
577
{
579
578
$ code = '' ;
580
579
$ compiledRoute = $ route ->compile ();
581
580
$ conditions = array ();
582
581
$ matches = (bool ) $ compiledRoute ->getPathVariables ();
583
582
$ hostMatches = (bool ) $ compiledRoute ->getHostVariables ();
584
583
$ methods = array_flip ($ route ->getMethods ());
585
- $ supportsTrailingSlash = $ supportsRedirections && (!$ methods || isset ($ methods ['GET ' ]));
586
-
587
- if ($ hasTrailingSlash && !$ supportsTrailingSlash ) {
588
- $ hasTrailingSlash = false ;
589
- $ conditions [] = "'/' === \$pathinfo[-1] " ;
590
- }
591
584
592
585
if ($ route ->getCondition ()) {
593
586
$ expression = $ this ->getExpressionLanguage ()->compile ($ route ->getCondition (), array ('context ' , 'request ' ));
@@ -645,21 +638,6 @@ private function compileRoute(Route $route, string $name, bool $supportsRedirect
645
638
$ code .= sprintf (" \$ret = array('_route' => '%s'); \n" , $ name );
646
639
}
647
640
648
- if ($ hasTrailingSlash ) {
649
- $ code .= <<<EOF
650
- if ('/' === \$pathinfo[-1]) {
651
- // no-op
652
- } elseif ('GET' !== \$canonicalMethod) {
653
- \$allow['GET'] = 'GET';
654
- goto $ gotoname;
655
- } else {
656
- return array_replace( \$ret, \$this->redirect( \$rawPathinfo.'/', ' $ name'));
657
- }
658
-
659
-
660
- EOF ;
661
- }
662
-
663
641
if ($ schemes = $ route ->getSchemes ()) {
664
642
if (!$ supportsRedirections ) {
665
643
throw new \LogicException ('The "schemes" requirement is only supported for URL matchers that implement RedirectableUrlMatcherInterface. ' );
@@ -694,18 +672,18 @@ private function compileRoute(Route $route, string $name, bool $supportsRedirect
694
672
EOF ;
695
673
}
696
674
697
- if ($ hasTrailingSlash || $ schemes || $ methods ) {
675
+ if ($ schemes || $ methods ) {
698
676
$ code .= " return \$ret; \n" ;
699
677
} else {
700
678
$ code = substr_replace ($ code , 'return ' , $ retOffset , 6 );
701
679
}
702
680
if ($ conditions ) {
703
681
$ code .= " } \n" ;
704
- } elseif ($ hasTrailingSlash || $ schemes || $ methods ) {
682
+ } elseif ($ schemes || $ methods ) {
705
683
$ code .= ' ' ;
706
684
}
707
685
708
- if ($ hasTrailingSlash || $ schemes || $ methods ) {
686
+ if ($ schemes || $ methods ) {
709
687
$ code .= " $ gotoname: \n" ;
710
688
}
711
689
0 commit comments