Chris@0: dumper = $dumper; Chris@0: $this->lock = $lock; Chris@0: $this->dispatcher = $dispatcher; Chris@0: $this->moduleHandler = $module_handler; Chris@0: $this->controllerResolver = $controller_resolver; Chris@0: $this->checkProvider = $check_provider; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function setRebuildNeeded() { Chris@0: $this->rebuildNeeded = TRUE; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function rebuild() { Chris@0: if ($this->building) { Chris@0: throw new \RuntimeException('Recursive router rebuild detected.'); Chris@0: } Chris@0: Chris@0: if (!$this->lock->acquire('router_rebuild')) { Chris@0: // Wait for another request that is already doing this work. Chris@0: // We choose to block here since otherwise the routes might not be Chris@0: // available, resulting in a 404. Chris@0: $this->lock->wait('router_rebuild'); Chris@0: return FALSE; Chris@0: } Chris@0: Chris@0: $this->building = TRUE; Chris@0: Chris@0: $collection = new RouteCollection(); Chris@0: foreach ($this->getRouteDefinitions() as $routes) { Chris@0: // The top-level 'routes_callback' is a list of methods in controller Chris@0: // syntax, see \Drupal\Core\Controller\ControllerResolver. These methods Chris@0: // should return a set of \Symfony\Component\Routing\Route objects, either Chris@0: // in an associative array keyed by the route name, which will be iterated Chris@0: // over and added to the collection for this provider, or as a new Chris@0: // \Symfony\Component\Routing\RouteCollection object, which will be added Chris@0: // to the collection. Chris@0: if (isset($routes['route_callbacks'])) { Chris@0: foreach ($routes['route_callbacks'] as $route_callback) { Chris@0: $callback = $this->controllerResolver->getControllerFromDefinition($route_callback); Chris@0: if ($callback_routes = call_user_func($callback)) { Chris@0: // If a RouteCollection is returned, add the whole collection. Chris@0: if ($callback_routes instanceof RouteCollection) { Chris@0: $collection->addCollection($callback_routes); Chris@0: } Chris@0: // Otherwise, add each Route object individually. Chris@0: else { Chris@0: foreach ($callback_routes as $name => $callback_route) { Chris@0: $collection->add($name, $callback_route); Chris@0: } Chris@0: } Chris@0: } Chris@0: } Chris@0: unset($routes['route_callbacks']); Chris@0: } Chris@0: foreach ($routes as $name => $route_info) { Chris@0: $route_info += [ Chris@0: 'defaults' => [], Chris@0: 'requirements' => [], Chris@0: 'options' => [], Chris@0: 'host' => NULL, Chris@0: 'schemes' => [], Chris@0: 'methods' => [], Chris@0: 'condition' => '', Chris@0: ]; Chris@18: // Ensure routes default to using Drupal's route compiler instead of Chris@18: // Symfony's. Chris@18: $route_info['options'] += [ Chris@18: 'compiler_class' => RouteCompiler::class, Chris@18: ]; Chris@0: Chris@0: $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']); Chris@0: $collection->add($name, $route); Chris@0: } Chris@0: } Chris@0: Chris@0: // DYNAMIC is supposed to be used to add new routes based upon all the Chris@0: // static defined ones. Chris@0: $this->dispatcher->dispatch(RoutingEvents::DYNAMIC, new RouteBuildEvent($collection)); Chris@0: Chris@0: // ALTER is the final step to alter all the existing routes. We cannot stop Chris@0: // people from adding new routes here, but we define two separate steps to Chris@0: // make it clear. Chris@0: $this->dispatcher->dispatch(RoutingEvents::ALTER, new RouteBuildEvent($collection)); Chris@0: Chris@0: $this->checkProvider->setChecks($collection); Chris@0: Chris@0: $this->dumper->addRoutes($collection); Chris@0: $this->dumper->dump(); Chris@0: Chris@0: $this->lock->release('router_rebuild'); Chris@0: $this->dispatcher->dispatch(RoutingEvents::FINISHED, new Event()); Chris@0: $this->building = FALSE; Chris@0: Chris@0: $this->rebuildNeeded = FALSE; Chris@0: Chris@0: return TRUE; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function rebuildIfNeeded() { Chris@0: if ($this->rebuildNeeded) { Chris@0: return $this->rebuild(); Chris@0: } Chris@0: return FALSE; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function destruct() { Chris@0: // Rebuild routes only once at the end of the request lifecycle to not Chris@0: // trigger multiple rebuilds and also make the page more responsive for the Chris@0: // user. Chris@0: $this->rebuildIfNeeded(); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Retrieves all defined routes from .routing.yml files. Chris@0: * Chris@0: * @return array Chris@0: * The defined routes, keyed by provider. Chris@0: */ Chris@0: protected function getRouteDefinitions() { Chris@0: // Always instantiate a new YamlDiscovery object so that we always search on Chris@0: // the up-to-date list of modules. Chris@0: $discovery = new YamlDiscovery('routing', $this->moduleHandler->getModuleDirectories()); Chris@0: return $discovery->findAll(); Chris@0: } Chris@0: Chris@0: }