Commit 3b84f7b2 authored by catch's avatar catch

Issue #2238217 by effulgentsia, neclimdul, martin107, tim.plunkett, cweagans,...

Issue #2238217 by effulgentsia, neclimdul, martin107, tim.plunkett, cweagans, kim.pepper, xjm: Introduce a RouteMatch class.
parent 6140e65c
...@@ -19,7 +19,7 @@ services: ...@@ -19,7 +19,7 @@ services:
- { name: cache.context} - { name: cache.context}
cache_context.theme: cache_context.theme:
class: Drupal\Core\Cache\ThemeCacheContext class: Drupal\Core\Cache\ThemeCacheContext
arguments: ['@request_stack', '@theme.negotiator'] arguments: ['@current_route_match', '@theme.negotiator']
tags: tags:
- { name: cache.context} - { name: cache.context}
cache_context.timezone: cache_context.timezone:
...@@ -201,7 +201,7 @@ services: ...@@ -201,7 +201,7 @@ services:
- { name: service_collector, tag: http_client_subscriber, call: attach } - { name: service_collector, tag: http_client_subscriber, call: attach }
theme.negotiator: theme.negotiator:
class: Drupal\Core\Theme\ThemeNegotiator class: Drupal\Core\Theme\ThemeNegotiator
arguments: ['@access_check.theme', '@request_stack'] arguments: ['@access_check.theme']
tags: tags:
- { name: service_collector, tag: theme_negotiator, call: addNegotiator } - { name: service_collector, tag: theme_negotiator, call: addNegotiator }
theme.negotiator.default: theme.negotiator.default:
...@@ -211,7 +211,7 @@ services: ...@@ -211,7 +211,7 @@ services:
- { name: theme_negotiator, priority: -100 } - { name: theme_negotiator, priority: -100 }
theme.negotiator.ajax_base_page: theme.negotiator.ajax_base_page:
class: Drupal\Core\Theme\AjaxBasePageNegotiator class: Drupal\Core\Theme\AjaxBasePageNegotiator
arguments: ['@csrf_token', '@config.factory'] arguments: ['@csrf_token', '@config.factory', '@request_stack']
tags: tags:
- { name: theme_negotiator, priority: 1000 } - { name: theme_negotiator, priority: 1000 }
container.namespaces: container.namespaces:
...@@ -273,6 +273,9 @@ services: ...@@ -273,6 +273,9 @@ services:
class: Symfony\Component\HttpFoundation\RequestStack class: Symfony\Component\HttpFoundation\RequestStack
tags: tags:
- { name: persist } - { name: persist }
current_route_match:
class: Drupal\Core\Routing\CurrentRouteMatch
arguments: ['@request_stack']
event_dispatcher: event_dispatcher:
class: Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher class: Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher
arguments: ['@service_container'] arguments: ['@service_container']
......
...@@ -8,7 +8,6 @@ ...@@ -8,7 +8,6 @@
use Drupal\Component\Utility\String; use Drupal\Component\Utility\String;
use Drupal\Core\Render\Element; use Drupal\Core\Render\Element;
use Drupal\Core\Template\Attribute; use Drupal\Core\Template\Attribute;
use Symfony\Cmf\Component\Routing\RouteObjectInterface;
/** /**
* @defgroup menu Menu and routing system * @defgroup menu Menu and routing system
...@@ -479,7 +478,7 @@ function menu_local_tasks($level = 0) { ...@@ -479,7 +478,7 @@ function menu_local_tasks($level = 0) {
$data['tabs'] = array(); $data['tabs'] = array();
$data['actions'] = array(); $data['actions'] = array();
$route_name = \Drupal::request()->attributes->get(RouteObjectInterface::ROUTE_NAME); $route_name = \Drupal::routeMatch()->getRouteName();
if (!empty($route_name)) { if (!empty($route_name)) {
$manager = \Drupal::service('plugin.manager.menu.local_task'); $manager = \Drupal::service('plugin.manager.menu.local_task');
$local_tasks = $manager->getTasksBuild($route_name); $local_tasks = $manager->getTasksBuild($route_name);
...@@ -534,7 +533,7 @@ function menu_secondary_local_tasks() { ...@@ -534,7 +533,7 @@ function menu_secondary_local_tasks() {
*/ */
function menu_get_local_actions() { function menu_get_local_actions() {
$links = menu_local_tasks(); $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'); $manager = \Drupal::service('plugin.manager.menu.local_action');
return $manager->getActionsForRoute($route_name) + $links['actions']; return $manager->getActionsForRoute($route_name) + $links['actions'];
} }
......
...@@ -104,8 +104,7 @@ function drupal_theme_initialize() { ...@@ -104,8 +104,7 @@ function drupal_theme_initialize() {
// @todo Let the theme.negotiator listen to the kernel request event. // @todo Let the theme.negotiator listen to the kernel request event.
// Determine the active theme for the theme negotiator service. This includes // 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. // the default theme as well as really specific ones like the ajax base theme.
$request = \Drupal::request(); $theme = \Drupal::service('theme.negotiator')->determineActiveTheme(\Drupal::routeMatch());
$theme = \Drupal::service('theme.negotiator')->determineActiveTheme($request);
// If no theme could be negotiated, or if the negotiated theme is not within // 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 // the list of enabled themes, fall back to the default theme output of core
...@@ -2133,7 +2132,7 @@ function template_preprocess_page(&$variables) { ...@@ -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; $variables['node'] = $node;
} }
......
...@@ -187,6 +187,16 @@ public static function request() { ...@@ -187,6 +187,16 @@ public static function request() {
return static::$container->get('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. * Gets the current active user.
* *
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
namespace Drupal\Core\Cache; namespace Drupal\Core\Cache;
use Symfony\Component\HttpFoundation\RequestStack; use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Theme\ThemeNegotiatorInterface; use Drupal\Core\Theme\ThemeNegotiatorInterface;
/** /**
...@@ -16,11 +16,11 @@ ...@@ -16,11 +16,11 @@
class ThemeCacheContext implements CacheContextInterface { 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. * The theme negotiator.
...@@ -32,13 +32,13 @@ class ThemeCacheContext implements CacheContextInterface { ...@@ -32,13 +32,13 @@ class ThemeCacheContext implements CacheContextInterface {
/** /**
* Constructs a new ThemeCacheContext service. * Constructs a new ThemeCacheContext service.
* *
* @param \Symfony\Component\HttpFoundation\RequestStack $request_stack * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
* The request stack. * The route match.
* @param \Drupal\Core\Theme\ThemeNegotiatorInterface $theme_negotiator * @param \Drupal\Core\Theme\ThemeNegotiatorInterface $theme_negotiator
* The theme negotiator. * The theme negotiator.
*/ */
public function __construct(RequestStack $request_stack, ThemeNegotiatorInterface $theme_negotiator) { public function __construct(RouteMatchInterface $route_match, ThemeNegotiatorInterface $theme_negotiator) {
$this->requestStack = $request_stack; $this->routeMatch = $route_match;
$this->themeNegotiator = $theme_negotiator; $this->themeNegotiator = $theme_negotiator;
} }
...@@ -53,8 +53,7 @@ public static function getLabel() { ...@@ -53,8 +53,7 @@ public static function getLabel() {
* {@inheritdoc} * {@inheritdoc}
*/ */
public function getContext() { public function getContext() {
$request = $this->requestStack->getCurrentRequest(); return $this->themeNegotiator->determineActiveTheme($this->routeMatch) ?: 'stark';
return $this->themeNegotiator->determineActiveTheme($request) ?: 'stark';
} }
} }
<?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;
}
}
<?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();
}
}
<?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;
}
}
<?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);