Problem/Motivation

As a developer the routing stack is harder to understand since this never to be called \Drupal\Core\Routing\UrlMatcher::finalMatch()

\Drupal\Core\Routing\AccessAwareRouter::__call()

Proposed resolution

Deprecate Drupal\Core\Routing\AccessAwareRouter and fold its functionality into Drupal\Core\Routing\Router to reduce layers of indirection.

Remaining tasks

User interface changes

None

API changes

Drupal\Core\Routing\AccessAwareRouter is deprecated.

Data model changes

None

Comments

pwolanin created an issue. See original summary.

dawehner’s picture

We added __call simply to fetch potential calls we haven't thought about. I think we should totally add function if we forgot about any of them.

Also combine \Drupal\Core\Routing\Router with \Drupal\Core\Routing\UrlMatcher to reduce confusion

That's something which is its own issue totally, IMHO :)

pwolanin’s picture

@dawehner - well the other class only has a single method, so I think this issue is about better DX by removing extra layers of indirection

dawehner’s picture

Assigned: Unassigned » dawehner

It fullfills me with joy to do that

dawehner’s picture

Assigned: dawehner » Unassigned
Status: Active » Needs review

I guess this step is enough for now. To be honest I would love to tag the router class as internal. It is not in anyone's business really to manipulate the router. There are too many implicit assumptions made all over the place.

pwolanin’s picture

Status: Needs review » Active

@dawehner did you forget the patch?

dawehner’s picture

Status: Active » Needs review
StatusFileSize
new11.8 KB

yes

Status: Needs review » Needs work

The last submitted patch, 7: 2849595-5.patch, failed testing.

dawehner’s picture

Status: Needs work » Needs review
StatusFileSize
new4.88 KB
new16.68 KB

Is there really that much value in this unit test?

pwolanin’s picture

+++ b/core/lib/Drupal/Core/Routing/Router.php
@@ -151,7 +186,15 @@ public function matchRequest(Request $request) {
-      return $this->applyRouteEnhancers($ret, $request);
+      $parameters = $this->applyRouteEnhancers($ret, $request);
+      $request->attributes->add($parameters);

Do we know that the route attributes start out empty?

Seems like it's possible the return value is not the same as it used to be when not checking access?

cilefen’s picture

Title: Replace \Drupal\Core\Routing\AccessAwareRouter::__call() with the rela methods » Replace \Drupal\Core\Routing\AccessAwareRouter::__call() with the real methods
dawehner’s picture

Do we know that the route attributes start out empty?

Do we care if they are not empty in the beginning?

pwolanin’s picture

@dawhener - if they don't start out empty, the return value of the non-access-aware router is different before an after the patch (and the 2 routers in general return different results).

So - this might also be a bug fix to make those consistent, I just want to be sure it's intended

dawehner’s picture

+++ /dev/null
@@ -1,146 +0,0 @@
-    $parameters = $this->router->matchRequest($request);
-    $request->attributes->add($parameters);
...
-    return $request->attributes->all();

+++ b/core/lib/Drupal/Core/Routing/Router.php
@@ -151,7 +186,15 @@ public function matchRequest(Request $request) {
+      $parameters = $this->applyRouteEnhancers($ret, $request);
+      $request->attributes->add($parameters);
...
+      return $request->attributes->all();

Isn't still the same code running at the end?

pwolanin’s picture

@dawhner - for the access aware one yes

for the non-access-aware one, it was returning the parameters directly without merging them with the request attributes.

dawehner’s picture

Oh I see, mhh, do you think we should return all attributes?

klausi’s picture

Cool, I like this issue :)

The new methods on the Router class should have unit tests. We should port over that from the removed AccessAwareRouterTest.

pwolanin’s picture

Issue tags: +Needs tests

I think this patch is probably fine since it's now consistent. Should probably verify that with test coverage.

Version: 8.4.x-dev » 8.5.x-dev

Drupal 8.4.0-alpha1 will be released the week of July 31, 2017, which means new developments and disruptive changes should now be targeted against the 8.5.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

dawehner’s picture

Issue tags: -Needs tests
StatusFileSize
new5.27 KB
new17.82 KB

Here are the previous unit tests converted over ...

dawehner’s picture

Oh I also kept a BC layer around for AccessRouter, you never know ...

Status: Needs review » Needs work

The last submitted patch, 20: 2849595-20.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

dawehner’s picture

Status: Needs work » Needs review
StatusFileSize
new22.46 KB
new4.64 KB

I forgot to remove the AccessAwareRouterTest ...

wim leers’s picture

StatusFileSize
new2.18 KB
new23.03 KB
  1. +++ b/core/core.services.yml
    @@ -864,7 +869,7 @@ services:
       router.no_access_checks:
         class: \Drupal\Core\Routing\Router
    -    arguments: ['@router.route_provider', '@path.current', '@url_generator']
    +    arguments: ['@router.route_provider', '@path.current', '@url_generator', '@access_manager', '@current_user', FALSE]
         tags:
           - { name: service_collector, tag: non_lazy_route_enhancer, call: addRouteEnhancer }
           - { name: service_collector, tag: non_lazy_route_filter, call: addRouteFilter }
    

    Can't we simplify this even further, to inherit as much as possible from router? That'd make the relation clearer.

  2. +++ b/core/lib/Drupal/Core/Routing/AccessAwareRouter.php
    @@ -4,143 +4,28 @@
    + * @deprecated in Drupal 8.3.x and will be removed before Drupal 9.0.0.
    

    8.5.x by now.

  3. +++ b/core/tests/Drupal/Tests/Core/Routing/RouterTest.php
    @@ -0,0 +1,159 @@
    +  ¶
    

    Stupid whitespace nit here.

Addressed all my feedback. Thoughts?

dawehner’s picture

Priority: Normal » Major

Can't we simplify this even further, to inherit as much as possible from router? That'd make the relation clearer.

Oh wow, I did not knew this is actually possible.

I think this should be major given what #2883680-69: Force all route filters and route enhancers to be non-lazy argued abotu.

wim leers’s picture

So you liked #24.1? If so, I'd be happy to RTBC. Or perhaps we need @tim.plunkett to RTBC?

dawehner’s picture

I guess its another case where we need a subsystem maintainer review?

wim leers’s picture

Yep, I think so!

tim.plunkett’s picture

This patch looks great, and is a nice cleanup.

  1. +++ b/core/core.services.yml
    @@ -864,12 +869,13 @@ services:
    +    parent: router
    +    arguments:
    +      index_5: FALSE
    

    Wow, that is interesting. The brittleness of index_5 is worrisome, I hope we don't regret this later.

  2. +++ b/core/lib/Drupal/Core/Routing/AccessAwareRouter.php
    @@ -4,143 +4,28 @@
    -    $request->attributes->add($parameters);
    -    $this->checkAccess($request);
    -    // We can not return $parameters because the access check can change the
    -    // request attributes.
    -    return $request->attributes->all();
    
    +++ b/core/lib/Drupal/Core/Routing/Router.php
    @@ -151,7 +186,15 @@ public function matchRequest(Request $request) {
    +      $request->attributes->add($parameters);
    ...
    +        $this->checkAccess($request);
    ...
    +      return $request->attributes->all();
    

    Can we keep this comment? It is odd that we don't return $parameters, so we should document why we alter the request at all.

  3. +++ b/core/lib/Drupal/Core/Routing/Router.php
    @@ -151,7 +186,15 @@ public function matchRequest(Request $request) {
    +      $request->attributes->add($parameters);
    +
    +      // Checking access here. ::checkAccess might throw an exception, when
    +      // access is not granted.
    +      if ($this->checkAccess) {
    +        $this->checkAccess($request);
    +      }
    +      return $request->attributes->all();
    

    In theory we should put all of this within the if(), and return $parameters when $this->checkAccess is false.
    Though WTF I'd really like to see access checks stop expecting that they can manipulate the request directly...

  4. +++ b/core/lib/Drupal/Core/Routing/Router.php
    @@ -374,6 +417,29 @@ protected function sortFilters() {
    +   * Apply access check service to the route and parameters in the request.
    

    nit: s/Apply/Applies

Very close to RTBC!

dawehner’s picture

StatusFileSize
new22.76 KB
new867 bytes

Thank you tim!

Wow, that is interesting. The brittleness of index_5 is worrisome, I hope we don't regret this later.

I hope we never swap the order of parameters ... given that this would fuck up other routers, of there are probably zero.

Can we keep this comment? It is odd that we don't return $parameters, so we should document why we alter the request at all.

Nice observation

In theory we should put all of this within the if(), and return $parameters when $this->checkAccess is false.
Though WTF I'd really like to see access checks stop expecting that they can manipulate the request directly...

If things aren't changed it doesn't make a difference anyway, right?

tim.plunkett’s picture

Status: Needs review » Reviewed & tested by the community

Surely no one would ever change the arguments to the router... #2883680: Force all route filters and route enhancers to be non-lazy
😆

You are right, it doesn't matter, since the calling code (\Symfony\Component\HttpKernel\EventListener\RouterListener) immediately calls

$request->attributes->add()

on this return value.

dawehner’s picture

👏

wim leers’s picture

🎉

alexpott’s picture

Status: Reviewed & tested by the community » Needs work
Issue tags: +Needs change record
  1. +++ b/core/lib/Drupal/Core/Routing/AccessAwareRouter.php
    @@ -4,143 +4,28 @@
     /**
    - * A router class for Drupal with access check and upcasting.
    + * @deprecated in Drupal 8.5.x and will be removed before Drupal 9.0.0.
    + * Use Drupal\Core\Routing\Router with $check_access in the constructor instead.
      */
    

    This should be linked to the CR no? Which I think we need.

  2. +++ b/core/lib/Drupal/Core/Routing/AccessAwareRouter.php
    @@ -4,143 +4,28 @@
    +  public function __construct(RouteProviderInterface $route_provider, CurrentPathStack $current_path, UrlGeneratorInterface $url_generator, AccessManagerInterface $access_manager, AccountInterface $account, $check_access = TRUE) {
    +    parent::__construct($route_provider, $current_path, $url_generator, $access_manager, $account, $check_access);
    

    Trigger a deprecation notice in the constructor?

dawehner’s picture

Status: Needs work » Needs review
Issue tags: -Needs change record
StatusFileSize
new23.06 KB
new1.2 KB

Sure, let's do that.

borisson_’s picture

Status: Needs review » Reviewed & tested by the community

#35 correctly fixes the remarks in #34, back to RTBC.

dawehner’s picture

Issue summary: View changes
alexpott’s picture

Issue summary: View changes

Updating the issue summary to reflect the final patch.

alexpott’s picture

Status: Reviewed & tested by the community » Needs review

I wonder what we should be doing with \Drupal\Core\Routing\AccessAwareRouterInterface? Should that be deprecated too?

Also do we have a hard API break if the router service no longer implements \Drupal\Core\Routing\AccessAwareRouterInterface?

alexpott’s picture

So part of #39 is incorrect. The patch changes \Drupal\Core\Routing\Router to implement AccessAwareRouterInterface. But this brings another question - what does it mean that a router can have the AccessAwareRouterInterface but not check access?

dawehner’s picture

Mhh, this is an interesting question.

We could keep the interface around as some form of documentation.
Instead of the making it possible to enable the access checking via constructor argument, we could continue having the AccessAwareRouter around, but now it extends the base router and sets the property on the object to be TRUE by default.

This way we just change the internal details of the code, but don't touch anything visible from the outside.

What do people think about this approach?

dawehner’s picture

StatusFileSize
new21.48 KB
new4.89 KB

Here is the implementation of #41

Status: Needs review » Needs work

The last submitted patch, 42: 2849595-42.patch, failed testing. View results

tim.plunkett’s picture

Status: Needs work » Needs review
StatusFileSize
new13.57 KB
new13.88 KB

If we go down this path, we should follow it to the logical conclusion: move all thought of access from Router back to AccessAwareRouter.

This undoes some of the original patch, but retains the key change to AccessAwareRouter: subclass over decorator.

tim.plunkett’s picture

Then this really boils down to "Drupal loves inheritance, or, decorators in PHP are not ideal".
And to be honest, none of this yet addresses the first line of the IS, about UrlMatcher::finalMatch no longer being called.

To actually accomplish that, we'd need this change too

diff --git a/core/lib/Drupal/Core/Routing/Router.php b/core/lib/Drupal/Core/Routing/Router.php
index e949c5efb8..afb50c9c50 100644
--- a/core/lib/Drupal/Core/Routing/Router.php
+++ b/core/lib/Drupal/Core/Routing/Router.php
@@ -6,14 +6,17 @@
 use Symfony\Cmf\Component\Routing\Enhancer\RouteEnhancerInterface as BaseRouteEnhancerInterface;
 use Symfony\Cmf\Component\Routing\LazyRouteCollection;
 use Symfony\Cmf\Component\Routing\NestedMatcher\RouteFilterInterface as BaseRouteFilterInterface;
+use Symfony\Cmf\Component\Routing\RouteObjectInterface;
 use Symfony\Cmf\Component\Routing\RouteProviderInterface as BaseRouteProviderInterface;
 use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\Routing\Exception\MethodNotAllowedException;
 use Symfony\Component\Routing\Exception\ResourceNotFoundException;
 use Symfony\Component\Routing\Generator\UrlGeneratorInterface as BaseUrlGeneratorInterface;
 use Symfony\Component\Routing\Matcher\RequestMatcherInterface;
+use Symfony\Component\Routing\Route;
 use Symfony\Component\Routing\RouteCollection;
 use Symfony\Component\Routing\RouterInterface;
+use Symfony\Component\Routing\Matcher\UrlMatcher as BaseUrlMatcher;
 
 /**
  * Router implementation in Drupal.
@@ -34,14 +37,19 @@
  *
  * This implementation uses ideas of the following routers:
  * - \Symfony\Cmf\Component\Routing\DynamicRouter
- * - \Drupal\Core\Routing\UrlMatcher
  * - \Symfony\Cmf\Component\Routing\NestedMatcher\NestedMatcher
  *
  * @see \Symfony\Cmf\Component\Routing\DynamicRouter
- * @see \Drupal\Core\Routing\UrlMatcher
  * @see \Symfony\Cmf\Component\Routing\NestedMatcher\NestedMatcher
  */
-class Router extends UrlMatcher implements RequestMatcherInterface, RouterInterface {
+class Router extends BaseUrlMatcher implements RequestMatcherInterface, RouterInterface {
+
+  /**
+   * The current path.
+   *
+   * @var \Drupal\Core\Path\CurrentPathStack
+   */
+  protected $currentPath;
 
   /**
    * The route provider responsible for the first-pass match.
@@ -96,7 +104,7 @@ class Router extends UrlMatcher implements RequestMatcherInterface, RouterInterf
    *   The URL generator.
    */
   public function __construct(BaseRouteProviderInterface $route_provider, CurrentPathStack $current_path, BaseUrlGeneratorInterface $url_generator) {
-    parent::__construct($current_path);
+    $this->currentPath = $current_path;
     $this->routeProvider = $route_provider;
     $this->urlGenerator = $url_generator;
   }
@@ -249,6 +257,19 @@ protected function doMatchCollection($pathinfo, RouteCollection $routes, $case_s
     }
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  protected function getAttributes(Route $route, $name, array $attributes) {
+    if ($route instanceof RouteObjectInterface && is_string($route->getRouteKey())) {
+      $name = $route->getRouteKey();
+    }
+    $attributes[RouteObjectInterface::ROUTE_NAME] = $name;
+    $attributes[RouteObjectInterface::ROUTE_OBJECT] = $route;
+
+    return $this->mergeDefaults($attributes, $route->getDefaults());
+  }
+
   /**
    * Returns a collection of potential matching routes for a request.
    *
diff --git a/core/lib/Drupal/Core/Routing/UrlMatcher.php b/core/lib/Drupal/Core/Routing/UrlMatcher.php
index 9056903368..4ec8706947 100644
--- a/core/lib/Drupal/Core/Routing/UrlMatcher.php
+++ b/core/lib/Drupal/Core/Routing/UrlMatcher.php
@@ -2,6 +2,8 @@
 
 namespace Drupal\Core\Routing;
 
+@trigger_error('\Drupal\Core\Routing\UrlMatcher is deprecated in Drupal 8.5.0 and will be removed before Drupal 9.0.0. Instead, use \Drupal\Core\Routing\Router', E_USER_DEPRECATED);
+
 use Drupal\Core\Path\CurrentPathStack;
 use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\Routing\RouteCollection;

which starts to overlap with #2917331: Decouple from Symfony CMF

dawehner’s picture

Mhh, yeah its a good question whether this makes things still easier to understand or whether its basically refactoring on the spot.

dawehner’s picture

So how should we proceed here?

Version: 8.5.x-dev » 8.6.x-dev

Drupal 8.5.0-alpha1 will be released the week of January 17, 2018, which means new developments and disruptive changes should now be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.6.x-dev » 8.7.x-dev

Drupal 8.6.0-alpha1 will be released the week of July 16, 2018, which means new developments and disruptive changes should now be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.7.x-dev » 8.8.x-dev

Drupal 8.7.0-alpha1 will be released the week of March 11, 2019, which means new developments and disruptive changes should now be targeted against the 8.8.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

jibran’s picture

Status: Needs review » Needs work
Issue tags: +Needs reroll
yogeshmpawar’s picture

Assigned: Unassigned » yogeshmpawar
yogeshmpawar’s picture

Assigned: yogeshmpawar » Unassigned
Status: Needs work » Needs review
Issue tags: -Needs reroll
StatusFileSize
new13.74 KB

Rerolled the patch #44

Status: Needs review » Needs work

The last submitted patch, 53: 2849595-53.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.0-alpha1 will be released the week of October 14th, 2019, which means new developments and disruptive changes should now be targeted against the 8.9.x-dev branch. (Any changes to 8.9.x will also be committed to 9.0.x in preparation for Drupal 9’s release, but some changes like significant feature additions will be deferred to 9.1.x.). For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 8.9.x-dev » 9.1.x-dev

Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 9.1.x-dev » 9.2.x-dev

Drupal 9.1.0-alpha1 will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule and the Allowed changes during the Drupal 9 release cycle.

andypost’s picture

Issue tags: +Needs reroll
suresh prabhu parkala’s picture

Status: Needs work » Needs review
StatusFileSize
new13.76 KB

Re-roll against the latest 9.2.x.

andypost’s picture

Status: Needs review » Needs work
Issue tags: -Needs reroll

NW for failed pre checks

ayushmishra206’s picture

Assigned: Unassigned » ayushmishra206
ayushmishra206’s picture

Assigned: ayushmishra206 » Unassigned
Status: Needs work » Needs review
StatusFileSize
new13.73 KB
new600 bytes

Fixed the error in #59. please review.

andypost’s picture

Status: Needs review » Needs work

Now something is wrong again

Version: 9.2.x-dev » 9.3.x-dev

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.5.x-dev » 10.1.x-dev

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

andypost’s picture

Status: Needs work » Needs review
StatusFileSize
new13.5 KB

re-roll for 10.1 and fix tests with $this->route->setOption('compiler_class', RouteCompiler::class);

andypost’s picture

andypost’s picture

smustgrave’s picture

Status: Needs review » Needs work
Issue tags: +Needs Review Queue Initiative

The issue summary talks about deprecating AccessAwareRouter but I don't see a deprecated call?

andypost’s picture

Issue summary: View changes
Status: Needs work » Needs review
StatusFileSize
new879 bytes
new13.9 KB

Here's reverted removal with deprecation, could use test but methods now should be defined

Maybe it should be no-op instead of attempt to call missing method?

andypost’s picture

Status: Needs review » Needs work
StatusFileSize
new755 bytes
new13.89 KB

It needs work for constructor changes (BC) and not sure we need to keep _call()

+++ b/core/core.services.yml
@@ -1015,7 +1015,11 @@ services:
+      - { name: service_collector, tag: non_lazy_route_enhancer, call: addRouteEnhancer }
+      - { name: service_collector, tag: non_lazy_route_filter, call: addRouteFilter }

patch replaced as deprecated https://www.drupal.org/node/2894934

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 11.x-dev » main

Drupal core is now using the main branch as the primary development branch. New developments and disruptive changes should now be targeted to the main branch.

Read more in the announcement.