diff --git a/core/core.services.yml b/core/core.services.yml index 598754e10b4a596efe225095a4b006765d8dd3c3..81cdb36aefbe22abfb16f3482cb529458a88e9c0 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -806,6 +806,8 @@ services: router.route_provider.lazy_builder: class: Drupal\Core\Routing\RouteProviderLazyBuilder arguments: ['@router.route_provider', '@router.builder'] + tags: + - { name: event_subscriber } router.route_preloader: class: Drupal\Core\Routing\RoutePreloader arguments: ['@router.route_provider', '@state', '@cache.bootstrap'] diff --git a/core/lib/Drupal/Core/Routing/RouteProviderLazyBuilder.php b/core/lib/Drupal/Core/Routing/RouteProviderLazyBuilder.php index 8c59900c82ff38b5f9deed5f97b50ffafd63e922..a2cb8c57851330c28545bd05fa136b6f36d52bf1 100644 --- a/core/lib/Drupal/Core/Routing/RouteProviderLazyBuilder.php +++ b/core/lib/Drupal/Core/Routing/RouteProviderLazyBuilder.php @@ -3,12 +3,13 @@ namespace Drupal\Core\Routing; use Symfony\Cmf\Component\Routing\PagedRouteProviderInterface; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpFoundation\Request; /** * A Route Provider front-end for all Drupal-stored routes. */ -class RouteProviderLazyBuilder implements PreloadableRouteProviderInterface, PagedRouteProviderInterface { +class RouteProviderLazyBuilder implements PreloadableRouteProviderInterface, PagedRouteProviderInterface, EventSubscriberInterface { /** * The route provider service. @@ -31,6 +32,18 @@ class RouteProviderLazyBuilder implements PreloadableRouteProviderInterface, Pag */ protected $rebuilt = FALSE; + /** + * Flag to determine if router is currently being rebuilt. + * + * Used to prevent recursive router rebuilds during module installation. + * Recursive rebuilds can occur when route information is required by alter + * hooks that are triggered during a rebuild, for example, + * hook_menu_links_discovered_alter(). + * + * @var bool + */ + protected $rebuilding = FALSE; + /** * RouteProviderLazyBuilder constructor. * @@ -51,7 +64,7 @@ public function __construct(RouteProviderInterface $route_provider, RouteBuilder * The route provider service. */ protected function getRouteProvider() { - if (!$this->rebuilt) { + if (!$this->rebuilt && !$this->rebuilding) { $this->routeBuilder->rebuild(); $this->rebuilt = TRUE; } @@ -132,4 +145,27 @@ public function hasRebuilt() { return $this->rebuilt; } + /** + * {@inheritdoc} + */ + public static function getSubscribedEvents() { + $events[RoutingEvents::DYNAMIC][] = ['routerRebuilding', 3000]; + $events[RoutingEvents::FINISHED][] = ['routerRebuildFinished', -3000]; + return $events; + } + + /** + * Sets the router rebuilding flag to TRUE. + */ + public function routerRebuilding() { + $this->rebuilding = TRUE; + } + + /** + * Sets the router rebuilding flag to FALSE. + */ + public function routerRebuildFinished() { + $this->rebuilding = FALSE; + } + } diff --git a/core/modules/system/tests/modules/lazy_route_provider_install_test/lazy_route_provider_install_test.module b/core/modules/system/tests/modules/lazy_route_provider_install_test/lazy_route_provider_install_test.module new file mode 100644 index 0000000000000000000000000000000000000000..7989d765e4e6f77c485bc09bfea69d5097774594 --- /dev/null +++ b/core/modules/system/tests/modules/lazy_route_provider_install_test/lazy_route_provider_install_test.module @@ -0,0 +1,21 @@ +<?php + +/** + * @file + * Helper module for the lazy route provider tests. + */ + +/** + * Implements hook_menu_links_discovered_alter(). + */ +function lazy_route_provider_install_test_menu_links_discovered_alter(&$links) { + $message = \Drupal::state()->get(__FUNCTION__, 'success'); + try { + // Ensure that calling this does not cause a recursive rebuild. + \Drupal::service('router.route_provider')->getAllRoutes(); + } + catch (\RuntimeException $e) { + $message = 'failed'; + } + \Drupal::state()->set(__FUNCTION__, $message); +} diff --git a/core/tests/Drupal/FunctionalTests/Routing/LazyRouteProviderInstallTest.php b/core/tests/Drupal/FunctionalTests/Routing/LazyRouteProviderInstallTest.php index bc01dc288a51fbf78dbbb3d64db8b02d8c9c2660..cec35a07acc0b7627795ea1f7853f6128461e70f 100644 --- a/core/tests/Drupal/FunctionalTests/Routing/LazyRouteProviderInstallTest.php +++ b/core/tests/Drupal/FunctionalTests/Routing/LazyRouteProviderInstallTest.php @@ -23,6 +23,11 @@ public function testInstallation() { // we cannot use ::assertEquals(). $this->assertStringEndsWith('/admin', \Drupal::state()->get('Drupal\lazy_route_provider_install_test\PluginManager')); $this->assertStringEndsWith('/router_test/test1', \Drupal::state()->get('router_test_install')); + // If there is an exception thrown in rebuilding a route then the state + // 'lazy_route_provider_install_test_menu_links_discovered_alter' will be + // set. + // @see lazy_route_provider_install_test_menu_links_discovered_alter(). + $this->assertEquals('success', \Drupal::state()->get('lazy_route_provider_install_test_menu_links_discovered_alter', NULL)); } }