core/core.services.yml | 1 - core/lib/Drupal/Core/Access/RouteProcessorCsrf.php | 9 +- .../OutboundPathProcessorInterface.php | 8 +- .../Core/PathProcessor/PathProcessorAlias.php | 3 +- .../Core/PathProcessor/PathProcessorFront.php | 5 +- .../Core/PathProcessor/PathProcessorManager.php | 5 +- core/lib/Drupal/Core/Render/Element/Link.php | 11 +- .../OutboundRouteProcessorInterface.php | 5 +- .../Core/RouteProcessor/RouteProcessorCurrent.php | 6 +- .../Core/RouteProcessor/RouteProcessorManager.php | 5 +- core/lib/Drupal/Core/Routing/NullGenerator.php | 5 +- core/lib/Drupal/Core/Routing/UrlGenerator.php | 20 ++- .../Drupal/Core/Routing/UrlGeneratorInterface.php | 7 +- core/lib/Drupal/Core/Template/TwigExtension.php | 36 ++--- core/lib/Drupal/Core/Url.php | 8 +- core/lib/Drupal/Core/Utility/LinkGenerator.php | 5 +- .../Drupal/Core/Utility/LinkGeneratorInterface.php | 3 +- .../Drupal/Core/Utility/UnroutedUrlAssembler.php | 14 +- .../Core/Utility/UnroutedUrlAssemblerInterface.php | 3 +- .../modules/help_test/src/SuperNovaGenerator.php | 3 +- .../src/HttpKernel/PathProcessorLanguage.php | 5 +- .../LanguageNegotiationSession.php | 6 +- .../LanguageNegotiation/LanguageNegotiationUrl.php | 9 +- .../MenuLinkContentCacheabilityBubblingTest.php | 149 +++++++++++++++++++++ .../outbound_processing_test.info.yml | 4 + .../outbound_processing_test.routing.yml | 5 + core/modules/menu_ui/src/MenuForm.php | 2 +- core/modules/shortcut/src/Form/SetCustomize.php | 6 +- .../url_alter_test/src/PathProcessorTest.php | 6 +- .../Tests/Core/Access/RouteProcessorCsrfTest.php | 22 ++- .../Drupal/Tests/Core/Entity/EntityUrlTest.php | 2 + .../Drupal/Tests/Core/Form/FormSubmitterTest.php | 4 +- .../RouteProcessor/RouteProcessorManagerTest.php | 7 +- core/tests/Drupal/Tests/Core/UrlTest.php | 16 +-- 34 files changed, 314 insertions(+), 91 deletions(-) diff --git a/core/core.services.yml b/core/core.services.yml index 6579204..4dce309 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -1315,7 +1315,6 @@ services: - { name: twig.extension, priority: 100 } calls: - [setGenerators, ['@url_generator']] - - [setLinkGenerator, ['@link_generator']] # @todo Figure out what to do about debugging functions. # @see http://drupal.org/node/1804998 twig.extension.debug: diff --git a/core/lib/Drupal/Core/Access/RouteProcessorCsrf.php b/core/lib/Drupal/Core/Access/RouteProcessorCsrf.php index 49873fa..f00053c 100644 --- a/core/lib/Drupal/Core/Access/RouteProcessorCsrf.php +++ b/core/lib/Drupal/Core/Access/RouteProcessorCsrf.php @@ -7,9 +7,8 @@ namespace Drupal\Core\Access; +use Drupal\Core\Cache\CacheableMetadata; use Drupal\Core\RouteProcessor\OutboundRouteProcessorInterface; -use Drupal\Core\Access\CsrfTokenGenerator; -use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\Route; /** @@ -37,7 +36,7 @@ function __construct(CsrfTokenGenerator $csrf_token) { /** * {@inheritdoc} */ - public function processOutbound($route_name, Route $route, array &$parameters) { + public function processOutbound($route_name, Route $route, array &$parameters, CacheableMetadata $cacheable_metadata = NULL) { if ($route->hasRequirement('_csrf_token')) { $path = ltrim($route->getPath(), '/'); // Replace the path parameters with values from the parameters array. @@ -47,6 +46,10 @@ public function processOutbound($route_name, Route $route, array &$parameters) { // Adding this to the parameters means it will get merged into the query // string when the route is compiled. $parameters['token'] = $this->csrfToken->get($path); + if ($cacheable_metadata) { + // Tokens are per user and per session, so not cacheable. + $cacheable_metadata->setCacheMaxAge(0); + } } } diff --git a/core/lib/Drupal/Core/PathProcessor/OutboundPathProcessorInterface.php b/core/lib/Drupal/Core/PathProcessor/OutboundPathProcessorInterface.php index 9e69001..993c1c5 100644 --- a/core/lib/Drupal/Core/PathProcessor/OutboundPathProcessorInterface.php +++ b/core/lib/Drupal/Core/PathProcessor/OutboundPathProcessorInterface.php @@ -7,8 +7,8 @@ namespace Drupal\Core\PathProcessor; +use Drupal\Core\Cache\CacheableMetadata; use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\Routing\Route; /** * Defines an interface for classes that process the outbound path. @@ -20,17 +20,17 @@ * * @param string $path * The path to process. - * * @param array $options * An array of options such as would be passed to the generator's * generateFromPath() method. - * * @param \Symfony\Component\HttpFoundation\Request $request * The HttpRequest object representing the current request. + * @param \Drupal\Core\Cache\CacheableMetadata $cacheable_metadata + * (optional) Object to collect path processors' cacheability metadata. * * @return * The processed path. */ - public function processOutbound($path, &$options = array(), Request $request = NULL); + public function processOutbound($path, &$options = array(), Request $request = NULL, CacheableMetadata $cacheable_metadata = NULL); } diff --git a/core/lib/Drupal/Core/PathProcessor/PathProcessorAlias.php b/core/lib/Drupal/Core/PathProcessor/PathProcessorAlias.php index 67226ab..206e566 100644 --- a/core/lib/Drupal/Core/PathProcessor/PathProcessorAlias.php +++ b/core/lib/Drupal/Core/PathProcessor/PathProcessorAlias.php @@ -7,6 +7,7 @@ namespace Drupal\Core\PathProcessor; +use Drupal\Core\Cache\CacheableMetadata; use Drupal\Core\Path\AliasManagerInterface; use Symfony\Component\HttpFoundation\Request; @@ -43,7 +44,7 @@ public function processInbound($path, Request $request) { /** * Implements Drupal\Core\PathProcessor\OutboundPathProcessorInterface::processOutbound(). */ - public function processOutbound($path, &$options = array(), Request $request = NULL) { + public function processOutbound($path, &$options = array(), Request $request = NULL, CacheableMetadata $cacheable_metadata = NULL) { if (empty($options['alias'])) { $langcode = isset($options['language']) ? $options['language']->getId() : NULL; $path = $this->aliasManager->getAliasByPath($path, $langcode); diff --git a/core/lib/Drupal/Core/PathProcessor/PathProcessorFront.php b/core/lib/Drupal/Core/PathProcessor/PathProcessorFront.php index e8ac5d9..9b6048f7 100644 --- a/core/lib/Drupal/Core/PathProcessor/PathProcessorFront.php +++ b/core/lib/Drupal/Core/PathProcessor/PathProcessorFront.php @@ -7,11 +7,14 @@ namespace Drupal\Core\PathProcessor; +use Drupal\Core\Cache\CacheableMetadata; use Drupal\Core\Config\ConfigFactoryInterface; use Symfony\Component\HttpFoundation\Request; /** * Processes the inbound path by resolving it to the front page if empty. + * + * @todo - remove ::processOutbound() when we remove UrlGenerator::fromPath(). */ class PathProcessorFront implements InboundPathProcessorInterface, OutboundPathProcessorInterface { @@ -45,7 +48,7 @@ public function processInbound($path, Request $request) { /** * Implements Drupal\Core\PathProcessor\OutboundPathProcessorInterface::processOutbound(). */ - public function processOutbound($path, &$options = array(), Request $request = NULL) { + public function processOutbound($path, &$options = array(), Request $request = NULL, CacheableMetadata $cacheable_metadata = NULL) { // The special path '' links to the default front page. if ($path == '') { $path = ''; diff --git a/core/lib/Drupal/Core/PathProcessor/PathProcessorManager.php b/core/lib/Drupal/Core/PathProcessor/PathProcessorManager.php index 3c8b9be..1bedad8 100644 --- a/core/lib/Drupal/Core/PathProcessor/PathProcessorManager.php +++ b/core/lib/Drupal/Core/PathProcessor/PathProcessorManager.php @@ -7,6 +7,7 @@ namespace Drupal\Core\PathProcessor; +use Drupal\Core\Cache\CacheableMetadata; use Symfony\Component\HttpFoundation\Request; /** @@ -107,10 +108,10 @@ public function addOutbound(OutboundPathProcessorInterface $processor, $priority /** * Implements Drupal\Core\PathProcessor\OutboundPathProcessorInterface::processOutbound(). */ - public function processOutbound($path, &$options = array(), Request $request = NULL) { + public function processOutbound($path, &$options = array(), Request $request = NULL, CacheableMetadata $cacheable_metadata = NULL) { $processors = $this->getOutbound(); foreach ($processors as $processor) { - $path = $processor->processOutbound($path, $options, $request); + $path = $processor->processOutbound($path, $options, $request, $cacheable_metadata); } return $path; } diff --git a/core/lib/Drupal/Core/Render/Element/Link.php b/core/lib/Drupal/Core/Render/Element/Link.php index c6714be..36269f6 100644 --- a/core/lib/Drupal/Core/Render/Element/Link.php +++ b/core/lib/Drupal/Core/Render/Element/Link.php @@ -9,6 +9,8 @@ use Drupal\Component\Utility\NestedArray; use Drupal\Component\Utility\Html as HtmlUtility; +use Drupal\Core\Cache\CacheableMetadata; +use Drupal\Core\Url as CoreUrl; /** * Provides a link render element. @@ -75,9 +77,14 @@ public static function preRenderLink($element) { $element = static::preRenderAjaxForm($element); } - if (!empty($element['#url'])) { + if (!empty($element['#url']) && $element['#url'] instanceof CoreUrl) { $options = NestedArray::mergeDeep($element['#url']->getOptions(), $element['#options']); - $element['#markup'] = \Drupal::l($element['#title'], $element['#url']->setOptions($options)); + /** @var \Drupal\Core\Utility\LinkGenerator $link_generator */ + $link_generator = \Drupal::service('link_generator'); + $cacheable_metadata = new CacheableMetadata(); + $element['#markup'] = $link_generator->generate($element['#title'], $element['#url']->setOptions($options), $cacheable_metadata); + $merged = $cacheable_metadata->merge(CacheableMetadata::createFromRenderArray($element)); + $merged->applyTo($element); } return $element; } diff --git a/core/lib/Drupal/Core/RouteProcessor/OutboundRouteProcessorInterface.php b/core/lib/Drupal/Core/RouteProcessor/OutboundRouteProcessorInterface.php index 145c8ee..f7487da 100644 --- a/core/lib/Drupal/Core/RouteProcessor/OutboundRouteProcessorInterface.php +++ b/core/lib/Drupal/Core/RouteProcessor/OutboundRouteProcessorInterface.php @@ -7,6 +7,7 @@ namespace Drupal\Core\RouteProcessor; +use Drupal\Core\Cache\CacheableMetadata; use Symfony\Component\Routing\Route; /** @@ -24,10 +25,12 @@ * @param array $parameters * An array of parameters to be passed to the route compiler. Passed by * reference. + * @param \Drupal\Core\Cache\CacheableMetadata $cacheable_metadata + * (optional) Object to collect path processors' cacheability metadata. * * @return * The processed path. */ - public function processOutbound($route_name, Route $route, array &$parameters); + public function processOutbound($route_name, Route $route, array &$parameters, CacheableMetadata $cacheable_metadata = NULL); } diff --git a/core/lib/Drupal/Core/RouteProcessor/RouteProcessorCurrent.php b/core/lib/Drupal/Core/RouteProcessor/RouteProcessorCurrent.php index 1a00b21..17a56b5 100644 --- a/core/lib/Drupal/Core/RouteProcessor/RouteProcessorCurrent.php +++ b/core/lib/Drupal/Core/RouteProcessor/RouteProcessorCurrent.php @@ -7,6 +7,7 @@ namespace Drupal\Core\RouteProcessor; +use Drupal\Core\Cache\CacheableMetadata; use Drupal\Core\Routing\RouteMatchInterface; use Symfony\Component\Routing\Route; @@ -35,7 +36,7 @@ public function __construct(RouteMatchInterface $route_match) { /** * {@inheritdoc} */ - public function processOutbound($route_name, Route $route, array &$parameters) { + public function processOutbound($route_name, Route $route, array &$parameters, CacheableMetadata $cacheable_metadata = NULL) { if ($route_name === '') { if ($current_route = $this->routeMatch->getRouteObject()) { $route->setPath($current_route->getPath()); @@ -43,6 +44,9 @@ public function processOutbound($route_name, Route $route, array &$parameters) { $route->setOptions($current_route->getOptions()); $route->setDefaults($current_route->getDefaults()); $parameters = array_merge($parameters, $this->routeMatch->getRawParameters()->all()); + if ($cacheable_metadata) { + $cacheable_metadata->addCacheContexts(['route']); + } } else { // If we have no current route match available, point to the frontpage. diff --git a/core/lib/Drupal/Core/RouteProcessor/RouteProcessorManager.php b/core/lib/Drupal/Core/RouteProcessor/RouteProcessorManager.php index 049fc7c..589af5a 100644 --- a/core/lib/Drupal/Core/RouteProcessor/RouteProcessorManager.php +++ b/core/lib/Drupal/Core/RouteProcessor/RouteProcessorManager.php @@ -7,6 +7,7 @@ namespace Drupal\Core\RouteProcessor; +use Drupal\Core\Cache\CacheableMetadata; use Symfony\Component\Routing\Route; /** @@ -50,10 +51,10 @@ public function addOutbound(OutboundRouteProcessorInterface $processor, $priorit /** * {@inheritdoc} */ - public function processOutbound($route_name, Route $route, array &$parameters) { + public function processOutbound($route_name, Route $route, array &$parameters, CacheableMetadata $cacheable_metadata = NULL) { $processors = $this->getOutbound(); foreach ($processors as $processor) { - $processor->processOutbound($route_name, $route, $parameters); + $processor->processOutbound($route_name, $route, $parameters, $cacheable_metadata); } } diff --git a/core/lib/Drupal/Core/Routing/NullGenerator.php b/core/lib/Drupal/Core/Routing/NullGenerator.php index 74bb41e..986523f 100644 --- a/core/lib/Drupal/Core/Routing/NullGenerator.php +++ b/core/lib/Drupal/Core/Routing/NullGenerator.php @@ -7,6 +7,7 @@ namespace Drupal\Core\Routing; +use Drupal\Core\Cache\CacheableMetadata; use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\Routing\RequestContext as SymfonyRequestContext; use Symfony\Component\Routing\Exception\RouteNotFoundException; @@ -50,7 +51,7 @@ protected function getRoute($name) { /** * {@inheritdoc} */ - protected function processRoute($name, Route $route, array &$parameters) { + protected function processRoute($name, Route $route, array &$parameters, CacheableMetadata $cacheable_metadata = NULL) { } /** @@ -75,7 +76,7 @@ public function getContext() { /** * Overrides Drupal\Core\Routing\UrlGenerator::processPath(). */ - protected function processPath($path, &$options = array()) { + protected function processPath($path, &$options = array(), CacheableMetadata $cacheable_metadata = NULL) { return $path; } } diff --git a/core/lib/Drupal/Core/Routing/UrlGenerator.php b/core/lib/Drupal/Core/Routing/UrlGenerator.php index 97530d7..933a0fc 100644 --- a/core/lib/Drupal/Core/Routing/UrlGenerator.php +++ b/core/lib/Drupal/Core/Routing/UrlGenerator.php @@ -7,6 +7,7 @@ namespace Drupal\Core\Routing; +use Drupal\Core\Cache\CacheableMetadata; use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\Routing\RequestContext as SymfonyRequestContext; use Symfony\Component\Routing\Route as SymfonyRoute; @@ -275,11 +276,11 @@ public function generate($name, $parameters = array(), $absolute = FALSE) { /** * {@inheritdoc} */ - public function generateFromRoute($name, $parameters = array(), $options = array()) { + public function generateFromRoute($name, $parameters = array(), $options = array(), CacheableMetadata $cacheable_metadata = NULL) { $options += array('prefix' => ''); $route = $this->getRoute($name); $name = $this->getRouteDebugMessage($name); - $this->processRoute($name, $route, $parameters); + $this->processRoute($name, $route, $parameters, $cacheable_metadata); $query_params = []; // Symfony adds any parameters that are not path slugs as query strings. @@ -288,7 +289,7 @@ public function generateFromRoute($name, $parameters = array(), $options = array } $path = $this->getInternalPathFromRoute($name, $route, $parameters, $query_params); - $path = $this->processPath($path, $options); + $path = $this->processPath($path, $options, $cacheable_metadata); if (!empty($options['prefix'])) { $path = ltrim($path, '/'); @@ -349,6 +350,9 @@ public function generateFromRoute($name, $parameters = array(), $options = array } elseif ('https' === $scheme && 443 != $this->context->getHttpsPort()) { $port = ':' . $this->context->getHttpsPort(); } + if ($cacheable_metadata) { + $cacheable_metadata->addCacheContexts(['url.host']); + } return $scheme . '://' . $host . $port . $base_url . $path . $fragment; } @@ -457,7 +461,7 @@ public function generateFromPath($path = NULL, $options = array()) { /** * Passes the path to a processor manager to allow alterations. */ - protected function processPath($path, &$options = array()) { + protected function processPath($path, &$options = array(), CacheableMetadata $cacheable_metadata = NULL) { // Router-based paths may have a querystring on them. if ($query_pos = strpos($path, '?')) { // We don't need to do a strict check here because position 0 would mean we @@ -469,7 +473,7 @@ protected function processPath($path, &$options = array()) { $actual_path = $path; $query_string = ''; } - $path = '/' . $this->pathProcessor->processOutbound(trim($actual_path, '/'), $options, $this->requestStack->getCurrentRequest()); + $path = '/' . $this->pathProcessor->processOutbound(trim($actual_path, '/'), $options, $this->requestStack->getCurrentRequest(), $cacheable_metadata); $path .= $query_string; return $path; } @@ -483,9 +487,11 @@ protected function processPath($path, &$options = array()) { * The route object to process. * @param array $parameters * An array of parameters to be passed to the route compiler. + * @param \Drupal\Core\Cache\CacheableMetadata $cacheable_metadata + * (optional) Object to collect path processors' cacheability metadata. */ - protected function processRoute($name, SymfonyRoute $route, array &$parameters) { - $this->routeProcessor->processOutbound($name, $route, $parameters); + protected function processRoute($name, SymfonyRoute $route, array &$parameters, CacheableMetadata $cacheable_metadata = NULL) { + $this->routeProcessor->processOutbound($name, $route, $parameters, $cacheable_metadata); } /** diff --git a/core/lib/Drupal/Core/Routing/UrlGeneratorInterface.php b/core/lib/Drupal/Core/Routing/UrlGeneratorInterface.php index de28531..7f51b8c 100644 --- a/core/lib/Drupal/Core/Routing/UrlGeneratorInterface.php +++ b/core/lib/Drupal/Core/Routing/UrlGeneratorInterface.php @@ -7,6 +7,7 @@ namespace Drupal\Core\Routing; +use Drupal\Core\Cache\CacheableMetadata; use Symfony\Cmf\Component\Routing\VersatileGeneratorInterface; /** @@ -100,7 +101,7 @@ public function generateFromPath($path = NULL, $options = array()); * @return string * The internal Drupal path corresponding to the route. * - * @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0. + * @deprecated in Drupal 8.0.x-dev, will be removed before Drupal 8.0.0 * System paths should not be used - use route names and parameters. */ public function getPathFromRoute($name, $parameters = array()); @@ -136,6 +137,8 @@ public function getPathFromRoute($name, $parameters = array()); * modify the base URL when a language dependent URL requires so. * - 'prefix': Only used internally, to modify the path when a language * dependent URL requires so. + * @param \Drupal\Core\Cache\CacheableMetadata $cacheable_metadata + * (optional) Object to collect path processors' cacheability metadata. * * @return string * The generated URL for the given route. @@ -148,6 +151,6 @@ public function getPathFromRoute($name, $parameters = array()); * Thrown when a parameter value for a placeholder is not correct because it * does not match the requirement. */ - public function generateFromRoute($name, $parameters = array(), $options = array()); + public function generateFromRoute($name, $parameters = array(), $options = array(), CacheableMetadata $cacheable_metadata = NULL); } diff --git a/core/lib/Drupal/Core/Template/TwigExtension.php b/core/lib/Drupal/Core/Template/TwigExtension.php index bb29cd1..d02fab9 100644 --- a/core/lib/Drupal/Core/Template/TwigExtension.php +++ b/core/lib/Drupal/Core/Template/TwigExtension.php @@ -15,8 +15,6 @@ use Drupal\Component\Utility\SafeMarkup; use Drupal\Core\Render\RendererInterface; use Drupal\Core\Routing\UrlGeneratorInterface; -use Drupal\Core\Url; -use Drupal\Core\Utility\LinkGeneratorInterface; /** * A class providing Drupal Twig extensions. @@ -35,13 +33,6 @@ class TwigExtension extends \Twig_Extension { protected $urlGenerator; /** - * The link generator. - * - * @var \Drupal\Core\Utility\LinkGeneratorInterface - */ - protected $linkGenerator; - - /** * The renderer. * * @var \Drupal\Core\Render\RendererInterface @@ -72,19 +63,6 @@ public function setGenerators(UrlGeneratorInterface $url_generator) { } /** - * Sets the link generator. - * - * @param \Drupal\Core\Utility\LinkGeneratorInterface $link_generator - * The link generator. - * - * @return $this - */ - public function setLinkGenerator(LinkGeneratorInterface $link_generator) { - $this->linkGenerator = $link_generator; - return $this; - } - - /** * {@inheritdoc} */ public function getFunctions() { @@ -232,14 +210,16 @@ public function getUrlFromPath($path, $options = array()) { * @param \Drupal\Core\Url|string $url * The URL object or string used for the link. * - * @return string - * An HTML string containing a link to the given url. + * @return array + * A render array representing a link to the given URL. */ public function getLink($text, $url) { - if (!$url instanceof Url) { - $url = Url::fromUri($url); - } - return $this->linkGenerator->generate($text, $url); + $build = [ + '#type' => 'link', + '#title' => $text, + '#url' => $url, + ]; + return $build; } /** diff --git a/core/lib/Drupal/Core/Url.php b/core/lib/Drupal/Core/Url.php index c6a5f05..aa71db9 100644 --- a/core/lib/Drupal/Core/Url.php +++ b/core/lib/Drupal/Core/Url.php @@ -9,6 +9,7 @@ use Drupal\Component\Utility\SafeMarkup; use Drupal\Component\Utility\UrlHelper; +use Drupal\Core\Cache\CacheableMetadata; use Drupal\Core\DependencyInjection\DependencySerializationTrait; use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Routing\UrlGeneratorInterface; @@ -728,15 +729,18 @@ public function setAbsolute($absolute = TRUE) { * http://example.com/node/1 depending on the options array, plus any * specified query string or fragment. * + * @param \Drupal\Core\Cache\CacheableMetadata $cacheable_metadata + * Optional object to collect cache tags and contexts for the URL. + * * @return string * A string URL. */ - public function toString() { + public function toString(CacheableMetadata $cacheable_metadata = NULL) { if ($this->unrouted) { return $this->unroutedUrlAssembler()->assemble($this->getUri(), $this->getOptions()); } - return $this->urlGenerator()->generateFromRoute($this->getRouteName(), $this->getRouteParameters(), $this->getOptions()); + return $this->urlGenerator()->generateFromRoute($this->getRouteName(), $this->getRouteParameters(), $this->getOptions(), $cacheable_metadata); } /** diff --git a/core/lib/Drupal/Core/Utility/LinkGenerator.php b/core/lib/Drupal/Core/Utility/LinkGenerator.php index 8794836..c81fb9d 100644 --- a/core/lib/Drupal/Core/Utility/LinkGenerator.php +++ b/core/lib/Drupal/Core/Utility/LinkGenerator.php @@ -9,6 +9,7 @@ use Drupal\Component\Serialization\Json; use Drupal\Component\Utility\SafeMarkup; +use Drupal\Core\Cache\CacheableMetadata; use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Link; use Drupal\Core\Path\AliasManagerInterface; @@ -67,7 +68,7 @@ public function generateFromLink(Link $link) { * * @see system_page_attachments() */ - public function generate($text, Url $url) { + public function generate($text, Url $url, CacheableMetadata $cacheable_metadata = NULL) { // Performance: avoid Url::toString() needing to retrieve the URL generator // service from the container. $url->setUrlGenerator($this->urlGenerator); @@ -131,7 +132,7 @@ public function generate($text, Url $url) { // The result of the url generator is a plain-text URL. Because we are using // it here in an HTML argument context, we need to encode it properly. - $url = SafeMarkup::checkPlain($url->toString()); + $url = SafeMarkup::checkPlain($url->toString($cacheable_metadata)); // Make sure the link text is sanitized. $safe_text = SafeMarkup::escape($variables['text']); diff --git a/core/lib/Drupal/Core/Utility/LinkGeneratorInterface.php b/core/lib/Drupal/Core/Utility/LinkGeneratorInterface.php index 8d2d9fc..92cb681 100644 --- a/core/lib/Drupal/Core/Utility/LinkGeneratorInterface.php +++ b/core/lib/Drupal/Core/Utility/LinkGeneratorInterface.php @@ -7,6 +7,7 @@ namespace Drupal\Core\Utility; +use Drupal\Core\Cache\CacheableMetadata; use Drupal\Core\Link; use Drupal\Core\Url; @@ -72,7 +73,7 @@ * Thrown when a parameter value for a placeholder is not correct because it * does not match the requirement. */ - public function generate($text, Url $url); + public function generate($text, Url $url, CacheableMetadata $cacheable_metadata = NULL); /** * Renders a link from a link object. diff --git a/core/lib/Drupal/Core/Utility/UnroutedUrlAssembler.php b/core/lib/Drupal/Core/Utility/UnroutedUrlAssembler.php index efc966b..5c69bc9 100644 --- a/core/lib/Drupal/Core/Utility/UnroutedUrlAssembler.php +++ b/core/lib/Drupal/Core/Utility/UnroutedUrlAssembler.php @@ -9,6 +9,7 @@ use Drupal\Component\Utility\SafeMarkup; use Drupal\Component\Utility\UrlHelper; +use Drupal\Core\Cache\CacheableMetadata; use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\PathProcessor\OutboundPathProcessorInterface; use Symfony\Component\HttpFoundation\RequestStack; @@ -57,12 +58,12 @@ public function __construct(RequestStack $request_stack, ConfigFactoryInterface * This is a helper function that calls buildExternalUrl() or buildLocalUrl() * based on a check of whether the path is a valid external URL. */ - public function assemble($uri, array $options = []) { + public function assemble($uri, array $options = [], CacheableMetadata $cacheable_metadata = NULL) { // Note that UrlHelper::isExternal will return FALSE if the $uri has a // disallowed protocol. This is later made safe since we always add at // least a leading slash. if (parse_url($uri, PHP_URL_SCHEME) === 'base') { - return $this->buildLocalUrl($uri, $options); + return $this->buildLocalUrl($uri, $options, $cacheable_metadata); } elseif (UrlHelper::isExternal($uri)) { // UrlHelper::isExternal() only returns true for safe protocols. @@ -104,7 +105,7 @@ protected function buildExternalUrl($uri, array $options = []) { /** * {@inheritdoc} */ - protected function buildLocalUrl($uri, array $options = []) { + protected function buildLocalUrl($uri, array $options = [], CacheableMetadata $cacheable_metadata = NULL) { $this->addOptionDefaults($options); $request = $this->requestStack->getCurrentRequest(); @@ -122,7 +123,9 @@ protected function buildLocalUrl($uri, array $options = []) { // alias overview form: // @see \Drupal\path\Controller\PathController::adminOverview(). if (!empty($options['path_processing'])) { - $uri = $this->pathProcessor->processOutbound($uri, $options); + // Do not pass the request, since this is a special case and we do not + // want to include e.g. the request language in the processing. + $uri = $this->pathProcessor->processOutbound($uri, $options, NULL, $cacheable_metadata); } // Add any subdirectory where Drupal is installed. @@ -143,6 +146,9 @@ protected function buildLocalUrl($uri, array $options = []) { else { $base = $current_base_url; } + if ($cacheable_metadata) { + $cacheable_metadata->addCacheContexts(['url.host']); + } } else { $base = $current_base_path; diff --git a/core/lib/Drupal/Core/Utility/UnroutedUrlAssemblerInterface.php b/core/lib/Drupal/Core/Utility/UnroutedUrlAssemblerInterface.php index 868f0c1..edba5cd 100644 --- a/core/lib/Drupal/Core/Utility/UnroutedUrlAssemblerInterface.php +++ b/core/lib/Drupal/Core/Utility/UnroutedUrlAssemblerInterface.php @@ -5,6 +5,7 @@ */ namespace Drupal\Core\Utility; +use Drupal\Core\Cache\CacheableMetadata; /** * Provides a way to build external or non Drupal local domain URLs. @@ -52,6 +53,6 @@ * @throws \InvalidArgumentException * Thrown when the passed in path has no scheme. */ - public function assemble($uri, array $options = array()); + public function assemble($uri, array $options = array(), CacheableMetadata $cacheable_metadata = NULL); } diff --git a/core/modules/help/tests/modules/help_test/src/SuperNovaGenerator.php b/core/modules/help/tests/modules/help_test/src/SuperNovaGenerator.php index 9b33f94..619603e 100644 --- a/core/modules/help/tests/modules/help_test/src/SuperNovaGenerator.php +++ b/core/modules/help/tests/modules/help_test/src/SuperNovaGenerator.php @@ -7,6 +7,7 @@ namespace Drupal\help_test; +use Drupal\Core\Cache\CacheableMetadata; use Drupal\Core\Routing\UrlGeneratorInterface; use Symfony\Component\Routing\RequestContext; @@ -53,7 +54,7 @@ public function getPathFromRoute($name, $parameters = array()) { /** * {@inheritdoc} */ - public function generateFromRoute($name, $parameters = array(), $options = array()) { + public function generateFromRoute($name, $parameters = array(), $options = array(), CacheableMetadata $cacheable_metadata = NULL) { throw new \Exception(); } diff --git a/core/modules/language/src/HttpKernel/PathProcessorLanguage.php b/core/modules/language/src/HttpKernel/PathProcessorLanguage.php index f1402b5..6d9bb71 100644 --- a/core/modules/language/src/HttpKernel/PathProcessorLanguage.php +++ b/core/modules/language/src/HttpKernel/PathProcessorLanguage.php @@ -8,6 +8,7 @@ namespace Drupal\language\HttpKernel; use Drupal\Component\Utility\Unicode; +use Drupal\Core\Cache\CacheableMetadata; use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\PathProcessor\InboundPathProcessorInterface; use Drupal\Core\PathProcessor\OutboundPathProcessorInterface; @@ -94,7 +95,7 @@ public function processInbound($path, Request $request) { /** * {@inheritdoc} */ - public function processOutbound($path, &$options = array(), Request $request = NULL) { + public function processOutbound($path, &$options = array(), Request $request = NULL, CacheableMetadata $cacheable_metadata = NULL) { if (!isset($this->multilingual)) { $this->multilingual = $this->languageManager->isMultilingual(); } @@ -105,7 +106,7 @@ public function processOutbound($path, &$options = array(), Request $request = N $this->initProcessors($scope); } foreach ($this->processors[$scope] as $instance) { - $path = $instance->processOutbound($path, $options, $request); + $path = $instance->processOutbound($path, $options, $request, $cacheable_metadata); } // No language dependent path allowed in this mode. if (empty($this->processors[$scope])) { diff --git a/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationSession.php b/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationSession.php index a1547c0..9e5489c 100644 --- a/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationSession.php +++ b/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationSession.php @@ -7,6 +7,7 @@ namespace Drupal\language\Plugin\LanguageNegotiation; +use Drupal\Core\Cache\CacheableMetadata; use Drupal\Core\Language\LanguageInterface; use Drupal\Core\PathProcessor\OutboundPathProcessorInterface; use Drupal\Core\Url; @@ -85,7 +86,7 @@ public function persist(LanguageInterface $language) { /** * {@inheritdoc} */ - public function processOutbound($path, &$options = array(), Request $request = NULL) { + public function processOutbound($path, &$options = array(), Request $request = NULL, CacheableMetadata $cacheable_metadata = NULL) { if ($request) { // The following values are not supposed to change during a single page // request processing. @@ -114,6 +115,9 @@ public function processOutbound($path, &$options = array(), Request $request = N if (!isset($options['query'][$this->queryParam])) { $options['query'][$this->queryParam] = $this->queryValue; } + if ($cacheable_metadata) { + $cacheable_metadata->addCacheContexts(['languages:' . LanguageInterface::TYPE_URL]); + } } } return $path; diff --git a/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationUrl.php b/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationUrl.php index b76b1b1..3315517 100644 --- a/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationUrl.php +++ b/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationUrl.php @@ -7,6 +7,7 @@ namespace Drupal\language\Plugin\LanguageNegotiation; +use Drupal\Core\Cache\CacheableMetadata; use Drupal\Core\Language\LanguageInterface; use Drupal\Core\PathProcessor\InboundPathProcessorInterface; use Drupal\Core\PathProcessor\OutboundPathProcessorInterface; @@ -122,7 +123,7 @@ public function processInbound($path, Request $request) { /** * Implements Drupal\Core\PathProcessor\InboundPathProcessorInterface::processOutbound(). */ - public function processOutbound($path, &$options = array(), Request $request = NULL) { + public function processOutbound($path, &$options = array(), Request $request = NULL, CacheableMetadata $cacheable_metadata = NULL) { $url_scheme = 'http'; $port = 80; if ($request) { @@ -143,6 +144,9 @@ public function processOutbound($path, &$options = array(), Request $request = N if ($config['source'] == LanguageNegotiationUrl::CONFIG_PATH_PREFIX) { if (is_object($options['language']) && !empty($config['prefixes'][$options['language']->getId()])) { $options['prefix'] = $config['prefixes'][$options['language']->getId()] . '/'; + if ($cacheable_metadata) { + $cacheable_metadata->addCacheContexts(['languages:' . LanguageInterface::TYPE_URL]); + } } } elseif ($config['source'] == LanguageNegotiationUrl::CONFIG_DOMAIN) { @@ -180,6 +184,9 @@ public function processOutbound($path, &$options = array(), Request $request = N // Add Drupal's subfolder from the base_path if there is one. $options['base_url'] .= rtrim(base_path(), '/'); + if ($cacheable_metadata) { + $cacheable_metadata->addCacheContexts(['languages:' . LanguageInterface::TYPE_URL, 'url.host']); + } } } return $path; diff --git a/core/modules/menu_link_content/src/Tests/MenuLinkContentCacheabilityBubblingTest.php b/core/modules/menu_link_content/src/Tests/MenuLinkContentCacheabilityBubblingTest.php new file mode 100644 index 0000000..7980c77 --- /dev/null +++ b/core/modules/menu_link_content/src/Tests/MenuLinkContentCacheabilityBubblingTest.php @@ -0,0 +1,149 @@ +installEntitySchema('menu_link_content'); + $this->installEntitySchema('user'); + $this->installSchema('system', ['url_alias', 'router']); + + // Ensure that the weight of module_link_content is higher than system. + // @see menu_link_content_install() + module_set_weight('menu_link_content', 1); + } + + /** + * Tests bubbling of menu links' outbound route/path processing cacheability. + */ + public function testOutboundPathAndRouteProcessing() { + \Drupal::service('router.builder')->rebuild(); + + $request_stack = \Drupal::requestStack(); + /** @var \Symfony\Component\Routing\RequestContext $request_context */ + $request_context = \Drupal::service('router.request_context'); + + $request = Request::create('/'); + $request->attributes->set(RouteObjectInterface::ROUTE_NAME, ''); + $request->attributes->set(RouteObjectInterface::ROUTE_OBJECT, new Route('/')); + $request_stack->push($request); + $request_context->fromRequest($request); + + $menu_tree = \Drupal::menuTree(); + $renderer = \Drupal::service('renderer'); + + + $default_menu_cacheability = (new CacheableMetadata()) + ->setCacheMaxAge(Cache::PERMANENT) + ->setCacheTags(['config:system.menu.tools']) + ->setCacheContexts(['languages:' . LanguageInterface::TYPE_INTERFACE, 'theme']); + + User::create(['uid' => 1, 'name' => $this->randomString()])->save(); + User::create(['uid' => 2, 'name' => $this->randomString()])->save(); + + // Five test cases, four asserting one outbound path/route processor, and + // together covering one of each: + // - no cacheability metadata, + // - a cache context, + // - a cache tag, + // - a cache max-age. + // Plus an additional test case to verify that multiple links adding + // cacheability metadata of the same type is working (two links with cache + // tags). + $test_cases = [ + // \Drupal\Core\RouteProcessor\RouteProcessorCurrent: 'route' cache context. + [ + 'uri' => 'route:', + 'cacheability' => (new CacheableMetadata())->setCacheContexts(['route']), + ], + // \Drupal\Core\Access\RouteProcessorCsrf: max-age = 0. + [ + 'uri' => 'route:outbound_processing_test.route.csrf', + 'cacheability' => (new CacheableMetadata())->setCacheMaxAge(0), + ], + // \Drupal\Core\PathProcessor\PathProcessorFront: permanently cacheable. + [ + 'uri' => 'internal:/', + 'cacheability' => (new CacheableMetadata()), + ], + // \Drupal\url_alter_test\PathProcessorTest: user entity's cache tags. + [ + 'uri' => 'internal:/user/1', + 'cacheability' => (new CacheableMetadata())->setCacheTags(User::load(1)->getCacheTags()), + ], + [ + 'uri' => 'internal:/user/2', + 'cacheability' => (new CacheableMetadata())->setCacheTags(User::load(2)->getCacheTags()), + ], + ]; + + // Test each expectation individually. + foreach ($test_cases as $expectation) { + $menu_link_content = MenuLinkContent::create([ + 'link' => ['uri' => $expectation['uri']], + 'menu_name' => 'tools', + ]); + $menu_link_content->save(); + $tree = $menu_tree->load('tools', new MenuTreeParameters()); + $build = $menu_tree->build($tree); + $renderer->renderRoot($build); + + $expected_cacheability = $default_menu_cacheability->merge($expectation['cacheability']); + $this->assertEqual($expected_cacheability, CacheableMetadata::createFromRenderArray($build)); + + $menu_link_content->delete(); + } + + // Now test them all together in one menu: the rendered menu's cacheability + // metadata should be the combination of the cacheability of all links, and + // thus of all tested outbound path & route processors. + $expected_cacheability = new CacheableMetadata(); + foreach ($test_cases as $expectation) { + $menu_link_content = MenuLinkContent::create([ + 'link' => ['uri' => $expectation['uri']], + 'menu_name' => 'tools', + ]); + $menu_link_content->save(); + $expected_cacheability = $expected_cacheability->merge($expectation['cacheability']); + } + $tree = $menu_tree->load('tools', new MenuTreeParameters()); + $build = $menu_tree->build($tree); + $renderer->renderRoot($build); + $expected_cacheability = $expected_cacheability->merge($default_menu_cacheability); + $this->assertEqual($expected_cacheability, CacheableMetadata::createFromRenderArray($build)); + } + +} diff --git a/core/modules/menu_link_content/tests/outbound_processing_test/outbound_processing_test.info.yml b/core/modules/menu_link_content/tests/outbound_processing_test/outbound_processing_test.info.yml new file mode 100644 index 0000000..6f4e4a9 --- /dev/null +++ b/core/modules/menu_link_content/tests/outbound_processing_test/outbound_processing_test.info.yml @@ -0,0 +1,4 @@ +name: 'Outbound route/path processing' +type: module +core: 8.x +hidden: true diff --git a/core/modules/menu_link_content/tests/outbound_processing_test/outbound_processing_test.routing.yml b/core/modules/menu_link_content/tests/outbound_processing_test/outbound_processing_test.routing.yml new file mode 100644 index 0000000..79934c6 --- /dev/null +++ b/core/modules/menu_link_content/tests/outbound_processing_test/outbound_processing_test.routing.yml @@ -0,0 +1,5 @@ +outbound_processing_test.route.csrf: + path: '/outbound_processing_test/route/csrf' + requirements: + _access: 'TRUE' + _csrf_token: 'TRUE' diff --git a/core/modules/menu_ui/src/MenuForm.php b/core/modules/menu_ui/src/MenuForm.php index 09f529a..334d496 100644 --- a/core/modules/menu_ui/src/MenuForm.php +++ b/core/modules/menu_ui/src/MenuForm.php @@ -344,7 +344,7 @@ protected function buildOverviewTreeForm($tree, $delta) { $id = 'menu_plugin_id:' . $link->getPluginId(); $form[$id]['#item'] = $element; $form[$id]['#attributes'] = $link->isEnabled() ? array('class' => array('menu-enabled')) : array('class' => array('menu-disabled')); - $form[$id]['title']['#markup'] = $this->linkGenerator->generate($link->getTitle(), $link->getUrlObject(), $link->getOptions()); + $form[$id]['title']['#markup'] = $this->linkGenerator->generate($link->getTitle(), $link->getUrlObject()); if (!$link->isEnabled()) { $form[$id]['title']['#markup'] .= ' (' . $this->t('disabled') . ')'; } diff --git a/core/modules/shortcut/src/Form/SetCustomize.php b/core/modules/shortcut/src/Form/SetCustomize.php index 4f84888..4058175 100644 --- a/core/modules/shortcut/src/Form/SetCustomize.php +++ b/core/modules/shortcut/src/Form/SetCustomize.php @@ -56,11 +56,13 @@ public function form(array $form, FormStateInterface $form_state) { continue; } $form['shortcuts']['links'][$id]['#attributes']['class'][] = 'draggable'; + // Skip setting #access_callback. $form['shortcuts']['links'][$id]['name'] = array( '#type' => 'link', '#title' => $shortcut->getTitle(), - ) + $url->toRenderArray(); - unset($form['shortcuts']['links'][$id]['name']['#access_callback']); + '#url' => $url, + '#options' => $url->getOptions(), + ); $form['shortcuts']['links'][$id]['#weight'] = $shortcut->getWeight(); $form['shortcuts']['links'][$id]['weight'] = array( '#type' => 'weight', diff --git a/core/modules/system/tests/modules/url_alter_test/src/PathProcessorTest.php b/core/modules/system/tests/modules/url_alter_test/src/PathProcessorTest.php index c4b8017..9af6cab 100644 --- a/core/modules/system/tests/modules/url_alter_test/src/PathProcessorTest.php +++ b/core/modules/system/tests/modules/url_alter_test/src/PathProcessorTest.php @@ -7,6 +7,7 @@ namespace Drupal\url_alter_test; +use Drupal\Core\Cache\CacheableMetadata; use Drupal\Core\PathProcessor\InboundPathProcessorInterface; use Drupal\Core\PathProcessor\OutboundPathProcessorInterface; use Symfony\Component\HttpFoundation\Request; @@ -43,12 +44,15 @@ public function processInbound($path, Request $request) { /** * Implements Drupal\Core\PathProcessor\OutboundPathProcessorInterface::processOutbound(). */ - public function processOutbound($path, &$options = array(), Request $request = NULL) { + public function processOutbound($path, &$options = array(), Request $request = NULL, CacheableMetadata $cacheable_metadata = NULL) { // Rewrite user/uid to user/username. if (preg_match('!^user/([0-9]+)(/.*)?!', $path, $matches)) { if ($account = User::load($matches[1])) { $matches += array(2 => ''); $path = 'user/' . $account->getUsername() . $matches[2]; + if ($cacheable_metadata) { + $cacheable_metadata->addCacheTags($account->getCacheTags()); + } } } diff --git a/core/tests/Drupal/Tests/Core/Access/RouteProcessorCsrfTest.php b/core/tests/Drupal/Tests/Core/Access/RouteProcessorCsrfTest.php index 48f77b9..7449536 100644 --- a/core/tests/Drupal/Tests/Core/Access/RouteProcessorCsrfTest.php +++ b/core/tests/Drupal/Tests/Core/Access/RouteProcessorCsrfTest.php @@ -7,6 +7,7 @@ namespace Drupal\Tests\Core\Access; +use Drupal\Core\Cache\CacheableMetadata; use Drupal\Tests\UnitTestCase; use Drupal\Core\Access\RouteProcessorCsrf; use Symfony\Component\Routing\Route; @@ -49,9 +50,13 @@ public function testProcessOutboundNoRequirement() { $route = new Route('/test-path'); $parameters = array(); - $this->processor->processOutbound('test', $route, $parameters); + $cacheable_metadata = new CacheableMetadata(); + $this->processor->processOutbound('test', $route, $parameters, $cacheable_metadata); // No parameters should be added to the parameters array. $this->assertEmpty($parameters); + // Cacheability of routes without a _csrf_token route requirement is + // unaffected. + $this->assertEquals((new CacheableMetadata()), $cacheable_metadata); } /** @@ -67,10 +72,13 @@ public function testProcessOutbound() { $route = new Route('/test-path', array(), array('_csrf_token' => 'TRUE')); $parameters = array(); - $this->processor->processOutbound('test', $route, $parameters); + $cacheable_metadata = new CacheableMetadata(); + $this->processor->processOutbound('test', $route, $parameters, $cacheable_metadata); // 'token' should be added to the parameters array. $this->assertArrayHasKey('token', $parameters); $this->assertSame($parameters['token'], 'test_token'); + // Cacheability of routes with a _csrf_token route requirement is max-age=0. + $this->assertEquals((new CacheableMetadata())->setCacheMaxAge(0), $cacheable_metadata); } /** @@ -85,7 +93,10 @@ public function testProcessOutboundDynamicOne() { $route = new Route('/test-path/{slug}', array(), array('_csrf_token' => 'TRUE')); $parameters = array('slug' => 100); - $this->assertNull($this->processor->processOutbound('test', $route, $parameters)); + $cacheable_metadata = new CacheableMetadata(); + $this->assertNull($this->processor->processOutbound('test', $route, $parameters, $cacheable_metadata)); + // Cacheability of routes with a _csrf_token route requirement is max-age=0. + $this->assertEquals((new CacheableMetadata())->setCacheMaxAge(0), $cacheable_metadata); } /** @@ -100,7 +111,10 @@ public function testProcessOutboundDynamicTwo() { $route = new Route('{slug_1}/test-path/{slug_2}', array(), array('_csrf_token' => 'TRUE')); $parameters = array('slug_1' => 100, 'slug_2' => 'test'); - $this->assertNull($this->processor->processOutbound('test', $route, $parameters)); + $cacheable_metadata = new CacheableMetadata(); + $this->assertNull($this->processor->processOutbound('test', $route, $parameters, $cacheable_metadata)); + // Cacheability of routes with a _csrf_token route requirement is max-age=0. + $this->assertEquals((new CacheableMetadata())->setCacheMaxAge(0), $cacheable_metadata); } } diff --git a/core/tests/Drupal/Tests/Core/Entity/EntityUrlTest.php b/core/tests/Drupal/Tests/Core/Entity/EntityUrlTest.php index 390eaf7..4f5feb1 100644 --- a/core/tests/Drupal/Tests/Core/Entity/EntityUrlTest.php +++ b/core/tests/Drupal/Tests/Core/Entity/EntityUrlTest.php @@ -184,12 +184,14 @@ public function testUrl() { 'entity.test_entity_type.canonical', array('test_entity_type' => 'test_entity_id'), array('entity_type' => 'test_entity_type', 'entity' => $valid_entity), + NULL, '/entity/test_entity_type/test_entity_id', ), array( 'entity.test_entity_type.canonical', array('test_entity_type' => 'test_entity_id'), array('absolute' => TRUE, 'entity_type' => 'test_entity_type', 'entity' => $valid_entity), + NULL, 'http://drupal/entity/test_entity_type/test_entity_id', ), ))); diff --git a/core/tests/Drupal/Tests/Core/Form/FormSubmitterTest.php b/core/tests/Drupal/Tests/Core/Form/FormSubmitterTest.php index 4e7e85c..463da74 100644 --- a/core/tests/Drupal/Tests/Core/Form/FormSubmitterTest.php +++ b/core/tests/Drupal/Tests/Core/Form/FormSubmitterTest.php @@ -134,8 +134,8 @@ public function testRedirectWithUrl(Url $redirect_value, $result, $status = 303) $this->urlGenerator->expects($this->once()) ->method('generateFromRoute') ->will($this->returnValueMap(array( - array('test_route_a', array(), array('absolute' => TRUE), 'test-route'), - array('test_route_b', array('key' => 'value'), array('absolute' => TRUE), 'test-route/value'), + array('test_route_a', array(), array('absolute' => TRUE), NULL, 'test-route'), + array('test_route_b', array('key' => 'value'), array('absolute' => TRUE), NULL, 'test-route/value'), )) ); diff --git a/core/tests/Drupal/Tests/Core/RouteProcessor/RouteProcessorManagerTest.php b/core/tests/Drupal/Tests/Core/RouteProcessor/RouteProcessorManagerTest.php index e002f44..77d31ff 100644 --- a/core/tests/Drupal/Tests/Core/RouteProcessor/RouteProcessorManagerTest.php +++ b/core/tests/Drupal/Tests/Core/RouteProcessor/RouteProcessorManagerTest.php @@ -7,6 +7,8 @@ namespace Drupal\Tests\Core\RouteProcessor; +use Drupal\Core\Cache\Cache; +use Drupal\Core\Cache\CacheableMetadata; use Drupal\Core\RouteProcessor\RouteProcessorManager; use Drupal\Tests\UnitTestCase; use Symfony\Component\Routing\Route; @@ -47,7 +49,10 @@ public function testRouteProcessorManager() { $this->processorManager->addOutbound($processor, $priority); } - $this->processorManager->processOutbound($route_name, $route, $parameters); + $cacheable_metadata = new CacheableMetadata(); + $this->processorManager->processOutbound($route_name, $route, $parameters, $cacheable_metadata); + // Default cacheability is: permanently cacheable, no cache tags/contexts. + $this->assertEquals((new CacheableMetadata())->setCacheMaxAge(Cache::PERMANENT), $cacheable_metadata); } /** diff --git a/core/tests/Drupal/Tests/Core/UrlTest.php b/core/tests/Drupal/Tests/Core/UrlTest.php index dcc2c66..92b3bb9 100644 --- a/core/tests/Drupal/Tests/Core/UrlTest.php +++ b/core/tests/Drupal/Tests/Core/UrlTest.php @@ -73,19 +73,19 @@ protected function setUp() { parent::setUp(); $map = array(); - $map[] = array('view.frontpage.page_1', array(), array(), '/node'); - $map[] = array('node_view', array('node' => '1'), array(), '/node/1'); - $map[] = array('node_edit', array('node' => '2'), array(), '/node/2/edit'); + $map[] = array('view.frontpage.page_1', array(), array(), NULL, '/node'); + $map[] = array('node_view', array('node' => '1'), array(), NULL, '/node/1'); + $map[] = array('node_edit', array('node' => '2'), array(), NULL, '/node/2/edit'); $this->map = $map; $alias_map = array( // Set up one proper alias that can be resolved to a system path. - array('node-alias-test', NULL, 'node'), + array('node-alias-test', NULL, NULL, 'node'), // Passing in anything else should return the same string. - array('node', NULL, 'node'), - array('node/1', NULL, 'node/1'), - array('node/2/edit', NULL, 'node/2/edit'), - array('non-existent', NULL, 'non-existent'), + array('node', NULL, NULL, 'node'), + array('node/1', NULL, NULL, 'node/1'), + array('node/2/edit', NULL, NULL, 'node/2/edit'), + array('non-existent', NULL, NULL, 'non-existent'), ); $this->urlGenerator = $this->getMock('Drupal\Core\Routing\UrlGeneratorInterface');