diff --git a/core/includes/common.inc b/core/includes/common.inc
index 3b6f1dd..0ad7d94 100644
--- a/core/includes/common.inc
+++ b/core/includes/common.inc
@@ -733,67 +733,13 @@ function drupal_http_header_attributes(array $attributes = array()) {
  *
  * @see url()
  * @see system_page_build()
+ *
+ * @deprecated as of 8.0.x
+ *   Use LinkGenerator::generate() for internal paths and
+ *   generateFromUriReference for external/paths not controlled by Drupal.
  */
 function l($text, $path, array $options = array()) {
-  // Start building a structured representation of our link to be altered later.
-  $variables = array(
-    'text' => is_array($text) ? drupal_render($text) : $text,
-    'path' => $path,
-    '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'])) {
-      $variables['options']['attributes']['data-drupal-link-system-path'] = \Drupal::service('path.alias_manager')->getPathByAlias($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.
-  \Drupal::moduleHandler()->alter('link', $variables);
-
-  // Move attributes out of options. url() doesn't need them.
-  $attributes = new Attribute($variables['options']['attributes']);
-  unset($variables['options']['attributes']);
-
-  // The result of url() 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($variables['path'], $variables['options']));
-
-  // Sanitize the link text if necessary.
-  $text = $variables['options']['html'] ? $variables['text'] : String::checkPlain($variables['text']);
-  return SafeMarkup::set('<a href="' . $url . '"' . $attributes . '>' . $text . '</a>');
+  return \Drupal::linkGenerator()->generateFromUriReference($text, $path, $options);
 }
 
 /**
diff --git a/core/lib/Drupal/Core/Utility/LinkGenerator.php b/core/lib/Drupal/Core/Utility/LinkGenerator.php
index f8660ab..7965e73 100644
--- a/core/lib/Drupal/Core/Utility/LinkGenerator.php
+++ b/core/lib/Drupal/Core/Utility/LinkGenerator.php
@@ -12,7 +12,6 @@
 use Drupal\Component\Utility\String;
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Link;
-use Drupal\Core\Path\AliasManagerInterface;
 use Drupal\Core\Routing\UrlGeneratorInterface;
 use Drupal\Core\Template\Attribute;
 use Drupal\Core\Url;
@@ -58,6 +57,71 @@ public function generateFromLink(Link $link) {
 
   /**
    * {@inheritdoc}
+   */
+  public function generateFromUriReference($text, $path, array $options = []) {
+    // Start building a structured representation of our link to be altered later.
+    $variables = array(
+      'text' => is_array($text) ? drupal_render($text) : $text,
+      'path' => $path,
+      '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'])) {
+        $variables['options']['attributes']['data-drupal-link-system-path'] = \Drupal::service('path.alias_manager')->getPathByAlias($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. url() doesn't need them.
+    $attributes = new Attribute($variables['options']['attributes']);
+    unset($variables['options']['attributes']);
+
+    // The result of url() 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->generateFromPath($variables['path'], $variables['options']));
+
+    // Sanitize the link text if necessary.
+    $text = $variables['options']['html'] ? $variables['text'] : String::checkPlain($variables['text']);
+    return SafeMarkup::set('<a href="' . $url . '"' . $attributes . '>' . $text . '</a>');
+  }
+
+  /**
+   * {@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.
diff --git a/core/lib/Drupal/Core/Utility/LinkGeneratorInterface.php b/core/lib/Drupal/Core/Utility/LinkGeneratorInterface.php
index edce761..e20b180 100644
--- a/core/lib/Drupal/Core/Utility/LinkGeneratorInterface.php
+++ b/core/lib/Drupal/Core/Utility/LinkGeneratorInterface.php
@@ -104,4 +104,70 @@ public function generateFromUrl($text, Url $url);
    */
   public function generateFromLink(Link $link);
 
+  /**
+   * Formats an internal or external URL link as an HTML anchor tag.
+   *
+   * This function correctly handles aliased paths and adds an 'active' class
+   * attribute to links that point to the current page (for theming), so all
+   * internal links output by modules should be generated by this function if
+   * possible.
+   *
+   * However, for links enclosed in translatable text you should use t() and
+   * embed the HTML anchor tag directly in the translated string. For example:
+   * @code
+   * t('Visit the <a href="@url">settings</a> page', array('@url' => url('admin')));
+   * @endcode
+   * This keeps the context of the link title ('settings' in the example) for
+   * translators.
+   *
+   * This function does not support generating links from internal routes. For
+   * that use \Drupal\Core\Utility\LinkGenerator::generate(), which is exposed via
+   * the 'link_generator' service. It requires an internal route name and does not
+   * support external URLs. Using Drupal 7 style system paths should be avoided if
+   * possible but l() should still be used when rendering links to external URLs.
+   *
+   * @param string|array $text
+   *   The link text for the anchor tag as a translated string or render array.
+   * @param string $path
+   *   The internal path or external URL being linked to, such as "node/34" or
+   *   "http://example.com/foo". After the url() function is called to construct
+   *   the URL from $path and $options, the resulting URL is passed through
+   *   \Drupal\Component\Utility\String::checkPlain() before it is inserted into
+   *   the HTML anchor tag, to ensure well-formed HTML. See url() for more
+   *   information and notes.
+   * @param array $options
+   *   An associative array of additional options. Defaults to an empty array. It
+   *   may contain the following elements.
+   *   - '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' (default FALSE): Whether $text is HTML or just plain-text. For
+   *     example, to make an image tag into a link, this must be set to TRUE, or
+   *     you will see the escaped HTML image tag. $text is not sanitized if
+   *     'html' is TRUE. The calling function must ensure that $text is already
+   *     safe.
+   *   - 'language': An optional language object. If the path being linked to is
+   *     internal to the site, $options['language'] is used to determine whether
+   *     the link is "active", or pointing to the current page (the language as
+   *     well as the path must match). This element is also used by url().
+   *   - 'set_active_class': Whether l() should compare the $path, language and
+   *     query options to the current URL to determine whether the link is
+   *     "active". Defaults to FALSE. If TRUE, an "active" class will be applied
+   *     to the link. It is important to use this sparingly since it is usually
+   *     unnecessary and requires extra processing.
+   *     For anonymous users, the "active" class will be calculated on the server,
+   *     because most sites serve each anonymous user the same cached page anyway.
+   *     For authenticated users, the "active" class will be calculated on the
+   *     client (through JavaScript), only data- attributes are added to links to
+   *     prevent breaking the render cache. The JavaScript is added in
+   *     system_page_build().
+   *   - Additional $options elements used by the url() function.
+   *
+   * @return string
+   *   An HTML string containing a link to the given path.
+   */
+  public function generateFromUriReference($text, $path, array $options = []);
+
 }
