diff --git a/core/lib/Drupal/Core/Routing/CompiledRoute.php b/core/lib/Drupal/Core/Routing/CompiledRoute.php index 7354e12..e9cb4ce 100644 --- a/core/lib/Drupal/Core/Routing/CompiledRoute.php +++ b/core/lib/Drupal/Core/Routing/CompiledRoute.php @@ -7,7 +7,6 @@ namespace Drupal\Core\Routing; -use Symfony\Component\Routing\Route; use Symfony\Component\Routing\CompiledRoute as SymfonyCompiledRoute; /** @@ -37,13 +36,6 @@ class CompiledRoute extends SymfonyCompiledRoute { protected $numParts; /** - * The Route object of which this object is the compiled version. - * - * @var \Symfony\Component\Routing\Route - */ - protected $route; - - /** * Constructs a new compiled route object. * * This is a ridiculously long set of constructor parameters, but as this @@ -51,8 +43,6 @@ class CompiledRoute extends SymfonyCompiledRoute { * problem. The parent Symfony class does the same, as well, making it * difficult to override differently. * - * @param \Symfony\Component\Routing\Route $route - * A original Route instance. * @param int $fit * The fitness of the route. * @param string $fit @@ -76,10 +66,9 @@ class CompiledRoute extends SymfonyCompiledRoute { * @param array $variables * An array of variables (variables defined in the path and in the host patterns) */ - public function __construct(Route $route, $fit, $pattern_outline, $num_parts, $staticPrefix, $regex, array $tokens, array $pathVariables, $hostRegex = null, array $hostTokens = array(), array $hostVariables = array(), array $variables = array()) { + public function __construct($fit, $pattern_outline, $num_parts, $staticPrefix, $regex, array $tokens, array $pathVariables, $hostRegex = null, array $hostTokens = array(), array $hostVariables = array(), array $variables = array()) { parent::__construct($staticPrefix, $regex, $tokens, $pathVariables, $hostRegex, $hostTokens, $hostVariables, $variables); - $this->route = $route; $this->fit = $fit; $this->patternOutline = $pattern_outline; $this->numParts = $num_parts; @@ -124,26 +113,6 @@ public function getPatternOutline() { } /** - * Returns the Route instance. - * - * @return Route - * A Route instance. - */ - public function getRoute() { - return $this->route; - } - - /** - * Returns the path. - * - * @return string - * The path. - */ - public function getPath() { - return $this->route->getPath(); - } - - /** * Returns the options. * * @return array @@ -177,6 +146,10 @@ public function getRequirements() { * {@inheritdoc} */ public function serialize() { + // At least some versions of PHP 5.4 (e.g. used by the testbot) segfault + // if this call to the parent method is missing. However, in other cases, + // calling the parent method is known to cause a segfault. + // https://bugs.php.net/bug.php?id=65591 $data = unserialize(parent::serialize()); $data['fit'] = $this->fit; $data['patternOutline'] = $this->patternOutline; @@ -188,8 +161,7 @@ public function serialize() { /** * {@inheritdoc} */ - public function unserialize($serialized) - { + public function unserialize($serialized) { parent::unserialize($serialized); $data = unserialize($serialized); diff --git a/core/lib/Drupal/Core/Routing/MatcherDumper.php b/core/lib/Drupal/Core/Routing/MatcherDumper.php index 0b40946..43e3219 100644 --- a/core/lib/Drupal/Core/Routing/MatcherDumper.php +++ b/core/lib/Drupal/Core/Routing/MatcherDumper.php @@ -114,6 +114,7 @@ public function dump(array $options = array()) { foreach ($routes as $name => $route) { /** @var \Symfony\Component\Routing\Route $route */ $route->setOption('compiler_class', '\Drupal\Core\Routing\RouteCompiler'); + /** @var \Drupal\Core\Routing\CompiledRoute $compiled */ $compiled = $route->compile(); // The fit value is a binary number which has 1 at every fixed path // position and 0 where there is a wildcard. We keep track of all such @@ -124,7 +125,7 @@ public function dump(array $options = array()) { $values = array( 'name' => $name, 'fit' => $compiled->getFit(), - 'path' => $compiled->getPath(), + 'path' => $route->getPath(), 'pattern_outline' => $compiled->getPatternOutline(), 'number_parts' => $compiled->getNumParts(), 'route' => serialize($route), diff --git a/core/lib/Drupal/Core/Routing/RouteCompiler.php b/core/lib/Drupal/Core/Routing/RouteCompiler.php index 9e6de2c..5c129cc 100644 --- a/core/lib/Drupal/Core/Routing/RouteCompiler.php +++ b/core/lib/Drupal/Core/Routing/RouteCompiler.php @@ -46,7 +46,6 @@ public static function compile(Route $route) { $num_parts = count(explode('/', trim($pattern_outline, '/'))); return new CompiledRoute( - $route, $fit, $pattern_outline, $num_parts, diff --git a/core/lib/Drupal/Core/Routing/RoutePreloader.php b/core/lib/Drupal/Core/Routing/RoutePreloader.php index 1f223bf..328cc93 100644 --- a/core/lib/Drupal/Core/Routing/RoutePreloader.php +++ b/core/lib/Drupal/Core/Routing/RoutePreloader.php @@ -74,7 +74,7 @@ public function onRequest(KernelEvent $event) { */ protected function loadNonAdminRoutes() { if ($routes = $this->state->get('routing.non_admin_routes', array())) { - $this->routeProvider->getRoutesByNames($routes); + $this->routeProvider->preLoadRoutes($routes); } } diff --git a/core/lib/Drupal/Core/Routing/RouteProvider.php b/core/lib/Drupal/Core/Routing/RouteProvider.php index 1f3e580..c064622 100644 --- a/core/lib/Drupal/Core/Routing/RouteProvider.php +++ b/core/lib/Drupal/Core/Routing/RouteProvider.php @@ -54,6 +54,13 @@ class RouteProvider implements RouteProviderInterface, PagedRouteProviderInterfa protected $routes = array(); /** + * A cache of already-loaded serialized routes, keyed by route name. + * + * @var array + */ + protected $serializedRoutes = []; + + /** * The current path. * * @var \Drupal\Core\Path\CurrentPathStack @@ -132,34 +139,34 @@ public function getRouteByName($name) { } /** - * Find many routes by their names using the provided list of names. - * - * Note that this method may not throw an exception if some of the routes - * are not found. It will just return the list of those routes it found. - * - * This method exists in order to allow performance optimizations. The - * simple implementation could be to just repeatedly call - * $this->getRouteByName(). - * - * @param array $names - * The list of names to retrieve. - * - * @return \Symfony\Component\Routing\Route[] - * Iterable thing with the keys the names of the $names argument. + * {@inheritdoc} */ - public function getRoutesByNames($names) { - + public function preLoadRoutes($names) { if (empty($names)) { throw new \InvalidArgumentException('You must specify the route names to load'); } $routes_to_load = array_diff($names, array_keys($this->routes)); + $routes_to_load = array_diff($routes_to_load, array_keys($this->serializedRoutes)); if ($routes_to_load) { $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(); + $this->serializedRoutes += $routes; + } + } - foreach ($routes as $name => $route) { - $this->routes[$name] = unserialize($route); + /** + * {@inheritdoc} + */ + public function getRoutesByNames($names) { + $this->preLoadRoutes($names); + + foreach ($names as $name) { + // The specified route name might not exist or might be serialized. + if (!isset($this->routes[$name]) && isset($this->serializedRoutes[$name])) { + $this->routes[$name] = unserialize($this->serializedRoutes[$name]); + // Leave the variable set but free up the memory. + $this->serializedRoutes[$name] = ''; } } @@ -292,6 +299,7 @@ public function getAllRoutes() { */ public function reset() { $this->routes = array(); + $this->serializedRoutes = array(); } /** diff --git a/core/lib/Drupal/Core/Routing/RouteProviderInterface.php b/core/lib/Drupal/Core/Routing/RouteProviderInterface.php index 8c8e356..a508b67 100644 --- a/core/lib/Drupal/Core/Routing/RouteProviderInterface.php +++ b/core/lib/Drupal/Core/Routing/RouteProviderInterface.php @@ -18,6 +18,18 @@ interface RouteProviderInterface extends RouteProviderBaseInterface { /** + * Pre-load routes by their names using the provided list of names. + * + * This method exists in order to allow performance optimizations. It allows + * pre-loading serialized routes that may latter be retrieved using + * ::getRoutesByName() + * + * @param array $names + * The list of names to load. + */ + public function preLoadRoutes($names); + + /** * Get all routes which match a certain pattern. * * @param string $pattern diff --git a/core/modules/system/src/Tests/Routing/MockRouteProvider.php b/core/modules/system/src/Tests/Routing/MockRouteProvider.php index dce1b27..eba563f 100644 --- a/core/modules/system/src/Tests/Routing/MockRouteProvider.php +++ b/core/modules/system/src/Tests/Routing/MockRouteProvider.php @@ -57,6 +57,13 @@ public function getRouteByName($name) { } /** + * {@inheritdoc} + */ + public function preLoadRoutes($names) { + // Nothing to do. + } + + /** * Implements \Symfony\Cmf\Component\Routing\RouteProviderInterface::getRoutesByName(). */ public function getRoutesByNames($names) { diff --git a/core/tests/Drupal/Tests/Core/Routing/RouteCompilerTest.php b/core/tests/Drupal/Tests/Core/Routing/RouteCompilerTest.php index 4d1977c..a64cd69 100644 --- a/core/tests/Drupal/Tests/Core/Routing/RouteCompilerTest.php +++ b/core/tests/Drupal/Tests/Core/Routing/RouteCompilerTest.php @@ -62,7 +62,6 @@ public function testCompilation() { $route->setOption('compiler_class', 'Drupal\Core\Routing\RouteCompiler'); $compiled = $route->compile(); - $this->assertEquals($route, $compiled->getRoute(), 'Compiled route has the incorrect route object.'); $this->assertEquals($compiled->getFit(), 5 /* That's 101 binary*/, 'The fit was incorrect.'); $this->assertEquals($compiled->getPatternOutline(), '/test/%/more', 'The pattern outline was not correct.'); } @@ -79,7 +78,6 @@ public function testCompilationDefaultValue() { $route->setOption('compiler_class', 'Drupal\Core\Routing\RouteCompiler'); $compiled = $route->compile(); - $this->assertEquals($route, $compiled->getRoute(), 'Compiled route has an incorrect route object.'); $this->assertEquals($compiled->getFit(), 5 /* That's 101 binary*/, 'The fit was not correct.'); $this->assertEquals($compiled->getPatternOutline(), '/test/%/more', 'The pattern outline was not correct.'); } diff --git a/core/tests/Drupal/Tests/Core/Routing/RoutePreloaderTest.php b/core/tests/Drupal/Tests/Core/Routing/RoutePreloaderTest.php index 5692e6a..7df6259 100644 --- a/core/tests/Drupal/Tests/Core/Routing/RoutePreloaderTest.php +++ b/core/tests/Drupal/Tests/Core/Routing/RoutePreloaderTest.php @@ -153,8 +153,8 @@ public function testOnRequestOnHtml() { ->will($this->returnValue($request)); $this->routeProvider->expects($this->once()) - ->method('getRoutesByNames') - ->with(array('test2')); + ->method('preLoadRoutes') + ->with(['test2']); $this->state->expects($this->once()) ->method('get') ->with('routing.non_admin_routes')