diff --git a/core/core.services.yml b/core/core.services.yml index a8313a6df4f33c0307c371295906451622a29aa4..e1613195685a6d0d858f4dcfe620d76202261283 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -19,7 +19,7 @@ services: - { name: cache.context} cache_context.theme: class: Drupal\Core\Cache\ThemeCacheContext - arguments: ['@request_stack', '@theme.negotiator'] + arguments: ['@current_route_match', '@theme.negotiator'] tags: - { name: cache.context} cache_context.timezone: @@ -201,7 +201,7 @@ services: - { name: service_collector, tag: http_client_subscriber, call: attach } theme.negotiator: class: Drupal\Core\Theme\ThemeNegotiator - arguments: ['@access_check.theme', '@request_stack'] + arguments: ['@access_check.theme'] tags: - { name: service_collector, tag: theme_negotiator, call: addNegotiator } theme.negotiator.default: @@ -211,7 +211,7 @@ services: - { name: theme_negotiator, priority: -100 } theme.negotiator.ajax_base_page: class: Drupal\Core\Theme\AjaxBasePageNegotiator - arguments: ['@csrf_token', '@config.factory'] + arguments: ['@csrf_token', '@config.factory', '@request_stack'] tags: - { name: theme_negotiator, priority: 1000 } container.namespaces: @@ -273,6 +273,9 @@ services: class: Symfony\Component\HttpFoundation\RequestStack tags: - { name: persist } + current_route_match: + class: Drupal\Core\Routing\CurrentRouteMatch + arguments: ['@request_stack'] event_dispatcher: class: Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher arguments: ['@service_container'] diff --git a/core/includes/menu.inc b/core/includes/menu.inc index 1de74bf411251ee5130d59b979db91acd6867494..3b4b5c1fe26f49e268e61a5baa3bb54e5075d64e 100644 --- a/core/includes/menu.inc +++ b/core/includes/menu.inc @@ -8,7 +8,6 @@ use Drupal\Component\Utility\String; use Drupal\Core\Render\Element; use Drupal\Core\Template\Attribute; -use Symfony\Cmf\Component\Routing\RouteObjectInterface; /** * @defgroup menu Menu and routing system @@ -479,7 +478,7 @@ function menu_local_tasks($level = 0) { $data['tabs'] = array(); $data['actions'] = array(); - $route_name = \Drupal::request()->attributes->get(RouteObjectInterface::ROUTE_NAME); + $route_name = \Drupal::routeMatch()->getRouteName(); if (!empty($route_name)) { $manager = \Drupal::service('plugin.manager.menu.local_task'); $local_tasks = $manager->getTasksBuild($route_name); @@ -534,7 +533,7 @@ function menu_secondary_local_tasks() { */ function menu_get_local_actions() { $links = menu_local_tasks(); - $route_name = Drupal::request()->attributes->get(RouteObjectInterface::ROUTE_NAME); + $route_name = Drupal::routeMatch()->getRouteName(); $manager = \Drupal::service('plugin.manager.menu.local_action'); return $manager->getActionsForRoute($route_name) + $links['actions']; } diff --git a/core/includes/theme.inc b/core/includes/theme.inc index 03d0df0c2cb5f0d9d4f094774ac600537d39332c..18218af7d28880907d7b1e32978860c19a63083f 100644 --- a/core/includes/theme.inc +++ b/core/includes/theme.inc @@ -104,8 +104,7 @@ function drupal_theme_initialize() { // @todo Let the theme.negotiator listen to the kernel request event. // Determine the active theme for the theme negotiator service. This includes // the default theme as well as really specific ones like the ajax base theme. - $request = \Drupal::request(); - $theme = \Drupal::service('theme.negotiator')->determineActiveTheme($request); + $theme = \Drupal::service('theme.negotiator')->determineActiveTheme(\Drupal::routeMatch()); // If no theme could be negotiated, or if the negotiated theme is not within // the list of enabled themes, fall back to the default theme output of core @@ -2133,7 +2132,7 @@ function template_preprocess_page(&$variables) { ); } - if ($node = \Drupal::request()->attributes->get('node')) { + if ($node = \Drupal::routeMatch()->getParameter('node')) { $variables['node'] = $node; } diff --git a/core/lib/Drupal.php b/core/lib/Drupal.php index 57cf2ef9f4222f72f1acd4cb2c63ac8057f0c49b..6409fd38c0b99c2c7cf3d47dca6fb3028ce9ba26 100644 --- a/core/lib/Drupal.php +++ b/core/lib/Drupal.php @@ -187,6 +187,16 @@ public static function request() { return static::$container->get('request'); } + /** + * Retrieves the currently active route match object. + * + * @return \Drupal\Core\Routing\RouteMatchInterface + * The currently active route match object. + */ + public static function routeMatch() { + return static::$container->get('current_route_match'); + } + /** * Gets the current active user. * diff --git a/core/lib/Drupal/Core/Cache/ThemeCacheContext.php b/core/lib/Drupal/Core/Cache/ThemeCacheContext.php index a11671b8feb37627623191f8490989a44f052dd4..5d0bd3e6e809a07b01f090a504263d3ed61ccae6 100644 --- a/core/lib/Drupal/Core/Cache/ThemeCacheContext.php +++ b/core/lib/Drupal/Core/Cache/ThemeCacheContext.php @@ -7,7 +7,7 @@ namespace Drupal\Core\Cache; -use Symfony\Component\HttpFoundation\RequestStack; +use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Theme\ThemeNegotiatorInterface; /** @@ -16,11 +16,11 @@ class ThemeCacheContext implements CacheContextInterface { /** - * The request stack. + * The current route match. * - * @var \Symfony\Component\HttpFoundation\RequestStack + * @var \Drupal\Core\Routing\RouteMatch */ - protected $requestStack; + protected $routeMatch; /** * The theme negotiator. @@ -32,13 +32,13 @@ class ThemeCacheContext implements CacheContextInterface { /** * Constructs a new ThemeCacheContext service. * - * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack - * The request stack. + * @param \Drupal\Core\Routing\RouteMatchInterface $route_match + * The route match. * @param \Drupal\Core\Theme\ThemeNegotiatorInterface $theme_negotiator * The theme negotiator. */ - public function __construct(RequestStack $request_stack, ThemeNegotiatorInterface $theme_negotiator) { - $this->requestStack = $request_stack; + public function __construct(RouteMatchInterface $route_match, ThemeNegotiatorInterface $theme_negotiator) { + $this->routeMatch = $route_match; $this->themeNegotiator = $theme_negotiator; } @@ -53,8 +53,7 @@ public static function getLabel() { * {@inheritdoc} */ public function getContext() { - $request = $this->requestStack->getCurrentRequest(); - return $this->themeNegotiator->determineActiveTheme($request) ?: 'stark'; + return $this->themeNegotiator->determineActiveTheme($this->routeMatch) ?: 'stark'; } } diff --git a/core/lib/Drupal/Core/Routing/CurrentRouteMatch.php b/core/lib/Drupal/Core/Routing/CurrentRouteMatch.php new file mode 100644 index 0000000000000000000000000000000000000000..b18893057e2e1da84c07128687680b17eb2ffabe --- /dev/null +++ b/core/lib/Drupal/Core/Routing/CurrentRouteMatch.php @@ -0,0 +1,121 @@ +<?php + +/** + * @file + * Contains Drupal\Core\Routing\CurrentRouteMatch. + */ + +namespace Drupal\Core\Routing; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\RequestStack; + +/** + * Default object for current_route_match service. + */ +class CurrentRouteMatch implements RouteMatchInterface { + + /** + * The related request stack. + * + * @var \Symfony\Component\HttpFoundation\RequestStack + */ + protected $requestStack; + + /** + * Internal cache of RouteMatch objects. + * + * @var \SplObjectStorage + */ + protected $routeMatches; + + /** + * Constructs a CurrentRouteMatch object. + * + * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack + * The request stack. + */ + public function __construct(RequestStack $request_stack) { + $this->requestStack = $request_stack; + $this->routeMatches = new \SplObjectStorage(); + } + + /** + * {@inheritdoc} + */ + public function getRouteName() { + return $this->getCurrentRouteMatch()->getRouteName(); + } + + /** + * {@inheritdoc} + */ + public function getRouteObject() { + return $this->getCurrentRouteMatch()->getRouteObject(); + } + + /** + * {@inheritdoc} + */ + public function getParameter($parameter_name) { + return $this->getCurrentRouteMatch()->getParameter($parameter_name); + } + + /** + * {@inheritdoc} + */ + public function getParameters() { + return $this->getCurrentRouteMatch()->getParameters(); + } + + /** + * {@inheritdoc} + */ + public function getRawParameter($parameter_name) { + return $this->getCurrentRouteMatch()->getRawParameter($parameter_name); + } + + /** + * {@inheritdoc} + */ + public function getRawParameters() { + return $this->getCurrentRouteMatch()->getRawParameters(); + } + + /** + * Returns the route match for the current request. + * + * @return \Drupal\Core\Routing\RouteMatchInterface + * The current route match object. + */ + protected function getCurrentRouteMatch() { + return $this->getRouteMatch($this->requestStack->getCurrentRequest()); + } + + /** + * Returns the route match for a passed in request. + * + * @param \Symfony\Component\HttpFoundation\Request $request + * A request object. + * + * @return \Drupal\Core\Routing\RouteMatchInterface + * A route match object created from the request. + */ + protected function getRouteMatch(Request $request) { + if (isset($this->routeMatches[$request])) { + $route_match = $this->routeMatches[$request]; + } + else { + $route_match = RouteMatch::createFromRequest($request); + + // Since getRouteMatch() might be invoked both before and after routing + // is completed, only statically cache the route match after there's a + // matched route. + if ($route_match->getRouteObject()) { + $this->routeMatches[$request] = $route_match; + } + } + return $route_match; + } + +} diff --git a/core/lib/Drupal/Core/Routing/NullRouteMatch.php b/core/lib/Drupal/Core/Routing/NullRouteMatch.php new file mode 100644 index 0000000000000000000000000000000000000000..0ef2c3b300dfa722d0960bcdd16f54e33abb547f --- /dev/null +++ b/core/lib/Drupal/Core/Routing/NullRouteMatch.php @@ -0,0 +1,59 @@ +<?php + +/** + * @file + * Contains Drupal\Core\Routing\NullRouteMatch. + */ + +namespace Drupal\Core\Routing; + +use Symfony\Component\HttpFoundation\ParameterBag; + +/** + * Stub implementation of RouteMatchInterface for when there's no matched route. + */ +class NullRouteMatch implements RouteMatchInterface { + + /** + * {@inheritdoc} + */ + public function getRouteName() { + return NULL; + } + + /** + * {@inheritdoc} + */ + public function getRouteObject() { + return NULL; + } + + /** + * {@inheritdoc} + */ + public function getParameter($parameter_name) { + return NULL; + } + + /** + * {@inheritdoc} + */ + public function getParameters() { + return new ParameterBag(); + } + + /** + * {@inheritdoc} + */ + public function getRawParameter($parameter_name) { + return NULL; + } + + /** + * {@inheritdoc} + */ + public function getRawParameters() { + return new ParameterBag(); + } + +} diff --git a/core/lib/Drupal/Core/Routing/RouteMatch.php b/core/lib/Drupal/Core/Routing/RouteMatch.php new file mode 100644 index 0000000000000000000000000000000000000000..eafb1842959f2734737d5acbe655ece5bf023e82 --- /dev/null +++ b/core/lib/Drupal/Core/Routing/RouteMatch.php @@ -0,0 +1,165 @@ +<?php + +/** + * @file + * Contains Drupal\Core\Routing\RouteMatch. + */ + +namespace Drupal\Core\Routing; + +use Symfony\Cmf\Component\Routing\RouteObjectInterface; +use Symfony\Component\HttpFoundation\ParameterBag; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\Routing\Route; + +/** + * Default object representing the results of routing. + */ +class RouteMatch implements RouteMatchInterface { + + /** + * The route name. + * + * @var string + */ + protected $routeName; + + /** + * The route. + * + * @var \Symfony\Component\Routing\Route + */ + protected $route; + + /** + * A key|value store of parameters. + * + * @var \Symfony\Component\HttpFoundation\ParameterBag + */ + protected $parameters; + + /** + * A key|value store of raw parameters. + * + * @var \Symfony\Component\HttpFoundation\ParameterBag + */ + protected $rawParameters; + + /** + * Constructs a RouteMatch object. + * + * @param string $route_name + * The name of the route. + * @param \Symfony\Component\Routing\Route $route + * The route. + * @param array $parameters + * The parameters array. + * @param array $raw_parameters + * The raw $parameters array. + */ + public function __construct($route_name, Route $route, array $parameters = array(), array $raw_parameters = array()) { + $this->routeName = $route_name; + $this->route = $route; + + // Pre-filter parameters. + $route_params = $this->getParameterNames(); + $parameters = array_intersect_key($parameters, $route_params); + $raw_parameters = array_intersect_key($raw_parameters, $route_params); + $this->parameters = new ParameterBag($parameters); + $this->rawParameters = new ParameterBag($raw_parameters); + } + + /** + * Creates a RouteMatch from a request. + * + * @param Request $request + * A request object. + * + * @return \Drupal\Core\Routing\RouteMatchInterface + * A new RouteMatch object if there's a matched route for the request. + * A new NullRouteMatch object otherwise (e.g., on a 404 page or when + * invoked prior to routing). + */ + public static function createFromRequest(Request $request) { + if ($request->attributes->get(RouteObjectInterface::ROUTE_OBJECT)) { + $raw_variables = array(); + if ($raw = $request->attributes->get('_raw_variables')) { + $raw_variables = $raw->all(); + } + return new static( + $request->attributes->get(RouteObjectInterface::ROUTE_NAME), + $request->attributes->get(RouteObjectInterface::ROUTE_OBJECT), + $request->attributes->all(), + $raw_variables); + } + else { + return new NullRouteMatch(); + } + } + + /** + * {@inheritdoc} + */ + public function getRouteName() { + return $this->routeName; + } + + /** + * {@inheritdoc} + */ + public function getRouteObject() { + return $this->route; + } + + /** + * {@inheritdoc} + */ + public function getParameter($parameter_name) { + return $this->parameters->get($parameter_name); + } + + /** + * {@inheritdoc} + */ + public function getParameters() { + return $this->parameters; + } + + /** + * {@inheritdoc} + */ + public function getRawParameter($parameter_name) { + return $this->rawParameters->get($parameter_name); + } + + /** + * {@inheritdoc} + */ + public function getRawParameters() { + return $this->rawParameters; + } + + /** + * Returns the names of all parameters for the currently matched route. + * + * @return array + * Route parameter names as both the keys and values. + */ + protected function getParameterNames() { + $names = array(); + if ($route = $this->getRouteObject()) { + // Variables defined in path and host patterns are route parameters. + $variables = $route->compile()->getVariables(); + $names = array_combine($variables, $variables); + // Route defaults that do not start with a leading "_" are also + // parameters, even if they are not included in path or host patterns. + foreach ($route->getDefaults() as $name => $value) { + if (!isset($names[$name]) && substr($name, 0, 1) !== '_') { + $names[$name] = $name; + } + } + } + return $names; + } + +} diff --git a/core/lib/Drupal/Core/Routing/RouteMatchInterface.php b/core/lib/Drupal/Core/Routing/RouteMatchInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..f0d9aa8dee4c57a78483f1ae6861d34b1948696d --- /dev/null +++ b/core/lib/Drupal/Core/Routing/RouteMatchInterface.php @@ -0,0 +1,82 @@ +<?php + +/** + * @file + * Contains Drupal\Core\Routing\RouteMatchInterface. + */ + +namespace Drupal\Core\Routing; + +/** + * Provides an interface for classes representing the result of routing. + * + * Routing is the process of selecting the best matching candidate from a + * collection of routes for an incoming request. The relevant properties of a + * request include the path as well as a list of raw parameter values derived + * from the URL. If an appropriate route is found, raw parameter values will be + * upcast automatically if possible. + * + * The route match object contains useful information about the selected route + * as well as the raw and upcast parameters derived from the incoming + * request. + */ +interface RouteMatchInterface { + + /** + * Returns the route name. + * + * @return string|null + * The route name. NULL if no route is matched. + */ + public function getRouteName(); + + /** + * Returns the route object. + * + * @return \Symfony\Component\Routing\Route|null + * The route object. NULL if no route is matched. + */ + public function getRouteObject(); + + /** + * Returns the value of a named route parameter. + * + * @param string $parameter_name + * The parameter name. + * + * @return mixed|null + * The parameter value. NULL if the route doesn't define the parameter or + * if the parameter value can't be determined from the request. + */ + public function getParameter($parameter_name); + + /** + * Returns the bag of all route parameters. + * + * @return \Symfony\Component\HttpFoundation\ParameterBag + * The parameter bag. + */ + public function getParameters(); + + /** + * Returns the raw value of a named route parameter. + * + * @param string $parameter_name + * The parameter name. + * + * @return string|null + * The raw (non-upcast) parameter value. NULL if the route doesn't define + * the parameter or if the raw parameter value can't be determined from the + * request. + */ + public function getRawParameter($parameter_name); + + /** + * Returns the bag of all raw route parameters. + * + * @return \Symfony\Component\HttpFoundation\ParameterBag + * The parameter bag. + */ + public function getRawParameters(); + +} diff --git a/core/lib/Drupal/Core/Theme/AjaxBasePageNegotiator.php b/core/lib/Drupal/Core/Theme/AjaxBasePageNegotiator.php index d3a4bac4caf7db605952ea76448c40442b1bbfda..b15ce93bef7b140e636443efdd5f7c65e4fbef88 100644 --- a/core/lib/Drupal/Core/Theme/AjaxBasePageNegotiator.php +++ b/core/lib/Drupal/Core/Theme/AjaxBasePageNegotiator.php @@ -9,8 +9,8 @@ use Drupal\Core\Access\CsrfTokenGenerator; use Drupal\Core\Config\ConfigFactoryInterface; -use Symfony\Cmf\Component\Routing\RouteObjectInterface; -use Symfony\Component\HttpFoundation\Request; +use Drupal\Core\Routing\RouteMatchInterface; +use Symfony\Component\HttpFoundation\RequestStack; /** * Defines a theme negotiator that deals with the active theme on ajax requests. @@ -45,6 +45,13 @@ class AjaxBasePageNegotiator implements ThemeNegotiatorInterface { */ protected $configFactory; + /** + * The request stack. + * + * @var \Symfony\Component\HttpFoundation\RequestStack + */ + protected $requestStack; + /** * Constructs a new AjaxBasePageNegotiator. * @@ -52,18 +59,21 @@ class AjaxBasePageNegotiator implements ThemeNegotiatorInterface { * The CSRF token generator. * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory * The config factory. + * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack + * The request stack used to retrieve the current request. */ - public function __construct(CsrfTokenGenerator $token_generator, ConfigFactoryInterface $config_factory) { + public function __construct(CsrfTokenGenerator $token_generator, ConfigFactoryInterface $config_factory, RequestStack $request_stack) { $this->csrfGenerator = $token_generator; $this->configFactory = $config_factory; + $this->requestStack = $request_stack; } /** * {@inheritdoc} */ - public function applies(Request $request) { + public function applies(RouteMatchInterface $route_match) { // Check whether the route was configured to use the base page theme. - return ($route = $request->attributes->get(RouteObjectInterface::ROUTE_OBJECT)) + return ($route = $route_match->getRouteObject()) && $route->hasOption('_theme') && $route->getOption('_theme') == 'ajax_base_page'; } @@ -71,8 +81,8 @@ public function applies(Request $request) { /** * {@inheritdoc} */ - public function determineActiveTheme(Request $request) { - if (($ajax_page_state = $request->request->get('ajax_page_state')) && !empty($ajax_page_state['theme']) && !empty($ajax_page_state['theme_token'])) { + public function determineActiveTheme(RouteMatchInterface $route_match) { + if (($ajax_page_state = $this->requestStack->getCurrentRequest()->request->get('ajax_page_state')) && !empty($ajax_page_state['theme']) && !empty($ajax_page_state['theme_token'])) { $theme = $ajax_page_state['theme']; $token = $ajax_page_state['theme_token']; diff --git a/core/lib/Drupal/Core/Theme/DefaultNegotiator.php b/core/lib/Drupal/Core/Theme/DefaultNegotiator.php index 22b5863b364df478c1785caeb8d1f7b2322ee724..2f0e6f78138b0a37a6ccc9bd5858d86eb7e7d8e1 100644 --- a/core/lib/Drupal/Core/Theme/DefaultNegotiator.php +++ b/core/lib/Drupal/Core/Theme/DefaultNegotiator.php @@ -8,7 +8,7 @@ namespace Drupal\Core\Theme; use Drupal\Core\Config\ConfigFactoryInterface; -use Symfony\Component\HttpFoundation\Request; +use Drupal\Core\Routing\RouteMatchInterface; /** * Determines the default theme of the site. @@ -35,14 +35,14 @@ public function __construct(ConfigFactoryInterface $config_factory) { /** * {@inheritdoc} */ - public function applies(Request $request) { + public function applies(RouteMatchInterface $route_match) { return TRUE; } /** * {@inheritdoc} */ - public function determineActiveTheme(Request $request) { + public function determineActiveTheme(RouteMatchInterface $route_match) { return $this->config->get('default'); } diff --git a/core/lib/Drupal/Core/Theme/ThemeNegotiator.php b/core/lib/Drupal/Core/Theme/ThemeNegotiator.php index 054d6be8ffc364598d4a624b9e950c57018f2f77..8c1ef0330847065051021cbe6d83d86a0dfa1eca 100644 --- a/core/lib/Drupal/Core/Theme/ThemeNegotiator.php +++ b/core/lib/Drupal/Core/Theme/ThemeNegotiator.php @@ -7,8 +7,7 @@ namespace Drupal\Core\Theme; -use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpFoundation\RequestStack; +use Drupal\Core\Routing\RouteMatchInterface; /** * Provides a class which determines the active theme of the page. @@ -37,13 +36,6 @@ class ThemeNegotiator implements ThemeNegotiatorInterface { */ protected $sortedNegotiators; - /** - * The request stack. - * - * @var \Symfony\Component\HttpFoundation\RequestStack - */ - protected $requestStack; - /** * The access checker for themes. * @@ -57,9 +49,8 @@ class ThemeNegotiator implements ThemeNegotiatorInterface { * @param \Drupal\Core\Theme\ThemeAccessCheck $theme_access * The access checker for themes. */ - public function __construct(ThemeAccessCheck $theme_access, RequestStack $request_stack) { + public function __construct(ThemeAccessCheck $theme_access) { $this->themeAccess = $theme_access; - $this->requestStack = $request_stack; } /** @@ -96,37 +87,22 @@ protected function getSortedNegotiators() { return $this->sortedNegotiators; } - /** - * Get the current active theme. - * - * @return string - * The current active string. - */ - public function getActiveTheme() { - $request = $this->requestStack->getCurrentRequest(); - if (!$request->attributes->has('_theme_active')) { - $this->determineActiveTheme($request); - } - return $request->attributes->get('_theme_active'); - } - /** * {@inheritdoc} */ - public function applies(Request $request) { + public function applies(RouteMatchInterface $route_match) { return TRUE; } /** * {@inheritdoc} */ - public function determineActiveTheme(Request $request) { + public function determineActiveTheme(RouteMatchInterface $route_match) { foreach ($this->getSortedNegotiators() as $negotiator) { - if ($negotiator->applies($request)) { - $theme = $negotiator->determineActiveTheme($request); + if ($negotiator->applies($route_match)) { + $theme = $negotiator->determineActiveTheme($route_match); if ($theme !== NULL && $this->themeAccess->checkAccess($theme)) { - $request->attributes->set('_theme_active', $theme); - return $request->attributes->get('_theme_active'); + return $theme; } } } diff --git a/core/lib/Drupal/Core/Theme/ThemeNegotiatorInterface.php b/core/lib/Drupal/Core/Theme/ThemeNegotiatorInterface.php index e0631c2cf9fb58b64f62865fed61a1c46ed1652f..673893e709035e2788e363dc80d80fa7f9f926a7 100644 --- a/core/lib/Drupal/Core/Theme/ThemeNegotiatorInterface.php +++ b/core/lib/Drupal/Core/Theme/ThemeNegotiatorInterface.php @@ -7,7 +7,7 @@ namespace Drupal\Core\Theme; -use Symfony\Component\HttpFoundation\Request; +use Drupal\Core\Routing\RouteMatchInterface; /** * Defines an interface for classes which determine the active theme. @@ -30,24 +30,24 @@ interface ThemeNegotiatorInterface { /** * Whether this theme negotiator should be used to set the theme. * - * @param \Symfony\Component\HttpFoundation\Request $request - * The current request object. + * @param \Drupal\Core\Routing\RouteMatchInterface $route_match + * The current route match object. * * @return bool * TRUE if this negotiator should be used or FALSE to let other negotiators * decide. */ - public function applies(Request $request); + public function applies(RouteMatchInterface $route_match); /** * Determine the active theme for the request. * - * @param \Symfony\Component\HttpFoundation\Request $request - * The active request of the site. + * @param \Drupal\Core\Routing\RouteMatchInterface $route_match + * The current route match object. * * @return string|null * Returns the active theme name, else return NULL. */ - public function determineActiveTheme(Request $request); + public function determineActiveTheme(RouteMatchInterface $route_match); } diff --git a/core/modules/block/block.module b/core/modules/block/block.module index e029b0e8ce2ad898d2f36b0d183a652cdcac97c5..ce880be0fcce7adc52257e061b2279c5455c0cfb 100644 --- a/core/modules/block/block.module +++ b/core/modules/block/block.module @@ -8,7 +8,6 @@ use Drupal\block\BlockInterface; use Drupal\language\Entity\Language; use Drupal\system\Entity\Menu; -use Symfony\Cmf\Component\Routing\RouteObjectInterface; use Symfony\Component\HttpFoundation\Request; /** @@ -86,7 +85,7 @@ function block_page_build(&$page) { // Fetch a list of regions for the current theme. $all_regions = system_region_list($theme); - if (\Drupal::request()->attributes->get(RouteObjectInterface::ROUTE_NAME) != 'block.admin_demo') { + if (\Drupal::routeMatch()->getRouteName() != 'block.admin_demo') { // Load all region content assigned via blocks. foreach (array_keys($all_regions) as $region) { // Assign blocks to region. diff --git a/core/modules/block/block.services.yml b/core/modules/block/block.services.yml index 5373645739f6fad5e1f8b768783892b63d12cd77..e5e58d1c2bfb6c75e83949f5f1bf8dc1797c4fc7 100644 --- a/core/modules/block/block.services.yml +++ b/core/modules/block/block.services.yml @@ -18,6 +18,6 @@ services: - { name: 'event_subscriber' } block.node_route_context: class: Drupal\block\EventSubscriber\NodeRouteContext - arguments: ['@request_stack'] + arguments: ['@current_route_match'] tags: - { name: 'event_subscriber' } diff --git a/core/modules/block/src/EventSubscriber/NodeRouteContext.php b/core/modules/block/src/EventSubscriber/NodeRouteContext.php index 117bf21b74c8c4af893fe894d5116883d9b526c8..adadbe828c12d02efa086f593f931baa1bea1ce1 100644 --- a/core/modules/block/src/EventSubscriber/NodeRouteContext.php +++ b/core/modules/block/src/EventSubscriber/NodeRouteContext.php @@ -8,9 +8,8 @@ namespace Drupal\block\EventSubscriber; use Drupal\Core\Plugin\Context\Context; +use Drupal\Core\Routing\RouteMatchInterface; use Drupal\node\Entity\Node; -use Symfony\Cmf\Component\Routing\RouteObjectInterface; -use Symfony\Component\HttpFoundation\RequestStack; /** * Sets the current node as a context on node routes. @@ -18,36 +17,35 @@ class NodeRouteContext extends BlockConditionContextSubscriberBase { /** - * The request stack. + * The route match object. * - * @var \Symfony\Component\HttpFoundation\RequestStack + * @var \Drupal\Core\Routing\RouteMatchInterface */ - protected $requestStack; + protected $routeMatch; /** * Constructs a new NodeRouteContext. * - * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack - * The request stack. + * @param \Drupal\Core\Routing\RouteMatchInterface $route_match + * The route match object. */ - public function __construct(RequestStack $request_stack) { - $this->requestStack = $request_stack; + public function __construct(RouteMatchInterface $route_match) { + $this->routeMatch = $route_match; } /** * {@inheritdoc} */ protected function determineBlockContext() { - $request = $this->requestStack->getCurrentRequest(); - if ($request->attributes->has(RouteObjectInterface::ROUTE_OBJECT) && ($route_contexts = $request->attributes->get(RouteObjectInterface::ROUTE_OBJECT)->getOption('parameters')) && isset($route_contexts['node'])) { + if (($route_object = $this->routeMatch->getRouteObject()) && ($route_contexts = $route_object->getOption('parameters')) && isset($route_contexts['node'])) { $context = new Context($route_contexts['node']); - if ($request->attributes->has('node')) { - $context->setContextValue($request->attributes->get('node')); + if ($node = $this->routeMatch->getParameter('node')) { + $context->setContextValue($node); } $this->addContext('node', $context); } - elseif ($request->attributes->get(RouteObjectInterface::ROUTE_NAME) == 'node.add') { - $node_type = $request->attributes->get('node_type'); + elseif ($this->routeMatch->getRouteName() == 'node.add') { + $node_type = $this->routeMatch->getParameter('node_type'); $context = new Context(array('type' => 'entity:node')); $context->setContextValue(Node::create(array('type' => $node_type->id()))); $this->addContext('node', $context); diff --git a/core/modules/block/src/Theme/AdminDemoNegotiator.php b/core/modules/block/src/Theme/AdminDemoNegotiator.php index d175b91dd970a09ef06155a37e277f344958ec79..50d88c320f9077d9a9f1491b776e763f5be41707 100644 --- a/core/modules/block/src/Theme/AdminDemoNegotiator.php +++ b/core/modules/block/src/Theme/AdminDemoNegotiator.php @@ -7,9 +7,8 @@ namespace Drupal\block\Theme; +use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Theme\ThemeNegotiatorInterface; -use Symfony\Cmf\Component\Routing\RouteObjectInterface; -use Symfony\Component\HttpFoundation\Request; /** * Negotiates the theme for the block admin demo page via the URL. @@ -19,17 +18,17 @@ class AdminDemoNegotiator implements ThemeNegotiatorInterface { /** * {@inheritdoc} */ - public function applies(Request $request) { - return $request->attributes->get(RouteObjectInterface::ROUTE_NAME) == 'block.admin_demo'; + public function applies(RouteMatchInterface $route_match) { + return $route_match->getRouteName() == 'block.admin_demo'; } /** * {@inheritdoc} */ - public function determineActiveTheme(Request $request) { + public function determineActiveTheme(RouteMatchInterface $route_match) { // We return exactly what was passed in, to guarantee that the page will // always be displayed using the theme whose blocks are being configured. - return $request->attributes->get('theme'); + return $route_match->getParameter('theme'); } } diff --git a/core/modules/forum/forum.module b/core/modules/forum/forum.module index af041fc4a19ba523abcaaa359ae3f542f17e44d2..941c112d9f659c375ccc7fdc036c903a399d0d78 100644 --- a/core/modules/forum/forum.module +++ b/core/modules/forum/forum.module @@ -110,8 +110,7 @@ function forum_menu_local_tasks(&$data, $route_name) { // Add action link to 'node/add/forum' on 'forum' sub-pages. if (in_array($route_name, array('forum.index', 'forum.page'))) { - $request = \Drupal::request(); - $forum_term = $request->attributes->get('taxonomy_term'); + $forum_term = \Drupal::routeMatch()->getParameter('taxonomy_term'); $vid = \Drupal::config('forum.settings')->get('vocabulary'); $links = array(); // Loop through all bundles for forum taxonomy vocabulary field. diff --git a/core/modules/node/node.module b/core/modules/node/node.module index 09aff4fdb62d33c48c8c30e413a699913cfef9fa..28c826fc077e6511619660a5c27a7a2c5c67e884 100644 --- a/core/modules/node/node.module +++ b/core/modules/node/node.module @@ -28,7 +28,6 @@ use Drupal\Core\Template\Attribute; use Drupal\file\Entity\File; use Drupal\language\Entity\Language as LanguageEntity; -use Symfony\Cmf\Component\Routing\RouteObjectInterface; use Drupal\block\Entity\Block; use Drupal\Core\Session\AccountInterface; @@ -559,9 +558,9 @@ function node_revision_delete($revision_id) { * The ID of the node if this is a full page view, otherwise FALSE. */ function node_is_page(NodeInterface $node) { - $request = \Drupal::request(); - if ($request->attributes->get(RouteObjectInterface::ROUTE_NAME) == 'node.view') { - $page_node = $request->attributes->get('node'); + $route_match = \Drupal::routeMatch(); + if ($route_match->getRouteName() == 'node.view') { + $page_node = $route_match->getParameter('node'); } return (!empty($page_node) ? $page_node->id() == $node->id() : FALSE); } @@ -571,7 +570,7 @@ function node_is_page(NodeInterface $node) { */ function node_preprocess_html(&$variables) { // If on an individual node page, add the node type to body classes. - if (($node = \Drupal::request()->attributes->get('node')) && $node instanceof NodeInterface) { + if (($node = \Drupal::routeMatch()->getParameter('node')) && $node instanceof NodeInterface) { $variables['attributes']['class'][] = drupal_html_class('node--type-' . $node->getType()); } } diff --git a/core/modules/rdf/rdf.module b/core/modules/rdf/rdf.module index a66af78b5f5285bd7f97e5aef8d39d512ae3c0cc..e300e80f8251e39d58a1897d4c0e0ce541bd017c 100644 --- a/core/modules/rdf/rdf.module +++ b/core/modules/rdf/rdf.module @@ -7,7 +7,6 @@ use Drupal\Component\Utility\String; use Drupal\Core\Template\Attribute; -use Symfony\Cmf\Component\Routing\RouteObjectInterface; use Symfony\Component\HttpFoundation\Request; /** @@ -353,7 +352,7 @@ function rdf_preprocess_user(&$variables) { } // If we are on the user account page, add the relationship between the // sioc:UserAccount and the foaf:Person who holds the account. - if (\Drupal::request()->attributes->get(RouteObjectInterface::ROUTE_NAME) == $uri->getRouteName()) { + if (\Drupal::routeMatch()->getRouteName() == $uri->getRouteName()) { // Adds the markup for username as language neutral literal, see // rdf_preprocess_username(). $name_mapping = $mapping->getPreparedFieldMapping('name'); diff --git a/core/modules/shortcut/shortcut.module b/core/modules/shortcut/shortcut.module index 0225e25371003826b5de20dfdb3504c9bea69b75..6b516d1b889da73ab1c6dcb03896115773e1facf 100644 --- a/core/modules/shortcut/shortcut.module +++ b/core/modules/shortcut/shortcut.module @@ -11,7 +11,6 @@ use Drupal\Core\Url; use Drupal\shortcut\Entity\ShortcutSet; use Drupal\shortcut\ShortcutSetInterface; -use Symfony\Cmf\Component\Routing\RouteObjectInterface; use Symfony\Component\HttpFoundation\Request; /** @@ -343,10 +342,8 @@ function shortcut_preprocess_page(&$variables) { // shortcuts and if the page's actual content is being shown (for example, // we do not want to display it on "access denied" or "page not found" // pages). - // Load the router item corresponding to the current page. - $request = \Drupal::request(); $item = array(); - if ($route = $request->attributes->get(RouteObjectInterface::ROUTE_NAME)) { + if (\Drupal::routeMatch()->getRouteObject()) { // @todo What should be done on a 404/403 page? $item['access'] = TRUE; } diff --git a/core/modules/system/src/Controller/BatchController.php b/core/modules/system/src/Controller/BatchController.php index 574872e05983075663721fbc28ac783ccfff0156..d14f9870b0ceae433fe4c74c272df1f115c60ec7 100644 --- a/core/modules/system/src/Controller/BatchController.php +++ b/core/modules/system/src/Controller/BatchController.php @@ -11,7 +11,6 @@ use Drupal\Core\DependencyInjection\ContainerInjectionInterface; use Drupal\Core\Page\DefaultHtmlFragmentRenderer; use Drupal\Core\Page\HtmlPage; -use Symfony\Cmf\Component\Routing\RouteObjectInterface; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; @@ -99,8 +98,7 @@ public function batchPage(Request $request) { */ public function render(array $output, $status_code = 200) { if (!isset($output['#title'])) { - $request = \Drupal::request(); - $output['#title'] = $this->titleResolver->getTitle($request, $request->attributes->get(RouteObjectInterface::ROUTE_OBJECT)); + $output['#title'] = $this->titleResolver->getTitle(\Drupal::request(), \Drupal::routeMatch()->getRouteObject()); } $page = new HtmlPage('', isset($output['#cache']) ? $output['#cache'] : array(), $output['#title']); diff --git a/core/modules/system/src/Theme/BatchNegotiator.php b/core/modules/system/src/Theme/BatchNegotiator.php index 700250415a049ec568a14ba0338fffe62daa6983..b8e1d472b01553db69b90a57377ede508e5299fb 100644 --- a/core/modules/system/src/Theme/BatchNegotiator.php +++ b/core/modules/system/src/Theme/BatchNegotiator.php @@ -8,9 +8,9 @@ namespace Drupal\system\Theme; use Drupal\Core\Batch\BatchStorageInterface; +use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Theme\ThemeNegotiatorInterface; -use Symfony\Cmf\Component\Routing\RouteObjectInterface; -use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\RequestStack; /** * Sets the active theme for the batch page. @@ -24,28 +24,39 @@ class BatchNegotiator implements ThemeNegotiatorInterface { */ protected $batchStorage; + /** + * The request stack. + * + * @var \Symfony\Component\HttpFoundation\RequestStack + */ + protected $requestStack; + /** * Constructs a BatchNegotiator. * * @param \Drupal\Core\Batch\BatchStorageInterface $batch_storage * The batch storage. + * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack + * The request stack used to retrieve the current request. */ - public function __construct(BatchStorageInterface $batch_storage) { + public function __construct(BatchStorageInterface $batch_storage, RequestStack $request_stack) { $this->batchStorage = $batch_storage; + $this->requestStack = $request_stack; } /** * {@inheritdoc} */ - public function applies(Request $request) { - return $request->attributes->get(RouteObjectInterface::ROUTE_NAME) == 'system.batch_page'; + public function applies(RouteMatchInterface $route_match) { + return $route_match->getRouteName() == 'system.batch_page'; } /** * {@inheritdoc} */ - public function determineActiveTheme(Request $request) { + public function determineActiveTheme(RouteMatchInterface $route_match) { // Retrieve the current state of the batch. + $request = $this->requestStack->getCurrentRequest(); $batch = &batch_get(); if (!$batch && $request->request->has('id')) { $batch = $this->batchStorage->load($request->request->get('id')); diff --git a/core/modules/system/system.services.yml b/core/modules/system/system.services.yml index 8d09004705c827de8fe8594d1acee8f876d4098b..2b7046cb0ec0e4da10402fc7aa4d0ec641b74e69 100644 --- a/core/modules/system/system.services.yml +++ b/core/modules/system/system.services.yml @@ -21,7 +21,7 @@ services: - { name: event_subscriber } theme.negotiator.system.batch: class: Drupal\system\Theme\BatchNegotiator - arguments: ['@batch.storage'] + arguments: ['@batch.storage', '@request_stack'] tags: - { name: theme_negotiator, priority: 1000 } system.config_subscriber: diff --git a/core/modules/system/tests/modules/menu_test/menu_test.module b/core/modules/system/tests/modules/menu_test/menu_test.module index f8aff5db4a10999f2df180493d1f5b41615e61fb..9f1d06d2688598fa8d11d4837e46135f841c1013 100644 --- a/core/modules/system/tests/modules/menu_test/menu_test.module +++ b/core/modules/system/tests/modules/menu_test/menu_test.module @@ -139,7 +139,7 @@ function menu_test_theme_page_callback($inherited = FALSE) { // Initialize the theme system so that $theme_key will be populated. drupal_theme_initialize(); // Now we check what the theme negotiator service returns. - $active_theme = \Drupal::service('theme.negotiator')->getActiveTheme('getActiveTheme'); + $active_theme = \Drupal::service('theme.negotiator')->determineActiveTheme(\Drupal::routeMatch()); $output = "Active theme: $active_theme. Actual theme: $theme_key."; if ($inherited) { $output .= ' Theme negotiation inheritance is being tested.'; diff --git a/core/modules/system/tests/modules/menu_test/src/Theme/TestThemeNegotiator.php b/core/modules/system/tests/modules/menu_test/src/Theme/TestThemeNegotiator.php index 77954a35bd91a891ce1a9e9df879afb92b1a9141..3d3a17ee5b49c1bcb5646f0b669c8a3f7cdb88ed 100644 --- a/core/modules/system/tests/modules/menu_test/src/Theme/TestThemeNegotiator.php +++ b/core/modules/system/tests/modules/menu_test/src/Theme/TestThemeNegotiator.php @@ -7,8 +7,8 @@ namespace Drupal\menu_test\Theme; +use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Theme\ThemeNegotiatorInterface; -use Symfony\Component\HttpFoundation\Request; /** * Tests the theme negotiation functionality. @@ -21,15 +21,15 @@ class TestThemeNegotiator implements ThemeNegotiatorInterface { /** * {@inheritdoc} */ - public function applies(Request $request) { - return $request->attributes->has('inherited'); + public function applies(RouteMatchInterface $route_match) { + return (bool) $route_match->getParameter('inherited'); } /** * {@inheritdoc} */ - public function determineActiveTheme(Request $request) { - $argument = $request->attributes->get('inherited'); + public function determineActiveTheme(RouteMatchInterface $route_match) { + $argument = $route_match->getParameter('inherited'); // Test using the variable administrative theme. if ($argument == 'use-admin-theme') { return \Drupal::config('system.theme')->get('admin'); diff --git a/core/modules/system/tests/modules/theme_test/src/Theme/CustomThemeNegotiator.php b/core/modules/system/tests/modules/theme_test/src/Theme/CustomThemeNegotiator.php index 33b9bec067a4cab7358ee494ce583c866f5a8da6..e95f57caea1d26fb236335d4fa31c20743a7bd1f 100644 --- a/core/modules/system/tests/modules/theme_test/src/Theme/CustomThemeNegotiator.php +++ b/core/modules/system/tests/modules/theme_test/src/Theme/CustomThemeNegotiator.php @@ -7,10 +7,8 @@ namespace Drupal\theme_test\Theme; +use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Theme\ThemeNegotiatorInterface; -use Symfony\Cmf\Component\Routing\RouteObjectInterface; -use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\Routing\Route; /** * Just forces the 'test_theme' theme. @@ -20,16 +18,16 @@ class CustomThemeNegotiator implements ThemeNegotiatorInterface { /** * {@inheritdoc} */ - public function applies(Request $request) { - return (($route_object = $request->attributes->get(RouteObjectInterface::ROUTE_OBJECT)) && $route_object instanceof Route && $route_object->hasOption('_custom_theme')); + public function applies(RouteMatchInterface $route_match) { + $route = $route_match->getRouteObject(); + return ($route && $route->hasOption('_custom_theme')); } /** * {@inheritdoc} */ - public function determineActiveTheme(Request $request) { - $route_object = $request->attributes->get(RouteObjectInterface::ROUTE_OBJECT); - return $route_object->getOption('_custom_theme'); + public function determineActiveTheme(RouteMatchInterface $route_match) { + return $route_match->getRouteObject()->getOption('_custom_theme'); } } diff --git a/core/modules/system/tests/modules/theme_test/src/Theme/HighPriorityThemeNegotiator.php b/core/modules/system/tests/modules/theme_test/src/Theme/HighPriorityThemeNegotiator.php index 91fc9365a167ccbe0da23fff28fddc0c468ac1c5..8d9326754dca3227197de91249a48ea1129b6ec9 100644 --- a/core/modules/system/tests/modules/theme_test/src/Theme/HighPriorityThemeNegotiator.php +++ b/core/modules/system/tests/modules/theme_test/src/Theme/HighPriorityThemeNegotiator.php @@ -7,28 +7,25 @@ namespace Drupal\theme_test\Theme; +use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Theme\ThemeNegotiatorInterface; -use Symfony\Cmf\Component\Routing\RouteObjectInterface; -use Symfony\Component\HttpFoundation\Request; /** * Implements a test theme negotiator which was configured with a high priority. */ class HighPriorityThemeNegotiator implements ThemeNegotiatorInterface { - - /** * {@inheritdoc} */ - public function applies(Request $request) { - return (($route_name = $request->attributes->get(RouteObjectInterface::ROUTE_NAME)) && $route_name == 'theme_test.priority'); + public function applies(RouteMatchInterface $route_match) { + return ($route_match->getRouteName() == 'theme_test.priority'); } /** * {@inheritdoc} */ - public function determineActiveTheme(Request $request) { + public function determineActiveTheme(RouteMatchInterface $route_match) { return 'stark'; } diff --git a/core/modules/taxonomy/taxonomy.module b/core/modules/taxonomy/taxonomy.module index fde291e76ef52c34ce55d6445a452e380343b2a3..cf9a6abde1a4e15c7c8e94c0678a1d766f48478c 100644 --- a/core/modules/taxonomy/taxonomy.module +++ b/core/modules/taxonomy/taxonomy.module @@ -332,9 +332,7 @@ function template_preprocess_taxonomy_term(&$variables) { * A taxonomy term entity. */ function taxonomy_term_is_page(Term $term) { - $request = \Drupal::request(); - if ($request->attributes->has('taxonomy_term')) { - $page_term = $request->attributes->get('taxonomy_term'); + if ($page_term = \Drupal::routeMatch()->getParameter('taxonomy_term')) { return $page_term->id() == $term->id(); } return FALSE; diff --git a/core/modules/tour/tour.module b/core/modules/tour/tour.module index ffe79c2dd52860da96448021dd0336d89853e5e1..f6774fdf6c9a4a231334bedad9d11805368aceb9 100644 --- a/core/modules/tour/tour.module +++ b/core/modules/tour/tour.module @@ -5,7 +5,6 @@ * Main functions of the module. */ use Drupal\Core\Cache\CacheBackendInterface; -use Symfony\Cmf\Component\Routing\RouteObjectInterface; use Symfony\Component\HttpFoundation\Request; /** @@ -83,8 +82,8 @@ function tour_preprocess_page(&$variables) { } // Load all of the items and match on route name. - $request = \Drupal::request(); - $route_name = $request->attributes->get(RouteObjectInterface::ROUTE_NAME); + $route_match = \Drupal::routeMatch(); + $route_name = $route_match->getRouteName(); $results = \Drupal::entityQuery('tour') ->condition('routes.*.route_name', $route_name) @@ -92,7 +91,7 @@ function tour_preprocess_page(&$variables) { if (!empty($results) && $tours = entity_load_multiple('tour', array_keys($results))) { foreach ($tours as $id => $tour) { // Match on params. - if (!$tour->hasMatchingRoute($route_name, $request->attributes->get('_raw_variables')->all())) { + if (!$tour->hasMatchingRoute($route_name, $route_match->getRawParameters()->all())) { unset($tours[$id]); } } diff --git a/core/modules/user/src/Theme/AdminNegotiator.php b/core/modules/user/src/Theme/AdminNegotiator.php index ce6f04bc6251f64b77594817370a13a9590244d5..9bb942ab2d1ebe5d02f74b96060932a0a991aa09 100644 --- a/core/modules/user/src/Theme/AdminNegotiator.php +++ b/core/modules/user/src/Theme/AdminNegotiator.php @@ -10,10 +10,9 @@ use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Routing\AdminContext; +use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Session\AccountInterface; use Drupal\Core\Theme\ThemeNegotiatorInterface; -use Symfony\Cmf\Component\Routing\RouteObjectInterface; -use Symfony\Component\HttpFoundation\Request; /** * Sets the active theme on admin pages. @@ -68,14 +67,14 @@ public function __construct(AccountInterface $user, ConfigFactoryInterface $conf /** * {@inheritdoc} */ - public function applies(Request $request) { - return ($this->entityManager->hasController('user_role', 'storage') && $this->user->hasPermission('view the administration theme') && $this->adminContext->isAdminRoute($request->attributes->get(RouteObjectInterface::ROUTE_OBJECT))); + public function applies(RouteMatchInterface $route_match) { + return ($this->entityManager->hasController('user_role', 'storage') && $this->user->hasPermission('view the administration theme') && $this->adminContext->isAdminRoute($route_match->getRouteObject())); } /** * {@inheritdoc} */ - public function determineActiveTheme(Request $request) { + public function determineActiveTheme(RouteMatchInterface $route_match) { return $this->configFactory->get('system.theme')->get('admin'); } diff --git a/core/tests/Drupal/Tests/Core/Routing/CurrentRouteMatchTest.php b/core/tests/Drupal/Tests/Core/Routing/CurrentRouteMatchTest.php new file mode 100644 index 0000000000000000000000000000000000000000..9deddd5d2c0a9d14c78154f384ac701d9ab0f8f3 --- /dev/null +++ b/core/tests/Drupal/Tests/Core/Routing/CurrentRouteMatchTest.php @@ -0,0 +1,93 @@ +<?php + +/** + * @file + * Contains Drupal\Tests\Core\Routing\RouteMatchTest. + */ + +namespace Drupal\Tests\Core\Routing; + +use Drupal\Core\Routing\CurrentRouteMatch; +use Drupal\Core\Routing\RouteMatch; +use Drupal\Tests\UnitTestCase; +use Symfony\Cmf\Component\Routing\RouteObjectInterface; +use Symfony\Component\HttpFoundation\ParameterBag; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\Routing\Route; + +/** + * Unit tests for CurrentRouteMatch. + * + * @coversDefaultClass \Drupal\Core\Routing\CurrentRouteMatch + */ +class CurrentRouteMatchTest extends RouteMatchBaseTest { + + /** + * {@inheritdoc} + */ + public static function getInfo() { + return array( + 'name' => 'CurrentRouteMatch', + 'description' => 'Unit tests for CurrentRouteMatch.', + 'group' => 'Routing', + ); + } + + /** + * {@inheritdoc} + */ + protected function getRouteMatch($name, Route $route, array $parameters, array $raw_parameters) { + $request_stack = new RequestStack(); + $request = new Request(); + $request_stack->push($request); + + $request = $request_stack->getCurrentRequest(); + $request->attributes = new ParameterBag($parameters); + $request->attributes->set(RouteObjectInterface::ROUTE_NAME, $name); + $request->attributes->set(RouteObjectInterface::ROUTE_OBJECT, $route); + $request->attributes->set('_raw_variables', new ParameterBag($raw_parameters)); + return new CurrentRouteMatch($request_stack); + } + + /** + * @covers ::__construct + * @covers ::getRouteObject + * @covers ::getCurrentRouteMatch + * @covers ::getRouteMatch + */ + public function testGetCurrentRouteObject() { + + $request_stack = new RequestStack(); + $request = new Request(); + $request_stack->push($request); + $current_route_match = new CurrentRouteMatch($request_stack); + + // Before routing. + $this->assertNull($current_route_match->getRouteObject()); + + // After routing. + $route = new Route('/test-route/{foo}'); + $request->attributes->set(RouteObjectInterface::ROUTE_NAME, 'test_route'); + $request->attributes->set(RouteObjectInterface::ROUTE_OBJECT, $route); + $request->attributes->set('foo', '1'); + $this->assertSame('1', $current_route_match->getParameter('foo')); + + // Immutable for the same request once a route has been matched. + $request->attributes->set('foo', '2'); + $this->assertSame('1', $current_route_match->getParameter('foo')); + + // Subrequest. + $subrequest = new Request(); + $subrequest->attributes->set(RouteObjectInterface::ROUTE_NAME, 'test_subrequest_route'); + $subrequest->attributes->set(RouteObjectInterface::ROUTE_OBJECT, new Route('/test-subrequest-route/{foo}')); + $subrequest->attributes->set('foo', '2'); + $request_stack->push($subrequest); + $this->assertSame('2', $current_route_match->getParameter('foo')); + + // Restored original request. + $request_stack->pop(); + $this->assertSame('1', $current_route_match->getParameter('foo')); + } + +} diff --git a/core/tests/Drupal/Tests/Core/Routing/RouteMatchBaseTest.php b/core/tests/Drupal/Tests/Core/Routing/RouteMatchBaseTest.php new file mode 100644 index 0000000000000000000000000000000000000000..57b03e3370b0d079ed6a5bb7f266b39cb3ba02c4 --- /dev/null +++ b/core/tests/Drupal/Tests/Core/Routing/RouteMatchBaseTest.php @@ -0,0 +1,149 @@ +<?php + +/** + * @file + * Contains Drupal\Tests\Core\Routing\RouteMatchTest. + */ + +namespace Drupal\Tests\Core\Routing; + +use Drupal\Core\Routing\RouteMatchInterface; +use Drupal\Tests\UnitTestCase; +use Symfony\Cmf\Component\Routing\RouteObjectInterface; +use Symfony\Component\HttpFoundation\ParameterBag; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\Routing\Route; + +/** + * Base test class for testing the RouteMatch object. + */ +abstract class RouteMatchBaseTest extends UnitTestCase { + + /** + * Build a test route match object for the given implementation. + * + * @param $name + * Route name. + * @param Route $route + * Request object + * @param array $parameters + * Parameters array + * @param $raw_parameters + * Raw parameters array + * @return \Drupal\Core\Routing\RouteMatchInterface + */ + abstract protected function getRouteMatch($name, Route $route, array $parameters, array $raw_parameters); + + + /** + * Provide sets of parameters and expected parameters for parameter tests. + */ + public function routeMatchProvider() { + $base_data = array( + array( + new Route( + '/test-route/{param_without_leading_underscore}/{_param_with_leading_underscore}', + array( + 'default_without_leading_underscore' => NULL, + '_default_with_leading_underscore' => NULL, + ) + ), + array( + 'param_without_leading_underscore' => 'value', + '_param_with_leading_underscore' => 'value', + 'default_without_leading_underscore' => 'value', + '_default_with_leading_underscore' => 'value', + 'foo' => 'value', + ), + // Parameters should be filtered to only those defined by the route. + // Specifically: + // - Path parameters, regardless of name. + // - Defaults that are not path parameters only if they do not start with + // an underscore. + array( + 'param_without_leading_underscore' => 'value', + '_param_with_leading_underscore' => 'value', + 'default_without_leading_underscore' => 'value', + ), + ), + ); + + $data = array(); + foreach ($base_data as $entry) { + $route = $entry[0]; + $params = $entry[1]; + $expected_params = $entry[2]; + $data[] = array( + $this->getRouteMatch('test_route', $route, $params, $params), + $route, + $params, + $expected_params, + ); + } + + return $data; + } + + /** + * @covers ::getRouteName + * @dataProvider routeMatchProvider + */ + public function testGetRouteName(RouteMatchInterface $route_match) { + $this->assertSame('test_route', $route_match->getRouteName()); + } + + /** + * @covers ::getRouteObject + * @dataProvider routeMatchProvider + */ + public function testGetRouteObject(RouteMatchInterface $route_match, Route $route) { + $this->assertSame($route, $route_match->getRouteObject()); + } + + /** + * @covers ::getParameter + * @covers \Drupal\Core\Routing\RouteMatch::getParameterNames + * @dataProvider routeMatchProvider + */ + public function testGetParameter(RouteMatchInterface $route_match, Route $route, $parameters, $expected_filtered_parameters) { + foreach ($expected_filtered_parameters as $name => $expected_value) { + $this->assertSame($expected_value, $route_match->getParameter($name)); + } + foreach (array_diff_key($parameters, $expected_filtered_parameters) as $name) { + $this->assertNull($route_match->getParameter($name)); + } + } + + /** + * @covers ::getParameters + * @covers \Drupal\Core\Routing\RouteMatch::getParameterNames + * @dataProvider routeMatchProvider + */ + public function testGetParameters(RouteMatchInterface $route_match, Route $route, $parameters, $expected_filtered_parameters) { + $this->assertSame($expected_filtered_parameters, $route_match->getParameters()->all()); + } + + /** + * @covers ::getRawParameter + * @covers \Drupal\Core\Routing\RouteMatch::getParameterNames + * @dataProvider routeMatchProvider + */ + public function testGetRawParameter(RouteMatchInterface $route_match, Route $route, $parameters, $expected_filtered_parameters) { + foreach ($expected_filtered_parameters as $name => $expected_value) { + $this->assertSame($expected_value, $route_match->getRawParameter($name)); + } + foreach (array_diff_key($parameters, $expected_filtered_parameters) as $name) { + $this->assertNull($route_match->getRawParameter($name)); + } + } + + /** + * @covers ::getRawParameters + * @covers \Drupal\Core\Routing\RouteMatch::getParameterNames + * @dataProvider routeMatchProvider + */ + public function testGetRawParameters(RouteMatchInterface $route_match, Route $route, $parameters, $expected_filtered_parameters) { + $this->assertSame($expected_filtered_parameters, $route_match->getRawParameters()->all()); + } + +} diff --git a/core/tests/Drupal/Tests/Core/Routing/RouteMatchTest.php b/core/tests/Drupal/Tests/Core/Routing/RouteMatchTest.php new file mode 100644 index 0000000000000000000000000000000000000000..6a51add267bd89a43c8bf2fbfd6aacb2aee1b4fb --- /dev/null +++ b/core/tests/Drupal/Tests/Core/Routing/RouteMatchTest.php @@ -0,0 +1,80 @@ +<?php + +/** + * @file + * Contains Drupal\Tests\Core\Routing\RouteMatchTest. + */ + +namespace Drupal\Tests\Core\Routing; + +use Drupal\Core\Routing\RouteMatch; +use Drupal\Tests\UnitTestCase; +use Symfony\Cmf\Component\Routing\RouteObjectInterface; +use Symfony\Component\HttpFoundation\ParameterBag; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\Routing\Route; + +/** + * Unit tests for RouteMatch. + * + * @coversDefaultClass \Drupal\Core\Routing\RouteMatch + */ +class RouteMatchTest extends RouteMatchBaseTest { + + /** + * {@inheritdoc} + */ + public static function getInfo() { + return array( + 'name' => 'RouteMatch', + 'description' => 'Unit tests for RouteMatch.', + 'group' => 'Routing', + ); + } + + /** + * {@inheritdoc} + */ + protected function getRouteMatch($name, Route $route, array $parameters, array $raw_parameters) { + return new RouteMatch($name, $route, $parameters, $raw_parameters); + } + + /** + * @covers ::createFromRequest + * @covers ::__construct + * @covers \Drupal\Core\Routing\NullRouteMatch + */ + public function testRouteMatchFromRequest() { + $request = new Request(); + + // A request that hasn't been routed yet. + $route_match = RouteMatch::createFromRequest($request); + $this->assertNull($route_match->getRouteName()); + $this->assertNull($route_match->getRouteObject()); + $this->assertSame(array(), $route_match->getParameters()->all()); + $this->assertNull($route_match->getParameter('foo')); + $this->assertSame(array(), $route_match->getRawParameters()->all()); + $this->assertNull($route_match->getRawParameter('foo')); + + // A routed request without parameter upcasting. + $route = new Route('/test-route/{foo}'); + $request->attributes->set(RouteObjectInterface::ROUTE_NAME, 'test_route'); + $request->attributes->set(RouteObjectInterface::ROUTE_OBJECT, $route); + $request->attributes->set('foo', '1'); + $route_match = RouteMatch::createFromRequest($request); + $this->assertSame('test_route', $route_match->getRouteName()); + $this->assertSame($route, $route_match->getRouteObject()); + $this->assertSame(array('foo' => '1'), $route_match->getParameters()->all()); + $this->assertSame(array(), $route_match->getRawParameters()->all()); + + // A routed request with parameter upcasting. + $foo = new \stdClass(); + $foo->value = 1; + $request->attributes->set('foo', $foo); + $request->attributes->set('_raw_variables', new ParameterBag(array('foo' => '1'))); + $route_match = RouteMatch::createFromRequest($request); + $this->assertSame(array('foo' => $foo), $route_match->getParameters()->all()); + $this->assertSame(array('foo' => '1'), $route_match->getRawParameters()->all()); + } + +} diff --git a/core/tests/Drupal/Tests/Core/Theme/ThemeNegotiatorTest.php b/core/tests/Drupal/Tests/Core/Theme/ThemeNegotiatorTest.php index 89a1e46358c11b27e306b08114af8acc1fe00594..4830e4757de6bdcf260b3cb6e0f143bbcaac2a06 100644 --- a/core/tests/Drupal/Tests/Core/Theme/ThemeNegotiatorTest.php +++ b/core/tests/Drupal/Tests/Core/Theme/ThemeNegotiatorTest.php @@ -7,10 +7,12 @@ namespace Drupal\Tests\Core\Theme; +use Drupal\Core\Routing\RouteMatch; use Drupal\Core\Theme\ThemeNegotiator; use Drupal\Tests\UnitTestCase; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\Routing\Route; /** * Tests the theme negotiator. @@ -54,8 +56,7 @@ protected function setUp() { $this->themeAccessCheck = $this->getMockBuilder('\Drupal\Core\Theme\ThemeAccessCheck') ->disableOriginalConstructor() ->getMock(); - $this->requestStack = new RequestStack(); - $this->themeNegotiator = new ThemeNegotiator($this->themeAccessCheck, $this->requestStack); + $this->themeNegotiator = new ThemeNegotiator($this->themeAccessCheck); } /** @@ -78,11 +79,10 @@ public function testDetermineActiveTheme() { ->method('checkAccess') ->will($this->returnValue(TRUE)); - $request = Request::create('/test-route'); - $theme = $this->themeNegotiator->determineActiveTheme($request); + $route_match = new RouteMatch('test_route', new Route('/test-route'), array(), array()); + $theme = $this->themeNegotiator->determineActiveTheme($route_match); $this->assertEquals('example_test', $theme); - $this->assertEquals('example_test', $request->attributes->get('_theme_active')); } /** @@ -113,11 +113,10 @@ public function testDetermineActiveThemeWithPriority() { ->method('checkAccess') ->will($this->returnValue(TRUE)); - $request = Request::create('/test-route'); - $theme = $this->themeNegotiator->determineActiveTheme($request); + $route_match = new RouteMatch('test_route', new Route('/test-route'), array(), array()); + $theme = $this->themeNegotiator->determineActiveTheme($route_match); $this->assertEquals('example_test', $theme); - $this->assertEquals('example_test', $request->attributes->get('_theme_active')); } /** @@ -156,11 +155,10 @@ public function testDetermineActiveThemeWithAccessCheck() { ->with('example_test2') ->will($this->returnValue(TRUE)); - $request = Request::create('/test-route'); - $theme = $this->themeNegotiator->determineActiveTheme($request); + $route_match = new RouteMatch('test_route', new Route('/test-route'), array(), array()); + $theme = $this->themeNegotiator->determineActiveTheme($route_match); $this->assertEquals('example_test2', $theme); - $this->assertEquals('example_test2', $request->attributes->get('_theme_active')); } /** @@ -192,11 +190,10 @@ public function testDetermineActiveThemeWithNotApplyingNegotiator() { ->method('checkAccess') ->will($this->returnValue(TRUE)); - $request = Request::create('/test-route'); - $theme = $this->themeNegotiator->determineActiveTheme($request); + $route_match = new RouteMatch('test_route', new Route('/test-route'), array(), array()); + $theme = $this->themeNegotiator->determineActiveTheme($route_match); $this->assertEquals('example_test2', $theme); - $this->assertEquals('example_test2', $request->attributes->get('_theme_active')); } }