comparison core/lib/Drupal/Core/Routing/RouteBuilder.php @ 0:4c8ae668cc8c

Initial import (non-working)
author Chris Cannam
date Wed, 29 Nov 2017 16:09:58 +0000
parents
children af1871eacc83
comparison
equal deleted inserted replaced
-1:000000000000 0:4c8ae668cc8c
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 }