diff -u b/core/core.services.yml b/core/core.services.yml --- b/core/core.services.yml +++ b/core/core.services.yml @@ -268,7 +268,9 @@ - [fromRequest, ['@request']] router.route_provider: class: Drupal\Core\Routing\RouteProvider - arguments: ['@database', 'router', '@router.builder'] + arguments: ['@database', '@router.builder'] + tags: + - { name: event_subscriber } router.matcher.final_matcher: class: Drupal\Core\Routing\UrlMatcher router.matcher: diff -u b/core/includes/menu.inc b/core/includes/menu.inc --- b/core/includes/menu.inc +++ b/core/includes/menu.inc @@ -479,7 +479,7 @@ // occurs rarely, likely due to a race condition of multiple rebuilds. if (\Drupal::state()->get('menu_rebuild_needed') || !\Drupal::state()->get('menu.masks')) { menu_router_rebuild(); - \Drupal::state()->set('router_rebuild_needed', TRUE); + \Drupal::service('router.builder')->setRebuildNeeded(); } $original_map = arg(NULL, $path); diff -u b/core/lib/Drupal/Core/Routing/RouteBuilder.php b/core/lib/Drupal/Core/Routing/RouteBuilder.php --- b/core/lib/Drupal/Core/Routing/RouteBuilder.php +++ b/core/lib/Drupal/Core/Routing/RouteBuilder.php @@ -24,7 +24,7 @@ * Because this class makes use of the modules system, it cannot currently * be unit tested. */ -class RouteBuilder { +class RouteBuilder implements RouteBuilderInterface { /** * The dumper to which we should send collected routes. @@ -69,7 +69,7 @@ protected $controllerResolver; /** - * Construcs the RouteBuilder using the passed MatcherDumperInterface. + * Constructs the RouteBuilder using the passed MatcherDumperInterface. * * @param \Drupal\Core\Routing\MatcherDumperInterface $dumper * The matcher dumper used to store the route information. @@ -92,10 +92,7 @@ } /** - * Rebuilds the route info and dumps to dumper. - * - * @return bool - * Returns TRUE if the rebuild succeeds, FALSE otherwise. + * {@inheritdoc} */ public function rebuild() { if (!$this->lock->acquire('router_rebuild')) { @@ -151,26 +148,30 @@ $this->dumper->addRoutes($collection); $this->dumper->dump(array('provider' => 'dynamic_routes')); - $this->state->delete('router_rebuild_needed'); + $this->state->delete(static::REBUILD_NEEDED); $this->lock->release('router_rebuild'); $this->dispatcher->dispatch(RoutingEvents::FINISHED, new Event()); return TRUE; } /** - * Rebuilds the route info and dumps to dumper if necessary. - * - * @return bool - * Returns TRUE if the rebuild succeeds, FALSE otherwise. + * {@inheritdoc} */ - public function rebuildIfNecessary() { - if ($this->state->get('router_rebuild_needed', FALSE)) { + public function rebuildIfNeeded() { + if ($this->state->get(static::REBUILD_NEEDED, FALSE)) { return $this->rebuild(); } return FALSE; } /** + * {@inheritdoc} + */ + public function setRebuildNeeded() { + $this->state->set(static::REBUILD_NEEDED, TRUE); + } + + /** * Returns the YAML discovery for getting all the .routing.yml files. * * @return \Drupal\Component\Discovery\YamlDiscovery diff -u b/core/lib/Drupal/Core/Routing/RouteProvider.php b/core/lib/Drupal/Core/Routing/RouteProvider.php --- b/core/lib/Drupal/Core/Routing/RouteProvider.php +++ b/core/lib/Drupal/Core/Routing/RouteProvider.php @@ -8,7 +8,7 @@ namespace Drupal\Core\Routing; use Drupal\Component\Utility\String; -use Drupal\Core\KeyValueStore\StateInterface; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\Exception\RouteNotFoundException; use Symfony\Component\Routing\RouteCollection; @@ -19,7 +19,7 @@ /** * A Route Provider front-end for all Drupal-stored routes. */ -class RouteProvider implements RouteProviderInterface { +class RouteProvider implements RouteProviderInterface, EventSubscriberInterface { /** * The database connection from which to read route information. @@ -38,7 +38,7 @@ /** * The route builder. * - * @var \Drupal\Core\Routing\RouteBuilder + * @var \Drupal\Core\Routing\RouteBuilderInterface */ protected $routeBuilder; @@ -54,15 +54,15 @@ * * @param \Drupal\Core\Database\Connection $connection * A database connection object. + * @param \Drupal\Core\Routing\RouteBuilderInterface $route_builder + * The route builder. * @param string $table * The table in the database to use for matching. - * @param \Drupal\Core\Routing\RouteBuilder $route_builder - * The route builder. */ - public function __construct(Connection $connection, $table = 'router', RouteBuilder $route_builder = NULL) { + public function __construct(Connection $connection, RouteBuilderInterface $route_builder, $table = 'router') { $this->connection = $connection; - $this->tableName = $table; $this->routeBuilder = $route_builder; + $this->tableName = $table; } /** @@ -169,7 +169,7 @@ $routes_to_load = array_diff($names, array_keys($this->routes)); if ($routes_to_load) { - $this->routeBuilder->rebuildIfNecessary(); + $this->routeBuilder->rebuildIfNeeded(); $result = $this->connection->query('SELECT name, route FROM {' . $this->connection->escapeTable($this->tableName) . '} WHERE name IN (:names)', array(':names' => $routes_to_load)); $routes = $result->fetchAllKeyed(); @@ -250,7 +250,7 @@ * Returns a route collection of matching routes. */ protected function getRoutesByPath($path) { - $this->routeBuilder->rebuildIfNecessary(); + $this->routeBuilder->rebuildIfNeeded(); // Filter out each empty value, though allow '0' and 0, which would be // filtered out by empty(). $parts = array_values(array_filter(explode('/', $path), function($value) { @@ -281,6 +281,21 @@ public function getAllRoutes() { - $this->routeBuilder->rebuildIfNecessary(); + $this->routeBuilder->rebuildIfNeeded(); return new LazyLoadingRouteCollection($this->connection, $this->tableName); } + /** + * {@inheritdoc} + */ + public function resetStaticCache() { + $this->routes = array(); + } + + /** + * {@inheritdoc} + */ + static function getSubscribedEvents() { + $events[RoutingEvents::FINISHED][] = array('resetStaticCache'); + return $events; + } + } diff -u b/core/modules/views/views.module b/core/modules/views/views.module --- b/core/modules/views/views.module +++ b/core/modules/views/views.module @@ -637,7 +637,7 @@ // Set the menu as needed to be rebuilt. \Drupal::state()->set('menu_rebuild_needed', TRUE); - \Drupal::state()->set('router_rebuild_needed', TRUE); + \Drupal::service('router.builder')->setRebuildNeeded(); $module_handler = \Drupal::moduleHandler(); diff -u b/core/tests/Drupal/Tests/Core/Routing/RouteBuilderTest.php b/core/tests/Drupal/Tests/Core/Routing/RouteBuilderTest.php --- b/core/tests/Drupal/Tests/Core/Routing/RouteBuilderTest.php +++ b/core/tests/Drupal/Tests/Core/Routing/RouteBuilderTest.php @@ -280,6 +280,10 @@ ->with('router_rebuild'); $this->state->expects($this->once()) + ->method('set') + ->with('router_rebuild_needed'); + + $this->state->expects($this->once()) ->method('delete') ->with('router_rebuild_needed'); @@ -292,11 +296,13 @@ ->method('findAll') ->will($this->returnValue(array())); + $this->routeBuilder->setRebuildNeeded(); + // This will trigger a successful rebuild. - $this->assertTrue($this->routeBuilder->rebuildIfNecessary()); + $this->assertTrue($this->routeBuilder->rebuildIfNeeded()); // This will not trigger a rebuild. - $this->assertFalse($this->routeBuilder->rebuildIfNecessary()); + $this->assertFalse($this->routeBuilder->rebuildIfNeeded()); } } only in patch2: unchanged: --- a/core/lib/Drupal/Core/Extension/ThemeHandler.php +++ b/core/lib/Drupal/Core/Extension/ThemeHandler.php @@ -75,7 +75,7 @@ class ThemeHandler implements ThemeHandlerInterface { * * @var \Drupal\Core\Routing\RouteBuilder */ - protected $routerBuilder; + protected $routeBuilder; /** * The system listing info @@ -456,7 +456,7 @@ protected function configInstallDefaultConfig($theme) { */ protected function resetSystem() { if ($this->routeBuilder) { - $this->routeBuilder->rebuild(); + $this->routeBuilder->setRebuildNeeded(); } $this->systemListReset(); only in patch2: unchanged: --- /dev/null +++ b/core/lib/Drupal/Core/Routing/RouteBuilderInterface.php @@ -0,0 +1,35 @@ +container->get('router.builder')->rebuild(); $keys = 'bike shed ' . $this->randomName(); $this->drupalGet("search/dummy_path/{$keys}"); $this->assertText('Dummy search snippet', 'Dummy search snippet is shown'); only in patch2: unchanged: --- a/core/modules/system/lib/Drupal/system/Tests/Routing/RouteProviderTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Routing/RouteProviderTest.php @@ -18,6 +18,7 @@ use Drupal\Core\Database\Database; use Drupal\Core\Routing\MatcherDumper; use Drupal\Tests\Core\Routing\RoutingFixtures; +use Drupal\Tests\Core\Routing\NullRouteBuilder; /** * Basic tests for the RouteProvider. @@ -31,6 +32,13 @@ class RouteProviderTest extends UnitTestBase { */ protected $fixtures; + /** + * A null route builder to enable testing of the route provider. + * + * @var \Drupal\Core\Routing\RouteBuilderInterface + */ + protected $routeBuilder; + public static function getInfo() { return array( 'name' => 'Route Provider tests', @@ -43,6 +51,7 @@ function __construct($test_id = NULL) { parent::__construct($test_id); $this->fixtures = new RoutingFixtures(); + $this->routeBuilder = new NullRouteBuilder(); } public function tearDown() { @@ -57,7 +66,7 @@ public function tearDown() { public function testCandidateOutlines() { $connection = Database::getConnection(); - $provider = new RouteProvider($connection); + $provider = new RouteProvider($connection, $this->routeBuilder); $parts = array('node', '5', 'edit'); @@ -81,7 +90,7 @@ public function testCandidateOutlines() { */ function testExactPathMatch() { $connection = Database::getConnection(); - $provider = new RouteProvider($connection, 'test_routes'); + $provider = new RouteProvider($connection, $this->routeBuilder, 'test_routes'); $this->fixtures->createTables($connection); @@ -105,7 +114,7 @@ function testExactPathMatch() { */ function testOutlinePathMatch() { $connection = Database::getConnection(); - $provider = new RouteProvider($connection, 'test_routes'); + $provider = new RouteProvider($connection, $this->routeBuilder, 'test_routes'); $this->fixtures->createTables($connection); @@ -134,7 +143,7 @@ function testOutlinePathMatch() { */ function testOutlinePathMatchTrailingSlash() { $connection = Database::getConnection(); - $provider = new RouteProvider($connection, 'test_routes'); + $provider = new RouteProvider($connection, $this->routeBuilder, 'test_routes'); $this->fixtures->createTables($connection); @@ -163,7 +172,7 @@ function testOutlinePathMatchTrailingSlash() { */ function testOutlinePathMatchDefaults() { $connection = Database::getConnection(); - $provider = new RouteProvider($connection, 'test_routes'); + $provider = new RouteProvider($connection, $this->routeBuilder, 'test_routes'); $this->fixtures->createTables($connection); @@ -201,7 +210,7 @@ function testOutlinePathMatchDefaults() { */ function testOutlinePathMatchDefaultsCollision() { $connection = Database::getConnection(); - $provider = new RouteProvider($connection, 'test_routes'); + $provider = new RouteProvider($connection, $this->routeBuilder, 'test_routes'); $this->fixtures->createTables($connection); @@ -240,7 +249,7 @@ function testOutlinePathMatchDefaultsCollision() { */ function testOutlinePathMatchDefaultsCollision2() { $connection = Database::getConnection(); - $provider = new RouteProvider($connection, 'test_routes'); + $provider = new RouteProvider($connection, $this->routeBuilder, 'test_routes'); $this->fixtures->createTables($connection); @@ -279,7 +288,7 @@ function testOutlinePathMatchDefaultsCollision2() { */ public function testOutlinePathMatchZero() { $connection = Database::getConnection(); - $provider = new RouteProvider($connection, 'test_routes'); + $provider = new RouteProvider($connection, $this->routeBuilder, 'test_routes'); $this->fixtures->createTables($connection); @@ -314,7 +323,7 @@ public function testOutlinePathMatchZero() { */ function testOutlinePathNoMatch() { $connection = Database::getConnection(); - $provider = new RouteProvider($connection, 'test_routes'); + $provider = new RouteProvider($connection, $this->routeBuilder, 'test_routes'); $this->fixtures->createTables($connection); @@ -343,7 +352,7 @@ function testOutlinePathNoMatch() { */ function testSystemPathMatch() { $connection = Database::getConnection(); - $provider = new RouteProvider($connection, 'test_routes'); + $provider = new RouteProvider($connection, $this->routeBuilder, 'test_routes'); $this->fixtures->createTables($connection); @@ -368,7 +377,7 @@ function testSystemPathMatch() { */ protected function testRouteByName() { $connection = Database::getConnection(); - $provider = new RouteProvider($connection, 'test_routes'); + $provider = new RouteProvider($connection, $this->routeBuilder, 'test_routes'); $this->fixtures->createTables($connection); @@ -403,7 +412,7 @@ protected function testRouteByName() { */ public function testGetRoutesByPatternWithLongPatterns() { $connection = Database::getConnection(); - $provider = new RouteProvider($connection, 'test_routes'); + $provider = new RouteProvider($connection, $this->routeBuilder, 'test_routes'); $this->fixtures->createTables($connection); only in patch2: unchanged: --- a/core/modules/system/system.admin.inc +++ b/core/modules/system/system.admin.inc @@ -211,7 +211,7 @@ function system_theme_default() { // implementations, and doing the variable_set() before the theme_enable() // could result in a race condition where the theme is default but not // enabled. - \Drupal::service('router.builder')->rebuild(); + \Drupal::service('router.builder')->setRebuildNeeded(); menu_router_rebuild(); \Drupal::cache('cache')->deleteTags(array('local_task' => 1)); only in patch2: unchanged: --- /dev/null +++ b/core/tests/Drupal/Tests/Core/Routing/NullRouteBuilder.php @@ -0,0 +1,18 @@ +