From 8e86885af9bdcd9f96a289eea0840fd4c63840b7 Mon Sep 17 00:00:00 2001
From: Ilias Dimopoulos <idimopoulos@hotmail.com>
Date: Thu, 18 Mar 2021 22:35:44 +0200
Subject: [PATCH 1/3] Reroll patch from 8.9.x to 9.2.x

---
 .../lib/Drupal/Core/Routing/RouteCompiler.php | 19 ++++++++
 .../lib/Drupal/Core/Routing/RouteProvider.php |  3 +-
 .../Core/Routing/RouteProviderTest.php        | 46 +++++++++++++++++++
 3 files changed, 67 insertions(+), 1 deletion(-)

diff --git a/core/lib/Drupal/Core/Routing/RouteCompiler.php b/core/lib/Drupal/Core/Routing/RouteCompiler.php
index a3dfae6fefc..3316134c5a8 100644
--- a/core/lib/Drupal/Core/Routing/RouteCompiler.php
+++ b/core/lib/Drupal/Core/Routing/RouteCompiler.php
@@ -11,6 +11,11 @@
  */
 class RouteCompiler extends SymfonyRouteCompiler implements RouteCompilerInterface {

+  /**
+   * Flag the path as having unlimited parts.
+   */
+  const UNLIMITED_PARTS = -1;
+
   /**
    * Compiles the current route instance.
    *
@@ -39,6 +44,20 @@ public static function compile(Route $route) {
     // allows the RouteProvider to filter candidate routes more efficiently.
     $num_parts = count(explode('/', trim($route->getPath(), '/')));

+    $unlimited_requirements = array_filter($route->getRequirements(), function ($it, $key) use ($stripped_path) {
+      if($it !== '.*' && $it !== '.+') {
+        return false;
+      }
+
+      $needle = "{{$key}}";
+      // Only the last parameter can be set to include '/' and only if the path ends with this parameter.
+      return substr_compare($stripped_path, $needle, -strlen($needle)) === 0;
+    }, ARRAY_FILTER_USE_BOTH);
+
+    if (count($unlimited_requirements) > 0) {
+      $num_parts = static::UNLIMITED_PARTS;
+    }
+
     return new CompiledRoute(
       $fit,
       $pattern_outline,
diff --git a/core/lib/Drupal/Core/Routing/RouteProvider.php b/core/lib/Drupal/Core/Routing/RouteProvider.php
index 534670f8de8..f3a4a13fd03 100644
--- a/core/lib/Drupal/Core/Routing/RouteProvider.php
+++ b/core/lib/Drupal/Core/Routing/RouteProvider.php
@@ -362,9 +362,10 @@ protected function getRoutesByPath($path) {
     // trailing wildcard parts as long as the pattern matches, since we
     // dump the route pattern without those optional parts.
     try {
-      $routes = $this->connection->query("SELECT [name], [route], [fit] FROM {" . $this->connection->escapeTable($this->tableName) . "} WHERE [pattern_outline] IN ( :patterns[] ) AND [number_parts] >= :count_parts", [
+      $routes = $this->connection->query("SELECT [name], [route], [fit] FROM {" . $this->connection->escapeTable($this->tableName) . "} WHERE [pattern_outline] IN ( :patterns[] ) AND ([number_parts] >= :count_parts OR [number_parts] = :unlimited_parts)", [
         ':patterns[]' => $ancestors,
         ':count_parts' => count($parts),
+        ':unlimited_parts' => RouteCompiler::UNLIMITED_PARTS,
       ])
         ->fetchAll(\PDO::FETCH_ASSOC);
     }
diff --git a/core/tests/Drupal/KernelTests/Core/Routing/RouteProviderTest.php b/core/tests/Drupal/KernelTests/Core/Routing/RouteProviderTest.php
index 29eb7578062..3fc6e3f1e94 100644
--- a/core/tests/Drupal/KernelTests/Core/Routing/RouteProviderTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Routing/RouteProviderTest.php
@@ -779,6 +779,52 @@ public function testGetRoutesCount() {
     $this->assertEqual(5, $provider->getRoutesCount());
   }

+  /**
+    * Tests the possibility to pass a forward slash as part of the parameters.
+    *
+    * @dataProvider providerTestParameterInfiniteParts
+    */
+   public function testParameterInfiniteParts(string $route_path, string $parameter, string $route_requirement, string $request_path, int $expected_routes) {
+     $connection = Database::getConnection();
+     $provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, $this->cacheTagsInvalidator, 'test_routes');
+
+     $this->fixtures->createTables($connection);
+     $dumper = new MatcherDumper($connection, $this->state, 'test_routes');
+     $collection = new RouteCollection();
+     $route = new Route($route_path);
+     $route->setRequirement($parameter, $route_requirement);
+     $collection->add('unlimited_parts', $route);
+     $dumper->addRoutes($collection);
+     $dumper->dump();
+     $request = Request::create($request_path, 'GET');
+     $provider->getRouteCollectionForRequest($request);
+     $cache = $this->cache->get("route:[language]=en:{$request_path}:");
+     $this->assertNotEmpty($cache);
+     $route_collection = $cache->data['routes'];
+     $this->assertEquals($route_collection->count(), $expected_routes);
+  }
+
+  /**
+   * Provides data for RouteCompilerTest::testParameterInfiniteParts()
+   *
+   * @return array
+   *   An array of test cases, each containing the route path, the parameter with a requirement, the requirement, the
+   *   path that is requested, and the expected routes that match the request.
+   */
+  public function providerTestParameterInfiniteParts() {
+    return [
+      ['/some/param/{with_unlimited_parts}', 'with_unlimited_parts', '.*', '/some/param/hello/world', 1],
+      ['/some/param/{with_unlimited_parts}', 'with_unlimited_parts', '.+', '/some/param/hello/world', 1],
+      ['/some/param/{no_infinite_cardinality}', 'no_infinite_cardinality', '\d+', '/some/param/hello/world', 0],
+      ['/some/{param}/{no_infinite_cardinality}', 'no_infinite_cardinality', '\d+', '/some/param/hello/world', 0],
+      // Only the latest parameter can be entitled in containing forward slash.
+      ['/some/{param}/{with_unlimited_parts}', 'param', '.*', '/some/param/hello/world', 0],
+      // Not supported.
+      ['/some/{param}', 'param', '[dor/]+', '/some/dor/dro', 0],
+      ['/some/{param}', 'random_var', '.+', '/some/dor/dro', 0],
+    ];
+  }
+
 }

 class TestRouteProvider extends RouteProvider {
--
GitLab


From 268c2e3ca53c8b9c790f23108a6e41e2fc484533 Mon Sep 17 00:00:00 2001
From: Ilias Dimopoulos <idimopoulos@hotmail.com>
Date: Thu, 18 Mar 2021 22:41:27 +0200
Subject: [PATCH 2/3] Fix codesniffs.

---
 core/lib/Drupal/Core/Routing/RouteCompiler.php | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/core/lib/Drupal/Core/Routing/RouteCompiler.php b/core/lib/Drupal/Core/Routing/RouteCompiler.php
index 3316134c5a8..3065e69b4db 100644
--- a/core/lib/Drupal/Core/Routing/RouteCompiler.php
+++ b/core/lib/Drupal/Core/Routing/RouteCompiler.php
@@ -45,8 +45,8 @@ public static function compile(Route $route) {
     $num_parts = count(explode('/', trim($route->getPath(), '/')));

     $unlimited_requirements = array_filter($route->getRequirements(), function ($it, $key) use ($stripped_path) {
-      if($it !== '.*' && $it !== '.+') {
-        return false;
+      if ($it !== '.*' && $it !== '.+') {
+        return FALSE;
       }

       $needle = "{{$key}}";
--
GitLab


From 5e91aae8cac46a56ce08bcad89989f94909ae57a Mon Sep 17 00:00:00 2001
From: Ilias Dimopoulos <idimopoulos@hotmail.com>
Date: Thu, 18 Mar 2021 23:20:01 +0200
Subject: [PATCH 3/3] Fix more codesniffs.

---
 .../Core/Routing/RouteProviderTest.php        | 44 +++++++++----------
 1 file changed, 22 insertions(+), 22 deletions(-)

diff --git a/core/tests/Drupal/KernelTests/Core/Routing/RouteProviderTest.php b/core/tests/Drupal/KernelTests/Core/Routing/RouteProviderTest.php
index 3fc6e3f1e94..9e0b5d40aa6 100644
--- a/core/tests/Drupal/KernelTests/Core/Routing/RouteProviderTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Routing/RouteProviderTest.php
@@ -780,28 +780,28 @@ public function testGetRoutesCount() {
   }

   /**
-    * Tests the possibility to pass a forward slash as part of the parameters.
-    *
-    * @dataProvider providerTestParameterInfiniteParts
-    */
-   public function testParameterInfiniteParts(string $route_path, string $parameter, string $route_requirement, string $request_path, int $expected_routes) {
-     $connection = Database::getConnection();
-     $provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, $this->cacheTagsInvalidator, 'test_routes');
-
-     $this->fixtures->createTables($connection);
-     $dumper = new MatcherDumper($connection, $this->state, 'test_routes');
-     $collection = new RouteCollection();
-     $route = new Route($route_path);
-     $route->setRequirement($parameter, $route_requirement);
-     $collection->add('unlimited_parts', $route);
-     $dumper->addRoutes($collection);
-     $dumper->dump();
-     $request = Request::create($request_path, 'GET');
-     $provider->getRouteCollectionForRequest($request);
-     $cache = $this->cache->get("route:[language]=en:{$request_path}:");
-     $this->assertNotEmpty($cache);
-     $route_collection = $cache->data['routes'];
-     $this->assertEquals($route_collection->count(), $expected_routes);
+   * Tests the possibility to pass a forward slash as part of the parameters.
+   *
+   * @dataProvider providerTestParameterInfiniteParts
+   */
+  public function testParameterInfiniteParts(string $route_path, string $parameter, string $route_requirement, string $request_path, int $expected_routes) {
+    $connection = Database::getConnection();
+    $provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, $this->cacheTagsInvalidator, 'test_routes');
+
+    $this->fixtures->createTables($connection);
+    $dumper = new MatcherDumper($connection, $this->state, 'test_routes');
+    $collection = new RouteCollection();
+    $route = new Route($route_path);
+    $route->setRequirement($parameter, $route_requirement);
+    $collection->add('unlimited_parts', $route);
+    $dumper->addRoutes($collection);
+    $dumper->dump();
+    $request = Request::create($request_path, 'GET');
+    $provider->getRouteCollectionForRequest($request);
+    $cache = $this->cache->get("route:[language]=en:{$request_path}:");
+    $this->assertNotEmpty($cache);
+    $route_collection = $cache->data['routes'];
+    $this->assertEquals($route_collection->count(), $expected_routes);
   }

   /**
--
GitLab

