Updated to Drupal 8.5. Core Media not yet in use.
[yaffs-website] / web / core / lib / Drupal / Core / Routing / RouteBuilder.php
1 <?php
2
3 namespace Drupal\Core\Routing;
4
5 use Drupal\Core\Access\CheckProviderInterface;
6 use Drupal\Core\Controller\ControllerResolverInterface;
7 use Drupal\Core\Discovery\YamlDiscovery;
8 use Drupal\Core\Extension\ModuleHandlerInterface;
9 use Drupal\Core\Lock\LockBackendInterface;
10 use Drupal\Core\DestructableInterface;
11 use Symfony\Component\EventDispatcher\Event;
12 use Symfony\Component\EventDispatcher\EventDispatcherInterface;
13 use Symfony\Component\Routing\RouteCollection;
14 use Symfony\Component\Routing\Route;
15
16 /**
17  * Managing class for rebuilding the router table.
18  */
19 class RouteBuilder implements RouteBuilderInterface, DestructableInterface {
20
21   /**
22    * The dumper to which we should send collected routes.
23    *
24    * @var \Drupal\Core\Routing\MatcherDumperInterface
25    */
26   protected $dumper;
27
28   /**
29    * The used lock backend instance.
30    *
31    * @var \Drupal\Core\Lock\LockBackendInterface
32    */
33   protected $lock;
34
35   /**
36    * The event dispatcher to notify of routes.
37    *
38    * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
39    */
40   protected $dispatcher;
41
42   /**
43    * The module handler.
44    *
45    * @var \Drupal\Core\Extension\ModuleHandlerInterface
46    */
47   protected $moduleHandler;
48
49   /**
50    * The controller resolver.
51    *
52    * @var \Drupal\Core\Controller\ControllerResolverInterface
53    */
54   protected $controllerResolver;
55
56   /**
57    * The route collection during the rebuild.
58    *
59    * @var \Symfony\Component\Routing\RouteCollection
60    */
61   protected $routeCollection;
62
63   /**
64    * Flag that indicates if we are currently rebuilding the routes.
65    *
66    * @var bool
67    */
68   protected $building = FALSE;
69
70   /**
71    * Flag that indicates if we should rebuild at the end of the request.
72    *
73    * @var bool
74    */
75   protected $rebuildNeeded = FALSE;
76
77   /**
78    * The check provider.
79    *
80    * @var \Drupal\Core\Access\CheckProviderInterface
81    */
82   protected $checkProvider;
83
84   /**
85    * Constructs the RouteBuilder using the passed MatcherDumperInterface.
86    *
87    * @param \Drupal\Core\Routing\MatcherDumperInterface $dumper
88    *   The matcher dumper used to store the route information.
89    * @param \Drupal\Core\Lock\LockBackendInterface $lock
90    *   The lock backend.
91    * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher
92    *   The event dispatcher to notify of routes.
93    * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
94    *   The module handler.
95    * @param \Drupal\Core\Controller\ControllerResolverInterface $controller_resolver
96    *   The controller resolver.
97    * @param \Drupal\Core\Access\CheckProviderInterface $check_provider
98    *   The check provider.
99    */
100   public function __construct(MatcherDumperInterface $dumper, LockBackendInterface $lock, EventDispatcherInterface $dispatcher, ModuleHandlerInterface $module_handler, ControllerResolverInterface $controller_resolver, CheckProviderInterface $check_provider) {
101     $this->dumper = $dumper;
102     $this->lock = $lock;
103     $this->dispatcher = $dispatcher;
104     $this->moduleHandler = $module_handler;
105     $this->controllerResolver = $controller_resolver;
106     $this->checkProvider = $check_provider;
107   }
108
109   /**
110    * {@inheritdoc}
111    */
112   public function setRebuildNeeded() {
113     $this->rebuildNeeded = TRUE;
114   }
115
116   /**
117    * {@inheritdoc}
118    */
119   public function rebuild() {
120     if ($this->building) {
121       throw new \RuntimeException('Recursive router rebuild detected.');
122     }
123
124     if (!$this->lock->acquire('router_rebuild')) {
125       // Wait for another request that is already doing this work.
126       // We choose to block here since otherwise the routes might not be
127       // available, resulting in a 404.
128       $this->lock->wait('router_rebuild');
129       return FALSE;
130     }
131
132     $this->building = TRUE;
133
134     $collection = new RouteCollection();
135     foreach ($this->getRouteDefinitions() as $routes) {
136       // The top-level 'routes_callback' is a list of methods in controller
137       // syntax, see \Drupal\Core\Controller\ControllerResolver. These methods
138       // should return a set of \Symfony\Component\Routing\Route objects, either
139       // in an associative array keyed by the route name, which will be iterated
140       // over and added to the collection for this provider, or as a new
141       // \Symfony\Component\Routing\RouteCollection object, which will be added
142       // to the collection.
143       if (isset($routes['route_callbacks'])) {
144         foreach ($routes['route_callbacks'] as $route_callback) {
145           $callback = $this->controllerResolver->getControllerFromDefinition($route_callback);
146           if ($callback_routes = call_user_func($callback)) {
147             // If a RouteCollection is returned, add the whole collection.
148             if ($callback_routes instanceof RouteCollection) {
149               $collection->addCollection($callback_routes);
150             }
151             // Otherwise, add each Route object individually.
152             else {
153               foreach ($callback_routes as $name => $callback_route) {
154                 $collection->add($name, $callback_route);
155               }
156             }
157           }
158         }
159         unset($routes['route_callbacks']);
160       }
161       foreach ($routes as $name => $route_info) {
162         $route_info += [
163           'defaults' => [],
164           'requirements' => [],
165           'options' => [],
166           'host' => NULL,
167           'schemes' => [],
168           'methods' => [],
169           'condition' => '',
170         ];
171
172         $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']);
173         $collection->add($name, $route);
174       }
175     }
176
177     // DYNAMIC is supposed to be used to add new routes based upon all the
178     // static defined ones.
179     $this->dispatcher->dispatch(RoutingEvents::DYNAMIC, new RouteBuildEvent($collection));
180
181     // ALTER is the final step to alter all the existing routes. We cannot stop
182     // people from adding new routes here, but we define two separate steps to
183     // make it clear.
184     $this->dispatcher->dispatch(RoutingEvents::ALTER, new RouteBuildEvent($collection));
185
186     $this->checkProvider->setChecks($collection);
187
188     $this->dumper->addRoutes($collection);
189     $this->dumper->dump();
190
191     $this->lock->release('router_rebuild');
192     $this->dispatcher->dispatch(RoutingEvents::FINISHED, new Event());
193     $this->building = FALSE;
194
195     $this->rebuildNeeded = FALSE;
196
197     return TRUE;
198   }
199
200   /**
201    * {@inheritdoc}
202    */
203   public function rebuildIfNeeded() {
204     if ($this->rebuildNeeded) {
205       return $this->rebuild();
206     }
207     return FALSE;
208   }
209
210   /**
211    * {@inheritdoc}
212    */
213   public function destruct() {
214     // Rebuild routes only once at the end of the request lifecycle to not
215     // trigger multiple rebuilds and also make the page more responsive for the
216     // user.
217     $this->rebuildIfNeeded();
218   }
219
220   /**
221    * Retrieves all defined routes from .routing.yml files.
222    *
223    * @return array
224    *   The defined routes, keyed by provider.
225    */
226   protected function getRouteDefinitions() {
227     // Always instantiate a new YamlDiscovery object so that we always search on
228     // the up-to-date list of modules.
229     $discovery = new YamlDiscovery('routing', $this->moduleHandler->getModuleDirectories());
230     return $discovery->findAll();
231   }
232
233 }