From 536a61b890b581d4fbdf7e5b6861658349f9f978 Mon Sep 17 00:00:00 2001
From: webchick <webchick@24967.no-reply.drupal.org>
Date: Thu, 15 Nov 2012 20:03:50 -0800
Subject: [PATCH] Issue #1801570 by effulgentsia, klausi, Crell, disasm: DX:
 Replace hook_route_info() with YAML files and RouteBuildEvent.

---
 core/lib/Drupal/Core/CoreBundle.php           |  4 +-
 .../Drupal/Core/Routing/RouteBuildEvent.php   | 54 +++++++++++++
 core/lib/Drupal/Core/Routing/RouteBuilder.php | 52 ++++++++++--
 .../lib/Drupal/Core/Routing/RoutingEvents.php | 38 +++++++++
 .../rest/EventSubscriber/RouteSubscriber.php  | 79 +++++++++++++++++++
 .../rest/lib/Drupal/rest/RestBundle.php       |  6 ++
 .../rest/lib/Drupal/rest/Tests/DBLogTest.php  | 10 +--
 core/modules/rest/rest.module                 | 29 -------
 .../system/Tests/Routing/RouterTest.php       | 16 ++++
 core/modules/system/system.api.php            | 45 -----------
 .../router_test/RouteTestSubscriber.php       | 59 ++++++++++++++
 .../Drupal/router_test/RouterTestBundle.php   | 24 ++++++
 .../Drupal/router_test/TestControllers.php    |  4 +
 .../modules/router_test/router_test.module    | 33 --------
 .../router_test/router_test.routing.yml       | 25 ++++++
 core/update.php                               |  3 +-
 16 files changed, 358 insertions(+), 123 deletions(-)
 create mode 100644 core/lib/Drupal/Core/Routing/RouteBuildEvent.php
 create mode 100644 core/lib/Drupal/Core/Routing/RoutingEvents.php
 create mode 100644 core/modules/rest/lib/Drupal/rest/EventSubscriber/RouteSubscriber.php
 create mode 100644 core/modules/system/tests/modules/router_test/lib/Drupal/router_test/RouteTestSubscriber.php
 create mode 100644 core/modules/system/tests/modules/router_test/lib/Drupal/router_test/RouterTestBundle.php
 create mode 100644 core/modules/system/tests/modules/router_test/router_test.routing.yml

diff --git a/core/lib/Drupal/Core/CoreBundle.php b/core/lib/Drupal/Core/CoreBundle.php
index bbc4e2e3dfeb..622ce3d818f7 100644
--- a/core/lib/Drupal/Core/CoreBundle.php
+++ b/core/lib/Drupal/Core/CoreBundle.php
@@ -72,7 +72,9 @@ public function build(ContainerBuilder $container) {
       ->addArgument(new Reference('database'));
     $container->register('router.builder', 'Drupal\Core\Routing\RouteBuilder')
       ->addArgument(new Reference('router.dumper'))
-      ->addArgument(new Reference('lock'));
+      ->addArgument(new Reference('lock'))
+      ->addArgument(new Reference('dispatcher'));
+
 
     $container->register('matcher', 'Drupal\Core\Routing\ChainMatcher');
     $container->register('legacy_url_matcher', 'Drupal\Core\LegacyUrlMatcher')
diff --git a/core/lib/Drupal/Core/Routing/RouteBuildEvent.php b/core/lib/Drupal/Core/Routing/RouteBuildEvent.php
new file mode 100644
index 000000000000..017b83a83316
--- /dev/null
+++ b/core/lib/Drupal/Core/Routing/RouteBuildEvent.php
@@ -0,0 +1,54 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Core\Routing\RouteBuildEvent.
+ */
+
+namespace Drupal\Core\Routing;
+
+use Symfony\Component\EventDispatcher\Event;
+use Symfony\Component\Routing\RouteCollection;
+
+/**
+ * Represents route building information as event.
+ */
+class RouteBuildEvent extends Event {
+
+  /**
+   * The route collection.
+   *
+   * @var \Symfony\Component\Routing\RouteCollection
+   */
+  protected $routeCollection;
+
+  /**
+   * The module name that provides the route.
+   *
+   * @var string
+   */
+  protected $module;
+
+  /**
+   * Constructs a RouteBuildEvent object.
+   */
+  public function __construct(RouteCollection $route_collection, $module) {
+    $this->routeCollection = $route_collection;
+    $this->module = $module;
+  }
+
+  /**
+   * Gets the route collection.
+   */
+  public function getRouteCollection() {
+    return $this->routeCollection;
+  }
+
+  /**
+   * Gets the module that provides the route.
+   */
+  public function getModule() {
+    return $this->module;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Routing/RouteBuilder.php b/core/lib/Drupal/Core/Routing/RouteBuilder.php
index 3a27767c5683..fc12ee8f7685 100644
--- a/core/lib/Drupal/Core/Routing/RouteBuilder.php
+++ b/core/lib/Drupal/Core/Routing/RouteBuilder.php
@@ -7,8 +7,13 @@
 
 namespace Drupal\Core\Routing;
 
-use Drupal\Core\Lock\LockBackendInterface;
 use Symfony\Component\Routing\Matcher\Dumper\MatcherDumperInterface;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
+use Symfony\Component\Yaml\Parser;
+use Symfony\Component\Routing\RouteCollection;
+use Symfony\Component\Routing\Route;
+
+use Drupal\Core\Lock\LockBackendInterface;
 
 /**
  * Managing class for rebuilding the router table.
@@ -32,6 +37,13 @@ class RouteBuilder {
    */
   protected $lock;
 
+  /**
+   * The event dispatcher to notify of routes.
+   *
+   * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
+   */
+  protected $dispatcher;
+
   /**
    * Construcs the RouteBuilder using the passed MatcherDumperInterface.
    *
@@ -39,10 +51,13 @@ class RouteBuilder {
    *   The matcher dumper used to store the route information.
    * @param \Drupal\Core\Lock\LockBackendInterface $lock
    *   The lock backend.
+   * @param \Symfony\Component\EventDispatcherEventDispatcherInterface
+   *   The event dispatcher to notify of routes.
    */
-  public function __construct(MatcherDumperInterface $dumper, LockBackendInterface $lock) {
+  public function __construct(MatcherDumperInterface $dumper, LockBackendInterface $lock, EventDispatcherInterface $dispatcher) {
     $this->dumper = $dumper;
     $this->lock = $lock;
+    $this->dispatcher = $dispatcher;
   }
 
   /**
@@ -57,15 +72,38 @@ public function rebuild() {
       return;
     }
 
+    $parser = new Parser();
+
     // We need to manually call each module so that we can know which module
     // a given item came from.
-
-    foreach (module_implements('route_info') as $module) {
-      $routes = call_user_func($module . '_route_info');
-      drupal_alter('router_info', $routes, $module);
-      $this->dumper->addRoutes($routes);
+    // @todo Use an injected Extension service rather than module_list():
+    //   http://drupal.org/node/1331486.
+    foreach (module_list() as $module) {
+      $collection = new RouteCollection();
+      $routing_file = DRUPAL_ROOT . '/' . drupal_get_path('module', $module) . '/' . $module . '.routing.yml';
+      if (file_exists($routing_file)) {
+        $routes = $parser->parse(file_get_contents($routing_file));
+        if (!empty($routes)) {
+          foreach ($routes as $name => $route_info) {
+            $defaults = isset($route_info['defaults']) ? $route_info['defaults'] : array();
+            $requirements = isset($route_info['requirements']) ? $route_info['requirements'] : array();
+            $route = new Route($route_info['pattern'], $defaults, $requirements);
+            $collection->add($name, $route);
+          }
+        }
+      }
+      $this->dispatcher->dispatch(RoutingEvents::ALTER, new RouteBuildEvent($collection, $module));
+      $this->dumper->addRoutes($collection);
       $this->dumper->dump(array('route_set' => $module));
     }
+
+    // Now allow modules to register additional, dynamic routes.
+    $collection = new RouteCollection();
+    $this->dispatcher->dispatch(RoutingEvents::DYNAMIC, new RouteBuildEvent($collection, 'dynamic_routes'));
+    $this->dispatcher->dispatch(RoutingEvents::ALTER, new RouteBuildEvent($collection, 'dynamic_routes'));
+    $this->dumper->addRoutes($collection);
+    $this->dumper->dump(array('route_set' => 'dynamic_routes'));
+
     $this->lock->release('router_rebuild');
   }
 
diff --git a/core/lib/Drupal/Core/Routing/RoutingEvents.php b/core/lib/Drupal/Core/Routing/RoutingEvents.php
new file mode 100644
index 000000000000..3ca6ef6e9fa2
--- /dev/null
+++ b/core/lib/Drupal/Core/Routing/RoutingEvents.php
@@ -0,0 +1,38 @@
+<?php
+
+/**
+ * @file
+ * Definition of \Drupal\Core\Routing\RoutingEvents.
+ */
+
+namespace Drupal\Core\Routing;
+
+/**
+ * Contains all events thrown in the core routing component.
+ */
+final class RoutingEvents {
+
+  /**
+   * The ALTER event is fired on a route collection to allow changes to routes.
+   *
+   * This event is used to process new routes before they get saved.
+   *
+   * @see \Drupal\Core\Routing\RouteBuildEvent
+   *
+   * @var string
+   */
+  const ALTER = 'routing.route_alter';
+
+  /**
+   * The DYNAMIC event is fired to allow modules to register additional routes.
+   *
+   * Most routes are static, an should be defined as such. Dynamic routes are
+   * only those whose existence changes depending on the state of the system
+   * at runtime, depending on configuration.
+   *
+   * @see \Drupal\Core\Routing\RouteBuildEvent
+   *
+   * @var string
+   */
+  const DYNAMIC = 'routing.route_dynamic';
+}
diff --git a/core/modules/rest/lib/Drupal/rest/EventSubscriber/RouteSubscriber.php b/core/modules/rest/lib/Drupal/rest/EventSubscriber/RouteSubscriber.php
new file mode 100644
index 000000000000..f76d535489c8
--- /dev/null
+++ b/core/modules/rest/lib/Drupal/rest/EventSubscriber/RouteSubscriber.php
@@ -0,0 +1,79 @@
+<?php
+
+/**
+ * Definition of \Drupal\rest\EventSubscriber\RouteSubscriber.
+ */
+
+namespace Drupal\rest\EventSubscriber;
+
+use Drupal\Core\Config\ConfigFactory;
+use Drupal\Core\Routing\RouteBuildEvent;
+use Drupal\Core\Routing\RoutingEvents;
+use Drupal\rest\Plugin\Type\ResourcePluginManager;
+
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+/**
+ * Subscriber for REST-style routes.
+ */
+class RouteSubscriber implements EventSubscriberInterface {
+
+  /**
+   * The plugin manager for REST plugins.
+   *
+   * @var \Drupal\rest\Plugin\Type\ResourcePluginManager
+   */
+  protected $manager;
+
+  /**
+   * The Drupal configuration factory.
+   *
+   * @var \Drupal\Core\Config\ConfigFactory
+   */
+  protected $config;
+
+  /**
+   * Constructs a RouteSubscriber object.
+   *
+   * @param \Drupal\rest\Plugin\Type\ResourcePluginManager $manager
+   *   The resource plugin manager.
+   * @param \Drupal\Core\Config\ConfigFactory $config
+   *   The configuration factory holding resource settings.
+   */
+  public function __construct(ResourcePluginManager $manager, ConfigFactory $config) {
+    $this->manager = $manager;
+    $this->config = $config;
+  }
+
+  /**
+   * Adds routes to enabled REST resources.
+   *
+   * @param \Drupal\Core\Routing\RouteBuildEvent $event
+   *   The route building event.
+   */
+  public function dynamicRoutes(RouteBuildEvent $event) {
+
+    $collection = $event->getRouteCollection();
+
+    $resources = $this->config->get('rest')->load()->get('resources');
+    if ($resources && $enabled = array_intersect_key($this->manager->getDefinitions(), $resources)) {
+      foreach ($enabled as $key => $resource) {
+        $plugin = $this->manager->getInstance(array('id' => $key));
+
+        // @todo Switch to ->addCollection() once http://drupal.org/node/1819018 is resolved.
+        foreach ($plugin->routes() as $name => $route) {
+          $collection->add("rest.$name", $route);
+        }
+      }
+    }
+  }
+
+  /**
+   * Implements EventSubscriberInterface::getSubscribedEvents().
+   */
+  static function getSubscribedEvents() {
+    $events[RoutingEvents::DYNAMIC] = 'dynamicRoutes';
+    return $events;
+  }
+}
+
diff --git a/core/modules/rest/lib/Drupal/rest/RestBundle.php b/core/modules/rest/lib/Drupal/rest/RestBundle.php
index 7810c307d8d9..67d3c59a15a4 100644
--- a/core/modules/rest/lib/Drupal/rest/RestBundle.php
+++ b/core/modules/rest/lib/Drupal/rest/RestBundle.php
@@ -8,6 +8,7 @@
 namespace Drupal\rest;
 
 use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Reference;
 use Symfony\Component\HttpKernel\Bundle\Bundle;
 
 /**
@@ -22,5 +23,10 @@ public function build(ContainerBuilder $container) {
     // Register the resource manager class with the dependency injection
     // container.
     $container->register('plugin.manager.rest', 'Drupal\rest\Plugin\Type\ResourcePluginManager');
+
+    $container->register('rest.route_subscriber', 'Drupal\rest\EventSubscriber\RouteSubscriber')
+      ->addArgument(new Reference('plugin.manager.rest'))
+      ->addArgument(new Reference('config.factory'))
+      ->addTag('event_subscriber');
   }
 }
diff --git a/core/modules/rest/lib/Drupal/rest/Tests/DBLogTest.php b/core/modules/rest/lib/Drupal/rest/Tests/DBLogTest.php
index 6bd8e657a890..a18d064a6aa6 100644
--- a/core/modules/rest/lib/Drupal/rest/Tests/DBLogTest.php
+++ b/core/modules/rest/lib/Drupal/rest/Tests/DBLogTest.php
@@ -51,13 +51,9 @@ public function setUp() {
   public function testWatchdog() {
     // Write a log message to the DB.
     watchdog('rest_test', 'Test message');
-    // Get ID of the written message.
-    $result = db_select('watchdog', 'w')
-      ->condition('type', 'rest_test')
-      ->fields('w', array('wid'))
-      ->execute()
-      ->fetchCol();
-    $id = $result[0];
+    // Get the ID of the written message.
+    $id = db_query_range("SELECT wid FROM {watchdog} WHERE type = :type ORDER BY wid DESC", 0, 1, array(':type' => 'rest_test'))
+      ->fetchField();
 
     // Create a user account that has the required permissions to read
     // the watchdog resource via the web API.
diff --git a/core/modules/rest/rest.module b/core/modules/rest/rest.module
index a0208459a834..b5594fe15378 100644
--- a/core/modules/rest/rest.module
+++ b/core/modules/rest/rest.module
@@ -5,9 +5,6 @@
  * RESTful web services module.
  */
 
-use Symfony\Component\Routing\Route;
-use Symfony\Component\Routing\RouteCollection;
-
 /**
  * Implements hook_menu().
  *
@@ -26,32 +23,6 @@ function rest_menu() {
   return $items;
 }
 
-/**
- * Implements hook_route_info().
- */
-function rest_route_info() {
-  $collection = new RouteCollection();
-
-  // This hook is called during module enabling where the manager has not been
-  // registered as service yet.
-  if (drupal_container()->has('plugin.manager.rest')) {
-    $manager = drupal_container()->get('plugin.manager.rest');
-    $resources = config('rest')->get('resources');
-    if ($resources && $enabled = array_intersect_key($manager->getDefinitions(), $resources)) {
-      foreach ($enabled as $key => $resource) {
-        $plugin = $manager->getInstance(array('id' => $key));
-
-        // @todo Switch to ->addCollection() once http://drupal.org/node/1819018 is resolved.
-        foreach ($plugin->routes() as $name => $route) {
-          $collection->add("rest.$name", $route);
-        }
-      }
-    }
-  }
-
-  return $collection;
-}
-
 /**
  * Implements hook_permission().
  */
diff --git a/core/modules/system/lib/Drupal/system/Tests/Routing/RouterTest.php b/core/modules/system/lib/Drupal/system/Tests/Routing/RouterTest.php
index 413737516ac3..e4611354fa9f 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Routing/RouterTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Routing/RouterTest.php
@@ -86,4 +86,20 @@ public function testControllerPlaceholdersDefaultValues() {
     $this->assertNoPattern('#</body>.*</body>#s', 'There was no double-page effect from a misrendered subrequest.');
   }
 
+  /**
+   * Checks that dynamically defined and altered routes work correctly.
+   *
+   * @see \Drupal\router_test\RouteSubscriber
+   */
+  public function testDynamicRoutes() {
+    // Test the dynamically added route.
+    $this->drupalGet('router_test/test5');
+    $this->assertResponse(200);
+    $this->assertRaw('test5', 'The correct string was returned because the route was successful.');
+
+    // Test the altered route.
+    $this->drupalGet('router_test/test6');
+    $this->assertResponse(200);
+    $this->assertRaw('test5', 'The correct string was returned because the route was successful.');
+  }
 }
diff --git a/core/modules/system/system.api.php b/core/modules/system/system.api.php
index d10d3e072a53..e9c086086e73 100644
--- a/core/modules/system/system.api.php
+++ b/core/modules/system/system.api.php
@@ -565,51 +565,6 @@ function hook_menu_get_item_alter(&$router_item, $path, $original_map) {
   }
 }
 
-/**
- * Defines routes in the new router system.
- *
- * A route is a Symfony Route object.  See the Symfony documentation for more
- * details on the available options.  Of specific note:
- *  - _controller: This is the PHP callable that will handle a request matching
- *              the route.
- *  - _content: This is the PHP callable that will handle the body of a request
- *              matching this route.  A default controller will provide the page
- *              rendering around it.
- *
- * Typically you will only specify one or the other of those properties.
- *
- * @deprecated
- *   This mechanism for registering routes is temporary. It will be replaced
- *   by a more robust mechanism in the near future.  It is documented here
- *   only for completeness.
- */
-function hook_route_info() {
-  $collection = new RouteCollection();
-
-  $route = new Route('router_test/test1', array(
-    '_controller' => '\Drupal\router_test\TestControllers::test1'
-  ));
-  $collection->add('router_test_1', $route);
-
-  $route = new Route('router_test/test2', array(
-    '_content' => '\Drupal\router_test\TestControllers::test2'
-  ));
-  $collection->add('router_test_2', $route);
-
-  $route = new Route('router_test/test3/{value}', array(
-    '_content' => '\Drupal\router_test\TestControllers::test3'
-  ));
-  $collection->add('router_test_3', $route);
-
-  $route = new Route('router_test/test4/{value}', array(
-    '_content' => '\Drupal\router_test\TestControllers::test4',
-    'value' => 'narf',
-  ));
-  $collection->add('router_test_4', $route);
-
-  return $collection;
-}
-
 /**
  * Define menu items and page callbacks.
  *
diff --git a/core/modules/system/tests/modules/router_test/lib/Drupal/router_test/RouteTestSubscriber.php b/core/modules/system/tests/modules/router_test/lib/Drupal/router_test/RouteTestSubscriber.php
new file mode 100644
index 000000000000..a67e83f2702d
--- /dev/null
+++ b/core/modules/system/tests/modules/router_test/lib/Drupal/router_test/RouteTestSubscriber.php
@@ -0,0 +1,59 @@
+<?php
+
+/**
+ * Definition of \Drupal\router_test\RouteTestSubscriber.
+ */
+
+namespace Drupal\router_test;
+
+use \Drupal\Core\Routing\RouteBuildEvent;
+use \Drupal\Core\Routing\RoutingEvents;
+use \Symfony\Component\EventDispatcher\EventSubscriberInterface;
+use \Symfony\Component\Routing\Route;
+
+/**
+ * Listens to the dynamic route event and add a test route.
+ */
+class RouteTestSubscriber implements EventSubscriberInterface {
+
+  /**
+   * Implements EventSubscriberInterface::getSubscribedEvents().
+   */
+  static function getSubscribedEvents() {
+    $events[RoutingEvents::DYNAMIC] = 'dynamicRoutes';
+    $events[RoutingEvents::ALTER] = 'alterRoutes';
+    return $events;
+  }
+
+  /**
+   * Adds a dynamic test route.
+   *
+   * @param \Drupal\Core\Routing\RouteBuildEvent $event
+   *   The route building event.
+   */
+  public function dynamicRoutes(RouteBuildEvent $event) {
+    $collection = $event->getRouteCollection();
+    $route = new Route('/router_test/test5', array(
+      '_content' => '\Drupal\router_test\TestControllers::test5'
+    ));
+    $collection->add('router_test_5', $route);
+  }
+
+  /**
+   * Alters an existing test route.
+   *
+   * @param \Drupal\Core\Routing\RouteBuildEvent $event
+   *   The route building event.
+   *
+   * @return \Symfony\Component\Routing\RouteCollection
+   *   The altered route collection.
+   */
+  public function alterRoutes(RouteBuildEvent $event) {
+    if ($event->getModule() == 'router_test') {
+      $collection = $event->getRouteCollection();
+      $route = $collection->get('router_test_6');
+      // Change controller method from test1 to test5.
+      $route->setDefault('_controller', '\Drupal\router_test\TestControllers::test5');
+    }
+  }
+}
diff --git a/core/modules/system/tests/modules/router_test/lib/Drupal/router_test/RouterTestBundle.php b/core/modules/system/tests/modules/router_test/lib/Drupal/router_test/RouterTestBundle.php
new file mode 100644
index 000000000000..f2e123b3d4c2
--- /dev/null
+++ b/core/modules/system/tests/modules/router_test/lib/Drupal/router_test/RouterTestBundle.php
@@ -0,0 +1,24 @@
+<?php
+
+/**
+ * @file
+ * Definition of \Drupal\router_test\RouterTestBundle.
+ */
+
+namespace Drupal\router_test;
+
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\HttpKernel\Bundle\Bundle;
+
+/**
+ * Registers a dynamic route provider.
+ */
+class RouterTestBundle extends Bundle {
+
+  /**
+   * Overrides Symfony\Component\HttpKernel\Bundle\Bundle::build().
+   */
+  public function build(ContainerBuilder $container) {
+    $container->register('router_test.subscriber', 'Drupal\router_test\RouteTestSubscriber')->addTag('event_subscriber');
+  }
+}
diff --git a/core/modules/system/tests/modules/router_test/lib/Drupal/router_test/TestControllers.php b/core/modules/system/tests/modules/router_test/lib/Drupal/router_test/TestControllers.php
index fa92fd89b3e5..bcf18b7b0fdd 100644
--- a/core/modules/system/tests/modules/router_test/lib/Drupal/router_test/TestControllers.php
+++ b/core/modules/system/tests/modules/router_test/lib/Drupal/router_test/TestControllers.php
@@ -30,4 +30,8 @@ public function test4($value) {
     return $value;
   }
 
+  public function test5() {
+    return "test5";
+  }
+
 }
diff --git a/core/modules/system/tests/modules/router_test/router_test.module b/core/modules/system/tests/modules/router_test/router_test.module
index 4da939d70592..b3d9bbc7f371 100644
--- a/core/modules/system/tests/modules/router_test/router_test.module
+++ b/core/modules/system/tests/modules/router_test/router_test.module
@@ -1,34 +1 @@
 <?php
-
-use Symfony\Component\Routing\Route;
-use Symfony\Component\Routing\RouteCollection;
-
-/**
- * Implements hook_router_info().
- */
-function router_test_route_info() {
-  $collection = new RouteCollection();
-
-  $route = new Route('router_test/test1', array(
-    '_controller' => '\Drupal\router_test\TestControllers::test1'
-  ));
-  $collection->add('router_test_1', $route);
-
-  $route = new Route('router_test/test2', array(
-    '_content' => '\Drupal\router_test\TestControllers::test2'
-  ));
-  $collection->add('router_test_2', $route);
-
-  $route = new Route('router_test/test3/{value}', array(
-    '_content' => '\Drupal\router_test\TestControllers::test3'
-  ));
-  $collection->add('router_test_3', $route);
-
-  $route = new Route('router_test/test4/{value}', array(
-    '_content' => '\Drupal\router_test\TestControllers::test4',
-    'value' => 'narf',
-  ));
-  $collection->add('router_test_4', $route);
-
-  return $collection;
-}
diff --git a/core/modules/system/tests/modules/router_test/router_test.routing.yml b/core/modules/system/tests/modules/router_test/router_test.routing.yml
new file mode 100644
index 000000000000..cc177d38b37b
--- /dev/null
+++ b/core/modules/system/tests/modules/router_test/router_test.routing.yml
@@ -0,0 +1,25 @@
+router_test_1:
+  pattern: '/router_test/test1'
+  defaults:
+    _controller: '\Drupal\router_test\TestControllers::test1'
+
+router_test_2:
+  pattern: '/router_test/test2'
+  defaults:
+    _content: '\Drupal\router_test\TestControllers::test2'
+
+router_test_3:
+  pattern: '/router_test/test3/{value}'
+  defaults:
+    _content: '\Drupal\router_test\TestControllers::test3'
+
+router_test_4:
+  pattern: '/router_test/test4/{value}'
+  defaults:
+    _content: '\Drupal\router_test\TestControllers::test4'
+    value: 'narf'
+
+router_test_6:
+  pattern: '/router_test/test6'
+  defaults:
+    _controller: '\Drupal\router_test\TestControllers::test1'
diff --git a/core/update.php b/core/update.php
index 968e8f4e4cf6..98296bbaf81f 100644
--- a/core/update.php
+++ b/core/update.php
@@ -459,7 +459,8 @@ function update_check_requirements($skip_warnings = FALSE) {
   ->addArgument(new Reference('database'));
 $container->register('router.builder', 'Drupal\Core\Routing\RouteBuilder')
   ->addArgument(new Reference('router.dumper'))
-  ->addArgument(new Reference('lock'));
+  ->addArgument(new Reference('lock'))
+  ->addArgument(new Reference('dispatcher'));
 
 // Turn error reporting back on. From now on, only fatal errors (which are
 // not passed through the error handler) will cause a message to be printed.
-- 
GitLab