diff --git a/core/lib/Drupal.php b/core/lib/Drupal.php
index 0ec88d7..f98d607 100644
--- a/core/lib/Drupal.php
+++ b/core/lib/Drupal.php
@@ -5,6 +5,7 @@
* Contains Drupal.
*/
+use Drupal\Core\Url;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
@@ -517,7 +518,13 @@ public static function linkGenerator() {
* @see \Drupal\Core\Utility\LinkGeneratorInterface::generate()
*/
public static function l($text, $route_name, array $parameters = array(), array $options = array()) {
- return static::$container->get('link_generator')->generate($text, $route_name, $parameters, $options);
+ $link_generator = static::linkGenerator();
+ if ($route_name instanceof Url) {
+ return $link_generator->generateFromUrl($text, $route_name);
+ }
+ else {
+ return $link_generator->generate($text, $route_name, $parameters, $options);
+ }
}
/**
diff --git a/core/lib/Drupal/Core/Url.php b/core/lib/Drupal/Core/Url.php
index 8b79112..4a55f55 100644
--- a/core/lib/Drupal/Core/Url.php
+++ b/core/lib/Drupal/Core/Url.php
@@ -7,6 +7,8 @@
namespace Drupal\Core;
+use Drupal\Core\Routing\UrlGeneratorInterface;
+
/**
* Defines an object that holds information about an internal route.
*/
@@ -206,6 +208,18 @@ public function toArray() {
}
/**
+ * Returns the internal path for this route.
+ *
+ * This path will not include any prefixes, fragments, or query strings.
+ *
+ * @return string
+ * The internal path for this route.
+ */
+ public function getInternalPath() {
+ return $this->urlGenerator()->getPathFromRoute($this->getRouteName(), $this->getRouteParameters());
+ }
+
+ /**
* {@inheritdoc}
*/
public function __sleep() {
@@ -226,4 +240,17 @@ protected function urlGenerator() {
return $this->urlGenerator;
}
+ /**
+ * Sets the URL generator.
+ *
+ * @param \Drupal\Core\Routing\UrlGeneratorInterface
+ * The URL generator.
+ *
+ * @return $this
+ */
+ public function setUrlGenerator(UrlGeneratorInterface $url_generator) {
+ $this->urlGenerator = $url_generator;
+ return $this;
+ }
+
}
diff --git a/core/lib/Drupal/Core/Utility/LinkGenerator.php b/core/lib/Drupal/Core/Utility/LinkGenerator.php
index 2874da4..bea2f0d 100644
--- a/core/lib/Drupal/Core/Utility/LinkGenerator.php
+++ b/core/lib/Drupal/Core/Utility/LinkGenerator.php
@@ -15,7 +15,7 @@
use Drupal\Core\Path\AliasManagerInterface;
use Drupal\Core\Template\Attribute;
use Drupal\Core\Routing\UrlGeneratorInterface;
-use Drupal\Core\Session\AccountInterface;
+use Drupal\Core\Url;
use Symfony\Cmf\Component\Routing\RouteObjectInterface;
use Symfony\Component\HttpFoundation\Request;
@@ -99,6 +99,74 @@ public function getActive() {
/**
* {@inheritdoc}
+ */
+ public function generateFromUrl($text, Url $url) {
+ // @todo Inject the service when drupal_render() is converted to one.
+ $text = is_array($text) ? drupal_render($text) : $text;
+
+ // Merge in default options.
+ $options = $url->getRouteOptions();
+ $options += array(
+ 'attributes' => array(),
+ 'query' => array(),
+ 'html' => FALSE,
+ 'language' => NULL,
+ 'set_active_class' => FALSE,
+ 'absolute' => FALSE,
+ );
+
+ // Add a hreflang attribute if we know the language of this link's url and
+ // hreflang has not already been set.
+ if (!empty($options['language']) && !isset($options['attributes']['hreflang'])) {
+ $options['attributes']['hreflang'] = $options['language']->id;
+ }
+
+ // Set the "active" class if the 'set_active_class' option is not empty.
+ if (!empty($options['set_active_class'])) {
+ // Add a "data-drupal-link-query" attribute to let the
+ // drupal.active-link library know the query in a standardized manner.
+ if (!empty($options['query'])) {
+ $query = $options['query'];
+ ksort($query);
+ $options['attributes']['data-drupal-link-query'] = Json::encode($query);
+ }
+
+ // Add a "data-drupal-link-system-path" attribute to let the
+ // drupal.active-link library know the path in a standardized manner.
+ if (!isset($options['attributes']['data-drupal-link-system-path'])) {
+ $options['attributes']['data-drupal-link-system-path'] = $this->aliasManager->getSystemPath($url->getInternalPath());
+ }
+ }
+
+ // Remove all HTML and PHP tags from a tooltip, calling expensive strip_tags()
+ // only when a quick strpos() gives suspicion tags are present.
+ if (isset($options['attributes']['title']) && strpos($options['attributes']['title'], '<') !== FALSE) {
+ $options['attributes']['title'] = strip_tags($options['attributes']['title']);
+ }
+
+ // Update the options before allowing altering.
+ $url->setRouteOptions($options);
+
+ // Allow other modules to modify the structure of the link.
+ $this->moduleHandler->alter('link', $text, $url);
+
+ // Move attributes out of options. generateFromRoute(() doesn't need them.
+ $attributes = new Attribute($options['attributes']);
+ unset($options['attributes']);
+ $url->setRouteOptions($options);
+
+ // 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 = String::checkPlain($url->toString());
+
+ // Sanitize the link text if necessary.
+ $text = $options['html'] ? $text : String::checkPlain($text);
+
+ return '' . $text . '';
+ }
+
+ /**
+ * {@inheritdoc}
*
* For anonymous users, the "active" class will be calculated on the server,
* because most sites serve each anonymous user the same cached page anyway.
@@ -110,69 +178,9 @@ public function getActive() {
* @see system_page_build()
*/
public function generate($text, $route_name, array $parameters = array(), array $options = array()) {
- // Start building a structured representation of our link to be altered later.
- $variables = array(
- // @todo Inject the service when drupal_render() is converted to one.
- 'text' => is_array($text) ? drupal_render($text) : $text,
- 'route_name' => $route_name,
- 'parameters' => $parameters,
- 'options' => $options,
- );
-
- // Merge in default options.
- $variables['options'] += array(
- 'attributes' => array(),
- 'query' => array(),
- 'html' => FALSE,
- 'language' => NULL,
- 'set_active_class' => FALSE,
- );
-
- // Add a hreflang attribute if we know the language of this link's url and
- // hreflang has not already been set.
- if (!empty($variables['options']['language']) && !isset($variables['options']['attributes']['hreflang'])) {
- $variables['options']['attributes']['hreflang'] = $variables['options']['language']->id;
- }
-
- // Set the "active" class if the 'set_active_class' option is not empty.
- if (!empty($variables['options']['set_active_class'])) {
- // Add a "data-drupal-link-query" attribute to let the
- // drupal.active-link library know the query in a standardized manner.
- if (!empty($variables['options']['query'])) {
- $query = $variables['options']['query'];
- ksort($query);
- $variables['options']['attributes']['data-drupal-link-query'] = Json::encode($query);
- }
-
- // Add a "data-drupal-link-system-path" attribute to let the
- // drupal.active-link library know the path in a standardized manner.
- if (!isset($variables['options']['attributes']['data-drupal-link-system-path'])) {
- $path = $this->urlGenerator->getPathFromRoute($route_name, $parameters);
- $variables['options']['attributes']['data-drupal-link-system-path'] = $this->aliasManager->getSystemPath($path);
- }
- }
-
- // Remove all HTML and PHP tags from a tooltip, calling expensive strip_tags()
- // only when a quick strpos() gives suspicion tags are present.
- if (isset($variables['options']['attributes']['title']) && strpos($variables['options']['attributes']['title'], '<') !== FALSE) {
- $variables['options']['attributes']['title'] = strip_tags($variables['options']['attributes']['title']);
- }
-
- // Allow other modules to modify the structure of the link.
- $this->moduleHandler->alter('link', $variables);
-
- // Move attributes out of options. generateFromRoute(() doesn't need them.
- $attributes = new Attribute($variables['options']['attributes']);
- unset($variables['options']['attributes']);
-
- // 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 = String::checkPlain($this->urlGenerator->generateFromRoute($variables['route_name'], $variables['parameters'], $variables['options']));
-
- // Sanitize the link text if necessary.
- $text = $variables['options']['html'] ? $variables['text'] : String::checkPlain($variables['text']);
-
- return '' . $text . '';
+ $url = new Url($route_name, $parameters, $options);
+ $url->setUrlGenerator($this->urlGenerator);
+ return $this->generateFromUrl($text, $url);
}
}
diff --git a/core/lib/Drupal/Core/Utility/LinkGeneratorInterface.php b/core/lib/Drupal/Core/Utility/LinkGeneratorInterface.php
index 02e31af..851100d 100644
--- a/core/lib/Drupal/Core/Utility/LinkGeneratorInterface.php
+++ b/core/lib/Drupal/Core/Utility/LinkGeneratorInterface.php
@@ -7,6 +7,8 @@
namespace Drupal\Core\Utility;
+use Drupal\Core\Url;
+
/**
* Defines an interface for generating links from route names and parameters.
*/
@@ -78,6 +80,19 @@
public function generate($text, $route_name, array $parameters = array(), array $options = array());
/**
+ * Renders a link to a URL.
+ *
+ * @param string $text
+ * The link text for the anchor tag as a translated string.
+ * @param \Drupal\Core\Url $url
+ * The URL object used for the link.
+ *
+ * @return string
+ * An HTML string containing a link to the given route and parameters.
+ */
+ public function generateFromUrl($text, Url $url);
+
+ /**
* Returns information for the currently active route.
*
* @return array
diff --git a/core/modules/system/system.api.php b/core/modules/system/system.api.php
index d4c1039..ce889f1 100644
--- a/core/modules/system/system.api.php
+++ b/core/modules/system/system.api.php
@@ -5,6 +5,7 @@
* Hooks provided by Drupal core and the System module.
*/
+use Drupal\Core\Url;
use Drupal\Core\Utility\UpdateException;
/**
@@ -3091,50 +3092,35 @@ function hook_filetransfer_info_alter(&$filetransfer_info) {
/**
* Alter the parameters for links.
*
- * @param array $variables
- * An associative array of variables defining a link. The link may be either a
- * "route link" using \Drupal\Core\Utility\LinkGenerator::link(), which is
- * exposed as the 'link_generator' service or a link generated by l(). If the
- * link is a "route link", 'route_name' will be set, otherwise 'path' will be
- * set. The following keys can be altered:
- * - text: The link text for the anchor tag as a translated string.
- * - url_is_active: Whether or not the link points to the currently active
- * URL.
- * - path: If this link is being generated by l(), this system path, relative
- * path, or external URL will be passed to url() to generate the href
- * attribute for this link.
- * - route_name: The name of the route to use to generate the link, if
- * this is a "route link".
- * - parameters: Any parameters needed to render the route path pattern, if
- * this is a "route link".
- * - options: An associative array of additional options that will be passed
- * to either \Drupal\Core\Routing\UrlGenerator::generateFromPath() or
- * \Drupal\Core\Routing\UrlGenerator::generateFromRoute() to generate the
- * href attribute for this link, and also used when generating the link.
- * Defaults to an empty array. It may contain the following elements:
- * - 'query': An array of query key/value-pairs (without any URL-encoding) to
- * append to the URL.
- * - absolute: Whether to force the output to be an absolute link (beginning
- * with http:). Useful for links that will be displayed outside the site,
- * such as in an RSS feed. Defaults to FALSE.
- * - language: An optional language object. May affect the rendering of
- * the anchor tag, such as by adding a language prefix to the path.
- * - attributes: An associative array of HTML attributes to apply to the
- * anchor tag. If element 'class' is included, it must be an array; 'title'
- * must be a string; other elements are more flexible, as they just need
- * to work as an argument for the constructor of the class
- * Drupal\Core\Template\Attribute($options['attributes']).
- * - html: Whether or not HTML should be allowed as the link text. If FALSE,
- * the text will be run through
- * \Drupal\Component\Utility\String::checkPlain() before being output.
+ * @param string $text
+ * The link text for the anchor tag as a translated string.
+ * @param \Drupal\Core\Url $url
+ * The URL object used for the link. Url::getRouteOptions() can contain many
+ * keys, but the following are guaranteed to exist:
+ * - query: An array of query key/value-pairs (without any URL-encoding) to
+ * append to the URL.
+ * - absolute: Whether to force the output to be an absolute link (beginning
+ * with http:). Useful for links that will be displayed outside the site,
+ * such as in an RSS feed. Defaults to FALSE.
+ * - language: An optional language object. May affect the rendering of the
+ * anchor tag, such as by adding a language prefix to the path.
+ * - attributes: An associative array of HTML attributes to apply to the
+ * anchor tag. If element 'class' is included, it must be an array; 'title'
+ * must be a string; other elements are more flexible, as they just need
+ * to work as an argument for the constructor of the class
+ * \Drupal\Core\Template\Attribute($options['attributes']).
+ * - html: Whether or not HTML should be allowed as the link text. If FALSE,
+ * the text will be run through
+ * \Drupal\Component\Utility\String::checkPlain() before being output.
+ * - set_active_class: Whether to set the 'active' class or not.
*
* @see \Drupal\Core\Routing\UrlGenerator::generateFromPath()
* @see \Drupal\Core\Routing\UrlGenerator::generateFromRoute()
*/
-function hook_link_alter(&$variables) {
+function hook_link_alter(&$text, Url $url) {
// Add a warning to the end of route links to the admin section.
- if (isset($variables['route_name']) && strpos($variables['route_name'], 'admin') !== FALSE) {
- $variables['text'] .= ' (Warning!)';
+ if (strpos($url->getRouteName(), 'admin') !== FALSE) {
+ $text .= ' (Warning!)';
}
}
diff --git a/core/tests/Drupal/Tests/Core/DrupalTest.php b/core/tests/Drupal/Tests/Core/DrupalTest.php
index b37010f..2a9e764 100644
--- a/core/tests/Drupal/Tests/Core/DrupalTest.php
+++ b/core/tests/Drupal/Tests/Core/DrupalTest.php
@@ -7,6 +7,7 @@
namespace Drupal\Tests\Core;
+use Drupal\Core\Url;
use Drupal\Tests\UnitTestCase;
/**
@@ -296,7 +297,30 @@ public function testL() {
->will($this->returnValue('link_html_string'));
$this->setMockContainerService('link_generator', $generator);
- $this->assertInternalType('string', \Drupal::l('Test title', 'test_route', $route_parameters, $options));
+ $link = \Drupal::l('Test title', 'test_route', $route_parameters, $options);
+ $this->assertSame('link_html_string', $link);
+ $this->assertInternalType('string', $link);
+ }
+
+ /**
+ * Tests the l() method.
+ *
+ * @see \Drupal\Core\Utility\LinkGeneratorInterface::generateFromUrl()
+ */
+ public function testLWithUrl() {
+ $route_parameters = array('test_parameter' => 'test');
+ $options = array('test_option' => 'test');
+ $url = new Url('test_route', $route_parameters, $options);
+ $generator = $this->getMock('Drupal\Core\Utility\LinkGeneratorInterface');
+ $generator->expects($this->once())
+ ->method('generateFromUrl')
+ ->with('Test title', $url)
+ ->will($this->returnValue('link_html_string'));
+ $this->setMockContainerService('link_generator', $generator);
+
+ $link = \Drupal::l('Test title', $url);
+ $this->assertSame('link_html_string', $link);
+ $this->assertInternalType('string', $link);
}
/**