You are here

public function DefaultMenuLinkTreeManipulators::checkAccess in Drupal 9

Same name and namespace in other branches
  1. 8 core/lib/Drupal/Core/Menu/DefaultMenuLinkTreeManipulators.php \Drupal\Core\Menu\DefaultMenuLinkTreeManipulators::checkAccess()

Performs access checks of a menu tree.

Sets the 'access' property to AccessResultInterface objects on menu link tree elements. Descends into subtrees if the root of the subtree is accessible. Inaccessible subtrees are deleted, except the top-level inaccessible link, to be compatible with render caching.

(This means that top-level inaccessible links are *not* removed; it is up to the code doing something with the tree to exclude inaccessible links, just like MenuLinkTree::build() does. This allows those things to specify the necessary cacheability metadata.)

This is compatible with render caching, because of cache context bubbling: conditionally defined cache contexts (i.e. subtrees that are only accessible to some users) will bubble just like they do for render arrays. This is why inaccessible subtrees are deleted, except at the top-level inaccessible link: if we didn't keep the first (depth-wise) inaccessible link, we wouldn't be able to know which cache contexts would cause those subtrees to become accessible again, thus forcing us to conclude that the subtree is unconditionally inaccessible.

Parameters

\Drupal\Core\Menu\MenuLinkTreeElement[] $tree: The menu link tree to manipulate.

Return value

\Drupal\Core\Menu\MenuLinkTreeElement[] The manipulated menu link tree.

File

core/lib/Drupal/Core/Menu/DefaultMenuLinkTreeManipulators.php, line 87

Class

DefaultMenuLinkTreeManipulators
Provides a couple of menu link tree manipulators.

Namespace

Drupal\Core\Menu

Code

public function checkAccess(array $tree) {
  foreach ($tree as $key => $element) {

    // Other menu tree manipulators may already have calculated access, do not
    // overwrite the existing value in that case.
    if (!isset($element->access)) {
      $tree[$key]->access = $this
        ->menuLinkCheckAccess($element->link);
    }
    if ($tree[$key]->access
      ->isAllowed()) {
      if ($tree[$key]->subtree) {
        $tree[$key]->subtree = $this
          ->checkAccess($tree[$key]->subtree);
      }
    }
    else {

      // Replace the link with an InaccessibleMenuLink object, so that if it
      // is accidentally rendered, no sensitive information is divulged.
      $tree[$key]->link = new InaccessibleMenuLink($tree[$key]->link);

      // Always keep top-level inaccessible links: their cacheability metadata
      // that indicates why they're not accessible by the current user must be
      // bubbled. Otherwise, those subtrees will not be varied by any cache
      // contexts at all, therefore forcing them to remain empty for all users
      // unless some other part of the menu link tree accidentally varies by
      // the same cache contexts.
      // For deeper levels, we *can* remove the subtrees and therefore also
      // not perform access checking on the subtree, thanks to bubbling/cache
      // redirects. This therefore allows us to still do significantly less
      // work in case of inaccessible subtrees, which is the entire reason why
      // this deletes subtrees in the first place.
      $tree[$key]->subtree = [];
    }
  }
  return $tree;
}