You are here

class RouteBuilder in Drupal 9

Same name in this branch
  1. 9 core/lib/Drupal/Core/Routing/RouteBuilder.php \Drupal\Core\Routing\RouteBuilder
  2. 9 core/lib/Drupal/Core/ProxyClass/Routing/RouteBuilder.php \Drupal\Core\ProxyClass\Routing\RouteBuilder
Same name and namespace in other branches
  1. 8 core/lib/Drupal/Core/Routing/RouteBuilder.php \Drupal\Core\Routing\RouteBuilder

Managing class for rebuilding the router table.

Hierarchy

Expanded class hierarchy of RouteBuilder

1 file declares its use of RouteBuilder
InstallerRouteBuilder.php in core/lib/Drupal/Core/Installer/InstallerRouteBuilder.php
1 string reference to 'RouteBuilder'
core.services.yml in core/core.services.yml
core/core.services.yml
1 service uses RouteBuilder
router.builder in core/core.services.yml
Drupal\Core\Routing\RouteBuilder

File

core/lib/Drupal/Core/Routing/RouteBuilder.php, line 19

Namespace

Drupal\Core\Routing
View source
class RouteBuilder implements RouteBuilderInterface, DestructableInterface {

  /**
   * The dumper to which we should send collected routes.
   *
   * @var \Drupal\Core\Routing\MatcherDumperInterface
   */
  protected $dumper;

  /**
   * The used lock backend instance.
   *
   * @var \Drupal\Core\Lock\LockBackendInterface
   */
  protected $lock;

  /**
   * The event dispatcher to notify of routes.
   *
   * @var \Symfony\Contracts\EventDispatcher\EventDispatcherInterface
   */
  protected $dispatcher;

  /**
   * The module handler.
   *
   * @var \Drupal\Core\Extension\ModuleHandlerInterface
   */
  protected $moduleHandler;

  /**
   * The controller resolver.
   *
   * @var \Drupal\Core\Controller\ControllerResolverInterface
   */
  protected $controllerResolver;

  /**
   * The route collection during the rebuild.
   *
   * @var \Symfony\Component\Routing\RouteCollection
   */
  protected $routeCollection;

  /**
   * Flag that indicates if we are currently rebuilding the routes.
   *
   * @var bool
   */
  protected $building = FALSE;

  /**
   * Flag that indicates if we should rebuild at the end of the request.
   *
   * @var bool
   */
  protected $rebuildNeeded = FALSE;

  /**
   * The check provider.
   *
   * @var \Drupal\Core\Access\CheckProviderInterface
   */
  protected $checkProvider;

  /**
   * Constructs the RouteBuilder using the passed MatcherDumperInterface.
   *
   * @param \Drupal\Core\Routing\MatcherDumperInterface $dumper
   *   The matcher dumper used to store the route information.
   * @param \Drupal\Core\Lock\LockBackendInterface $lock
   *   The lock backend.
   * @param \Symfony\Contracts\EventDispatcher\EventDispatcherInterface $dispatcher
   *   The event dispatcher to notify of routes.
   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
   *   The module handler.
   * @param \Drupal\Core\Controller\ControllerResolverInterface $controller_resolver
   *   The controller resolver.
   * @param \Drupal\Core\Access\CheckProviderInterface $check_provider
   *   The check provider.
   */
  public function __construct(MatcherDumperInterface $dumper, LockBackendInterface $lock, EventDispatcherInterface $dispatcher, ModuleHandlerInterface $module_handler, ControllerResolverInterface $controller_resolver, CheckProviderInterface $check_provider) {
    $this->dumper = $dumper;
    $this->lock = $lock;
    $this->dispatcher = $dispatcher;
    $this->moduleHandler = $module_handler;
    $this->controllerResolver = $controller_resolver;
    $this->checkProvider = $check_provider;
  }

  /**
   * {@inheritdoc}
   */
  public function setRebuildNeeded() {
    $this->rebuildNeeded = TRUE;
  }

  /**
   * {@inheritdoc}
   */
  public function rebuild() {
    if ($this->building) {
      throw new \RuntimeException('Recursive router rebuild detected.');
    }
    if (!$this->lock
      ->acquire('router_rebuild')) {

      // Wait for another request that is already doing this work.
      // We choose to block here since otherwise the routes might not be
      // available, resulting in a 404.
      $this->lock
        ->wait('router_rebuild');
      return FALSE;
    }
    $this->building = TRUE;
    $collection = new RouteCollection();
    foreach ($this
      ->getRouteDefinitions() as $routes) {

      // The top-level 'routes_callback' is a list of methods in controller
      // syntax, see \Drupal\Core\Controller\ControllerResolver. These methods
      // should return a set of \Symfony\Component\Routing\Route objects, either
      // in an associative array keyed by the route name, which will be iterated
      // over and added to the collection for this provider, or as a new
      // \Symfony\Component\Routing\RouteCollection object, which will be added
      // to the collection.
      if (isset($routes['route_callbacks'])) {
        foreach ($routes['route_callbacks'] as $route_callback) {
          $callback = $this->controllerResolver
            ->getControllerFromDefinition($route_callback);
          if ($callback_routes = call_user_func($callback)) {

            // If a RouteCollection is returned, add the whole collection.
            if ($callback_routes instanceof RouteCollection) {
              $collection
                ->addCollection($callback_routes);
            }
            else {
              foreach ($callback_routes as $name => $callback_route) {
                $collection
                  ->add($name, $callback_route);
              }
            }
          }
        }
        unset($routes['route_callbacks']);
      }
      foreach ($routes as $name => $route_info) {
        $route_info += [
          'defaults' => [],
          'requirements' => [],
          'options' => [],
          'host' => NULL,
          'schemes' => [],
          'methods' => [],
          'condition' => '',
        ];

        // Ensure routes default to using Drupal's route compiler instead of
        // Symfony's.
        $route_info['options'] += [
          'compiler_class' => RouteCompiler::class,
        ];
        $route = new Route($route_info['path'], $route_info['defaults'], $route_info['requirements'], $route_info['options'], $route_info['host'], $route_info['schemes'], $route_info['methods'], $route_info['condition']);
        $collection
          ->add($name, $route);
      }
    }

    // DYNAMIC is supposed to be used to add new routes based upon all the
    // static defined ones.
    $this->dispatcher
      ->dispatch(new RouteBuildEvent($collection), RoutingEvents::DYNAMIC);

    // ALTER is the final step to alter all the existing routes. We cannot stop
    // people from adding new routes here, but we define two separate steps to
    // make it clear.
    $this->dispatcher
      ->dispatch(new RouteBuildEvent($collection), RoutingEvents::ALTER);
    $this->checkProvider
      ->setChecks($collection);
    $this->dumper
      ->addRoutes($collection);
    $this->dumper
      ->dump();
    $this->lock
      ->release('router_rebuild');
    $this->dispatcher
      ->dispatch(new Event(), RoutingEvents::FINISHED);
    $this->building = FALSE;
    $this->rebuildNeeded = FALSE;
    return TRUE;
  }

  /**
   * {@inheritdoc}
   */
  public function rebuildIfNeeded() {
    if ($this->rebuildNeeded) {
      return $this
        ->rebuild();
    }
    return FALSE;
  }

  /**
   * {@inheritdoc}
   */
  public function destruct() {

    // Rebuild routes only once at the end of the request lifecycle to not
    // trigger multiple rebuilds and also make the page more responsive for the
    // user.
    $this
      ->rebuildIfNeeded();
  }

  /**
   * Retrieves all defined routes from .routing.yml files.
   *
   * @return array
   *   The defined routes, keyed by provider.
   */
  protected function getRouteDefinitions() {

    // Always instantiate a new YamlDiscovery object so that we always search on
    // the up-to-date list of modules.
    $discovery = new YamlDiscovery('routing', $this->moduleHandler
      ->getModuleDirectories());
    return $discovery
      ->findAll();
  }

}

Members

Namesort descending Modifiers Type Description Overrides
RouteBuilder::$building protected property Flag that indicates if we are currently rebuilding the routes.
RouteBuilder::$checkProvider protected property The check provider.
RouteBuilder::$controllerResolver protected property The controller resolver.
RouteBuilder::$dispatcher protected property The event dispatcher to notify of routes.
RouteBuilder::$dumper protected property The dumper to which we should send collected routes.
RouteBuilder::$lock protected property The used lock backend instance.
RouteBuilder::$moduleHandler protected property The module handler.
RouteBuilder::$rebuildNeeded protected property Flag that indicates if we should rebuild at the end of the request.
RouteBuilder::$routeCollection protected property The route collection during the rebuild.
RouteBuilder::destruct public function Performs destruct operations. Overrides DestructableInterface::destruct
RouteBuilder::getRouteDefinitions protected function Retrieves all defined routes from .routing.yml files. 1
RouteBuilder::rebuild public function Rebuilds the route information and dumps it. Overrides RouteBuilderInterface::rebuild
RouteBuilder::rebuildIfNeeded public function Rebuilds the route information if necessary, and dumps it. Overrides RouteBuilderInterface::rebuildIfNeeded
RouteBuilder::setRebuildNeeded public function Sets the router to be rebuilt next time rebuildIfNeeded() is called. Overrides RouteBuilderInterface::setRebuildNeeded
RouteBuilder::__construct public function Constructs the RouteBuilder using the passed MatcherDumperInterface.