Browse Source

Allow disabling urldecoding before route parsing.

Closes #15269
ADmad 4 years ago
parent
commit
818bcc688b
2 changed files with 43 additions and 2 deletions
  1. 9 2
      src/Routing/Route/Route.php
  2. 34 0
      tests/TestCase/Routing/Route/RouteTest.php

+ 9 - 2
src/Routing/Route/Route.php

@@ -128,6 +128,7 @@ class Route
      *   specific host names. You can use `.*` and to create wildcard subdomains/hosts
      *   e.g. `*.example.com` matches all subdomains on `example.com`.
      * - '_port` - Define the port if you want this route to only match specific port number.
+     * - '_urldecode' - Set to `false` to disable URL decoding before route parsing.
      *
      * @param string $template Template string with parameter placeholders
      * @param array $defaults Defaults for the route.
@@ -458,7 +459,12 @@ class Route
         $compiledRoute = $this->compile();
         [$url, $ext] = $this->_parseExtension($url);
 
-        if (!preg_match($compiledRoute, urldecode($url), $route)) {
+        $urldecode = $this->options['_urldecode'] ?? true;
+        if ($urldecode) {
+            $url = urldecode($url);
+        }
+
+        if (!preg_match($compiledRoute, $url, $route)) {
             return null;
         }
 
@@ -575,12 +581,13 @@ class Route
     {
         $pass = [];
         $args = explode('/', $args);
+        $urldecode = $this->options['_urldecode'] ?? true;
 
         foreach ($args as $param) {
             if (empty($param) && $param !== '0') {
                 continue;
             }
-            $pass[] = rawurldecode($param);
+            $pass[] = $urldecode ? rawurldecode($param) : $param;
         }
 
         return $pass;

+ 34 - 0
tests/TestCase/Routing/Route/RouteTest.php

@@ -1578,6 +1578,40 @@ class RouteTest extends TestCase
         $this->assertEquals($expected, $result);
     }
 
+    public function testUrlWithEncodedSlash(): void
+    {
+        $route = new Route(
+            '/products/tests/*',
+            ['controller' => 'Products', 'action' => 'test'],
+            ['_urldecode' => false]
+        );
+
+        $result = $route->parse('/products/tests/xx%2Fyy', 'GET');
+        $expected = [
+            'controller' => 'Products',
+            'action' => 'test',
+            'pass' => ['xx%2Fyy'],
+            '_matchedRoute' => '/products/tests/*',
+        ];
+        $this->assertEquals($expected, $result);
+
+        $route = new Route(
+            '/products/view/{slug}',
+            ['controller' => 'Products', 'action' => 'view'],
+            ['_urldecode' => false]
+        );
+
+        $result = $route->parse('/products/view/xx%2Fyy', 'GET');
+        $expected = [
+            'controller' => 'Products',
+            'action' => 'view',
+            'slug' => 'xx%2Fyy',
+            'pass' => [],
+            '_matchedRoute' => '/products/view/{slug}',
+        ];
+        $this->assertEquals($expected, $result);
+    }
+
     /**
      * Test getting the static path for a route.
      *