Commit 1cbbff15 authored by catch's avatar catch

Issue #2883680 by dawehner, Wim Leers, tim.plunkett, larowlan: Force all route...

Issue #2883680 by dawehner, Wim Leers, tim.plunkett, larowlan: Force all route filters and route enhancers to be non-lazy
parent 51f6344c
......@@ -823,16 +823,6 @@ services:
tags:
- { name: service_collector, tag: non_lazy_route_filter, call: addRouteFilter }
deprecated: The "%service_id%" service is deprecated. You should use the 'router.no_access_checks' service instead.
route_filter.lazy_collector:
class: Drupal\Core\Routing\LazyRouteFilter
tags:
- { name: non_lazy_route_filter }
parent: container.trait
route_filter_subscriber:
class: Drupal\Core\EventSubscriber\RouteFilterSubscriber
arguments: ['@route_filter.lazy_collector']
tags:
- { name: event_subscriber }
url_generator.non_bubbling:
class: Drupal\Core\Routing\UrlGenerator
arguments: ['@router.route_provider', '@path_processor_manager', '@route_processor_manager', '@request_stack', '%filter_protocols%']
......@@ -866,8 +856,11 @@ services:
class: \Drupal\Core\Routing\Router
arguments: ['@router.route_provider', '@path.current', '@url_generator']
tags:
# @todo Try to combine those tags together, see https://www.drupal.org/node/2915772.
- { name: service_collector, tag: non_lazy_route_enhancer, call: addRouteEnhancer }
- { name: service_collector, tag: route_enhancer, call: addRouteEnhancer }
- { name: service_collector, tag: non_lazy_route_filter, call: addRouteFilter }
- { name: service_collector, tag: route_filter, call: addRouteFilter }
calls:
- [setContext, ['@router.request_context']]
router.path_roots_subscriber:
......@@ -997,21 +990,11 @@ services:
arguments: ['@form_ajax_response_builder', '@string_translation']
tags:
- { name: event_subscriber }
route_enhancer.lazy_collector:
class: Drupal\Core\Routing\LazyRouteEnhancer
tags:
- { name: non_lazy_route_enhancer, priority: 5000 }
parent: container.trait
route_enhancer_subscriber:
class: Drupal\Core\EventSubscriber\RouteEnhancerSubscriber
arguments: ['@route_enhancer.lazy_collector']
tags:
- { name: event_subscriber }
route_enhancer.param_conversion:
class: Drupal\Core\Routing\Enhancer\ParamConversionEnhancer
arguments: ['@paramconverter_manager']
tags:
- { name: route_enhancer }
- { name: route_enhancer, priority: 5000 }
- { name: event_subscriber }
route_enhancer.form:
class: Drupal\Core\Routing\Enhancer\FormRouteEnhancer
......
......@@ -10,8 +10,6 @@
use Drupal\Core\DependencyInjection\Compiler\GuzzleMiddlewarePass;
use Drupal\Core\DependencyInjection\Compiler\ContextProvidersPass;
use Drupal\Core\DependencyInjection\Compiler\ProxyServicesPass;
use Drupal\Core\DependencyInjection\Compiler\RegisterLazyRouteEnhancers;
use Drupal\Core\DependencyInjection\Compiler\RegisterLazyRouteFilters;
use Drupal\Core\DependencyInjection\Compiler\DependencySerializationTraitPass;
use Drupal\Core\DependencyInjection\Compiler\StackedKernelPass;
use Drupal\Core\DependencyInjection\Compiler\StackedSessionHandlerPass;
......@@ -84,8 +82,6 @@ public function register(ContainerBuilder $container) {
$container->addCompilerPass(new RegisterEventSubscribersPass(), PassConfig::TYPE_AFTER_REMOVING);
$container->addCompilerPass(new RegisterAccessChecksPass());
$container->addCompilerPass(new RegisterLazyRouteEnhancers());
$container->addCompilerPass(new RegisterLazyRouteFilters());
// Add a compiler pass for registering services needing destruction.
$container->addCompilerPass(new RegisterServicesForDestructionPass());
......
<?php
namespace Drupal\Core\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
/**
* Registers all lazy route enhancers onto the lazy route enhancers.
*/
class RegisterLazyRouteEnhancers implements CompilerPassInterface {
/**
* {@inheritdoc}
*/
public function process(ContainerBuilder $container) {
if (!$container->hasDefinition('route_enhancer.lazy_collector')) {
return;
}
$service_ids = [];
foreach ($container->findTaggedServiceIds('route_enhancer') as $id => $attributes) {
$service_ids[$id] = $id;
}
$container
->getDefinition('route_enhancer.lazy_collector')
->addArgument($service_ids);
}
}
<?php
namespace Drupal\Core\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
/**
* Registers all lazy route filters onto the lazy route filter.
*/
class RegisterLazyRouteFilters implements CompilerPassInterface {
/**
* {@inheritdoc}
*/
public function process(ContainerBuilder $container) {
if (!$container->hasDefinition('route_filter.lazy_collector')) {
return;
}
$service_ids = [];
foreach ($container->findTaggedServiceIds('route_filter') as $id => $attributes) {
$service_ids[$id] = $id;
}
$container
->getDefinition('route_filter.lazy_collector')
->addArgument($service_ids);
}
}
......@@ -2,20 +2,25 @@
namespace Drupal\Core\Entity\Enhancer;
use Drupal\Core\Routing\Enhancer\RouteEnhancerInterface;
use Drupal\Core\Routing\EnhancerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Route;
use Symfony\Cmf\Component\Routing\RouteObjectInterface;
use Symfony\Component\Routing\Route;
/**
* Enhances an entity form route with the appropriate controller.
*/
class EntityRouteEnhancer implements RouteEnhancerInterface {
class EntityRouteEnhancer implements EnhancerInterface {
/**
* {@inheritdoc}
*/
public function enhance(array $defaults, Request $request) {
$route = $defaults[RouteObjectInterface::ROUTE_OBJECT];
if (!$this->applies($route)) {
return $defaults;
}
if (empty($defaults['_controller'])) {
if (!empty($defaults['_entity_form'])) {
$defaults = $this->enhanceEntityForm($defaults, $request);
......@@ -31,9 +36,14 @@ public function enhance(array $defaults, Request $request) {
}
/**
* {@inheritdoc}
* Returns whether the enhancer runs on the current route.
*
* @param \Symfony\Component\Routing\Route $route
* The current route.
*
* @return bool
*/
public function applies(Route $route) {
protected function applies(Route $route) {
return !$route->hasDefault('_controller') &&
($route->hasDefault('_entity_form')
|| $route->hasDefault('_entity_list')
......
<?php
namespace Drupal\Core\EventSubscriber;
use Drupal\Core\Routing\LazyRouteEnhancer;
use Drupal\Core\Routing\RouteBuildEvent;
use Drupal\Core\Routing\RoutingEvents;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* Listens to the new routes before they get saved.
*/
class RouteEnhancerSubscriber implements EventSubscriberInterface {
/**
* @var \Drupal\Core\Routing\LazyRouteEnhancer
*/
protected $routeEnhancer;
/**
* Constructs the RouteEnhancerSubscriber object.
*
* @param \Drupal\Core\Routing\LazyRouteEnhancer $route_enhancer
* The lazy route enhancer.
*/
public function __construct(LazyRouteEnhancer $route_enhancer) {
$this->routeEnhancer = $route_enhancer;
}
/**
* Adds the route_enhancer object to the route collection.
*
* @param \Drupal\Core\Routing\RouteBuildEvent $event
* The route build event.
*/
public function onRouteAlter(RouteBuildEvent $event) {
$this->routeEnhancer->setEnhancers($event->getRouteCollection());
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents() {
$events[RoutingEvents::ALTER][] = ['onRouteAlter', -300];
return $events;
}
}
<?php
namespace Drupal\Core\EventSubscriber;
use Drupal\Core\Routing\LazyRouteFilter;
use Drupal\Core\Routing\RouteBuildEvent;
use Drupal\Core\Routing\RoutingEvents;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* Listens to the filtered collection of route instances.
*/
class RouteFilterSubscriber implements EventSubscriberInterface {
/**
* The lazy route filter.
*
* @var \Drupal\Core\Routing\LazyRouteFilter
*/
protected $routeFilter;
/**
* Constructs the RouteFilterSubscriber object.
*
* @param \Drupal\Core\Routing\LazyRouteFilter $route_filter
* The lazy route filter.
*/
public function __construct(LazyRouteFilter $route_filter) {
$this->routeFilter = $route_filter;
}
/**
* Get the Response object from filtered route collection.
*
* @param \Drupal\Core\Routing\RouteBuildEvent $event
* The route build event.
*/
public function onRouteAlter(RouteBuildEvent $event) {
$this->routeFilter->setFilters($event->getRouteCollection());
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents() {
$events[RoutingEvents::ALTER][] = ['onRouteAlter', -300];
return $events;
}
}
......@@ -4,13 +4,12 @@
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\UnsupportedMediaTypeHttpException;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
/**
* Filters routes based on the HTTP Content-type header.
*/
class ContentTypeHeaderMatcher implements RouteFilterInterface {
class ContentTypeHeaderMatcher implements FilterInterface {
/**
* {@inheritdoc}
......@@ -50,11 +49,4 @@ public function filter(RouteCollection $collection, Request $request) {
}
}
/**
* {@inheritdoc}
*/
public function applies(Route $route) {
return TRUE;
}
}
......@@ -2,6 +2,7 @@
namespace Drupal\Core\Routing\Enhancer;
use Drupal\Core\Routing\EnhancerInterface;
use Symfony\Cmf\Component\Routing\RouteObjectInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Route;
......@@ -9,12 +10,17 @@
/**
* Adds _entity_revision to the request attributes, if possible.
*/
class EntityRevisionRouteEnhancer implements RouteEnhancerInterface {
class EntityRevisionRouteEnhancer implements EnhancerInterface {
/**
* {@inheritdoc}
* Returns whether the enhancer runs on the current route.
*
* @param \Symfony\Component\Routing\Route $route
* The current route.
*
* @return bool
*/
public function applies(Route $route) {
protected function applies(Route $route) {
// Check whether there is any entity revision parameter.
$parameters = $route->getOption('parameters') ?: [];
foreach ($parameters as $info) {
......@@ -31,6 +37,10 @@ public function applies(Route $route) {
public function enhance(array $defaults, Request $request) {
/** @var \Symfony\Component\Routing\Route $route */
$route = $defaults[RouteObjectInterface::ROUTE_OBJECT];
if (!$this->applies($route)) {
return $defaults;
}
$options = $route->getOptions();
if (isset($options['parameters'])) {
foreach ($options['parameters'] as $name => $details) {
......
......@@ -2,18 +2,25 @@
namespace Drupal\Core\Routing\Enhancer;
use Drupal\Core\Routing\EnhancerInterface;
use Symfony\Cmf\Component\Routing\RouteObjectInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Route;
/**
* Enhancer to add a wrapping controller for _form routes.
*/
class FormRouteEnhancer implements RouteEnhancerInterface {
class FormRouteEnhancer implements EnhancerInterface {
/**
* {@inheritdoc}
* Returns whether the enhancer runs on the current route.
*
* @param \Drupal\Core\Routing\Enhancer\Route $route
* The current route.
*
* @return bool
*/
public function applies(Route $route) {
protected function applies(Route $route) {
return $route->hasDefault('_form') && !$route->hasDefault('_controller');
}
......@@ -21,6 +28,11 @@ public function applies(Route $route) {
* {@inheritdoc}
*/
public function enhance(array $defaults, Request $request) {
$route = $defaults[RouteObjectInterface::ROUTE_OBJECT];
if (!$this->applies($route)) {
return $defaults;
}
$defaults['_controller'] = 'controller.form:getContentResult';
return $defaults;
}
......
......@@ -4,6 +4,7 @@
use Drupal\Core\ParamConverter\ParamConverterManagerInterface;
use Drupal\Core\ParamConverter\ParamNotConvertedException;
use Drupal\Core\Routing\EnhancerInterface;
use Symfony\Cmf\Component\Routing\RouteObjectInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\ParameterBag;
......@@ -11,12 +12,11 @@
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\Routing\Route;
/**
* Provides a route enhancer that handles parameter conversion.
*/
class ParamConversionEnhancer implements RouteEnhancerInterface, EventSubscriberInterface {
class ParamConversionEnhancer implements EnhancerInterface, EventSubscriberInterface {
/**
* The parameter conversion manager.
......@@ -89,11 +89,4 @@ public static function getSubscribedEvents() {
return $events;
}
/**
* {@inheritdoc}
*/
public function applies(Route $route) {
return TRUE;
}
}
......@@ -2,13 +2,21 @@
namespace Drupal\Core\Routing\Enhancer;
use Symfony\Cmf\Component\Routing\Enhancer\RouteEnhancerInterface as BaseRouteEnhancerInterface;
use Drupal\Core\Routing\EnhancerInterface;
use Symfony\Component\Routing\Route;
@trigger_error('\Drupal\Core\Routing\Enhancer\RouteEnhancerInterface is deprecated in Drupal 8.4.0 and will be removed before Drupal 9.0.0. Instead, you should use \Drupal\Core\Routing\EnhancerInterface. See https://www.drupal.org/node/2894934', E_USER_DEPRECATED);
/**
* A route enhance service to determine route enhance rules.
*
* @deprecated in Drupal 8.4.0 and will be removed before Drupal 9.0.0. Instead,
* you should use \Drupal\Core\Routing\EnhancerInterface.
* See https://www.drupal.org/node/2894934
* Part of the deprecation means that applies() is now called on runtime instead
* of compile time.
*/
interface RouteEnhancerInterface extends BaseRouteEnhancerInterface {
interface RouteEnhancerInterface extends EnhancerInterface {
/**
* Declares if the route enhancer applies to the given route.
......
<?php
namespace Drupal\Core\Routing;
use Symfony\Cmf\Component\Routing\Enhancer\RouteEnhancerInterface;
/**
* A route enhance service to determine route enhance rules.
*/
interface EnhancerInterface extends RouteEnhancerInterface {
}
<?php
namespace Drupal\Core\Routing;
use Symfony\Cmf\Component\Routing\NestedMatcher\RouteFilterInterface;
/**
* A route filter service to filter down the collection of route instances.
*/
interface FilterInterface extends RouteFilterInterface {
}
<?php
namespace Drupal\Core\Routing;
use Drupal\Core\Routing\Enhancer\RouteEnhancerInterface;
use Symfony\Cmf\Component\Routing\Enhancer\RouteEnhancerInterface as BaseRouteEnhancerInterface;
use Symfony\Cmf\Component\Routing\RouteObjectInterface;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\RouteCollection;
/**
* A route enhancer which lazily loads route enhancers, depending on the route.
*
* We lazy initialize route enhancers, because otherwise all dependencies of
* all route enhancers are initialized on every request, which is slow. However,
* with the use of lazy loading, dependencies are instantiated only when used.
*/
class LazyRouteEnhancer implements BaseRouteEnhancerInterface, ContainerAwareInterface {
use ContainerAwareTrait;
/**
* Array of enhancers service IDs.
*
* @var array
*/
protected $serviceIds = [];
/**
* The initialized route enhancers.
*
* @var \Symfony\Cmf\Component\Routing\Enhancer\RouteEnhancerInterface[]|\Drupal\Core\Routing\Enhancer\RouteEnhancerInterface[]
*/
protected $enhancers = NULL;
/**
* Constructs the LazyRouteEnhancer object.
*
* @param $service_ids
* Array of enhancers service IDs.
*/
public function __construct($service_ids) {
$this->serviceIds = $service_ids;
}
/**
* For each route, saves a list of applicable enhancers to the route.
*
* @param \Symfony\Component\Routing\RouteCollection $route_collection
* A collection of routes to apply enhancer checks to.
*/
public function setEnhancers(RouteCollection $route_collection) {
/** @var \Symfony\Component\Routing\Route $route **/
foreach ($route_collection as $route_name => $route) {
$service_ids = [];
foreach ($this->getEnhancers() as $service_id => $enhancer) {
if ((!$enhancer instanceof RouteEnhancerInterface) || $enhancer->applies($route)) {
$service_ids[] = $service_id;
}
}
if ($service_ids) {
$route->setOption('_route_enhancers', array_unique($service_ids));
}
}
}
/**
* For each route, gets a list of applicable enhancer to the route.
*
* @return \Symfony\Cmf\Component\Routing\Enhancer\RouteEnhancerInterface[]|\Drupal\Core\Routing\Enhancer\RouteEnhancerInterface[]
*/
protected function getEnhancers() {
if (!isset($this->enhancers)) {
foreach ($this->serviceIds as $service_id) {
$this->enhancers[$service_id] = $this->container->get($service_id);
}
}
return $this->enhancers;
}
/**
* {@inheritdoc}
*/
public function enhance(array $defaults, Request $request) {
/** @var \Symfony\Component\Routing\Route $route */
$route = $defaults[RouteObjectInterface::ROUTE_OBJECT];
$enhancer_ids = $route->getOption('_route_enhancers');
if (isset($enhancer_ids)) {
foreach ($enhancer_ids as $enhancer_id) {
$defaults = $this->container->get($enhancer_id)->enhance($defaults, $request);
}
}
return $defaults;
}
}
<?php
namespace Drupal\Core\Routing;
use Symfony\Cmf\Component\Routing\NestedMatcher\RouteFilterInterface as BaseRouteFilterInterface;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\RouteCollection;
/**
* A route filter which lazily loads route filters, depending on the route.
*
* We lazy initialize route filters, because otherwise all dependencies of all
* route filters are initialized on every request, which is slow. However, with
* the use of lazy loading, dependencies are instantiated only when used.
*/
class LazyRouteFilter implements BaseRouteFilterInterface, ContainerAwareInterface {
use ContainerAwareTrait;
/**
* Array of route filter service IDs.
*
* @var array
*/
protected $serviceIds = [];
/**
* The initialized route filters.
*
* @var \Drupal\Core\Routing\RouteFilterInterface[]
*/
protected $filters = NULL;
/**
* Constructs the LazyRouteEnhancer object.
*
* @param $service_ids
* Array of route filter service IDs.
*/
public function __construct($service_ids) {
$this->serviceIds = $service_ids;
}
/**
* For each route, filter down the route collection.
*
* @param \Symfony\Component\Routing\RouteCollection $route_collection
* A collection of routes to apply filter checks to.
*/
public function setFilters(RouteCollection $route_collection) {
/** @var \Symfony\Component\Routing\Route $route **/
foreach ($route_collection as $route) {
$service_ids = [];
foreach ($this->getFilters() as $service_id => $filter) {
if ($filter instanceof RouteFilterInterface && $filter->applies($route)) {
$service_ids[] = $service_id;
}
}
if ($service_ids) {
$route->setOption('_route_filters', array_unique($service_ids));
}
}
}
/**
* For each route, gets a list of applicable enhancers to the route.
*
* @return \Symfony\Cmf\Component\Routing\Enhancer\RouteEnhancerInterface[]|\Drupal\Core\Routing\Enhancer\RouteEnhancerInterface[]
*/
protected function getFilters() {
if (!isset($this->filters)) {
foreach ($this->serviceIds as $service_id) {
$this->filters[$service_id] = $this->container->get($service_id);
}
}
return $this->filters;
}
/**
* {@inheritdoc}
*/
public function filter(RouteCollection $collection, Request $request) {
$filter_ids = [];
foreach ($collection->all() as $route) {
$filter_ids = array_merge($filter_ids, $route->getOption('_route_filters') ?: []);
}
$filter_ids = array_unique($filter_ids);
if (isset($filter_ids)) {
foreach ($filter_ids as $filter_id) {
if ($filter = $this->container->get($filter_id, ContainerInterface::NULL_ON_INVALID_REFERENCE)) {
$collection = $filter->filter($collection, $request);
}
}
}
return $collection;
}
}
......@@ -4,13 +4,12 @@
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Exception\MethodNotAllowedException;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
/**
* Filters routes based on the HTTP method.
*/
class MethodFilter implements RouteFilterInterface {
class MethodFilter implements FilterInterface {
/**
* {@inheritdoc}
......@@ -47,11 +46,4 @@ public function filter(RouteCollection $collection, Request $request) {
throw new MethodNotAllowedException(array_unique($all_supported_methods));
}
/**
* {@inheritdoc}
*/
public function applies(Route $route) {
return !empty($route->getMethods());
}
}
......@@ -4,20 +4,12 @@
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\NotAcceptableHttpException;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
/**
* Provides a route filter, which filters by the request format.