diff --git a/core/core.services.yml b/core/core.services.yml
index 91a2c2b..7abab67 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -489,6 +489,9 @@ services:
arguments: ['@router.route_provider', '@path_processor_manager', '@route_processor_manager', '@config.factory', '@settings', '@logger.channel.default', '@request_stack']
calls:
- [setContext, ['@?router.request_context']]
+ unrouted_url_assembler:
+ class: Drupal\Core\Utility\UnroutedUrlAssembler
+ arguments: ['@request_stack', '@config.factory' ]
link_generator:
class: Drupal\Core\Utility\LinkGenerator
arguments: ['@url_generator', '@module_handler']
diff --git a/core/includes/batch.inc b/core/includes/batch.inc
index 7e6e12f..e36cab7 100644
--- a/core/includes/batch.inc
+++ b/core/includes/batch.inc
@@ -452,11 +452,17 @@ function _batch_finished() {
if ($_batch['form_state']->getRedirect() === NULL) {
$redirect = $_batch['batch_redirect'] ?: $_batch['source_url'];
$options = UrlHelper::parse($redirect);
- if (!UrlHelper::isExternal($options['path'])) {
- $options['path'] = $GLOBALS['base_url'] . '/' . $options['path'];
+ if (UrlHelper::isExternal($options['path'])) {
+ $redirect = Url::unrouted($options['path'], $options);
+ }
+ else {
+ $redirect = \Drupal::pathValidator()->getUrlIfValid($options['path']);
+ if (!$redirect) {
+ // Stay on the same page if the redirect was invalid.
+ $redirect = Url::routed('');
+ }
+ $redirect->setOptions($options);
}
- $redirect = Url::createFromPath($options['path']);
- $redirect->setOptions($options);
$_batch['form_state']->setRedirectUrl($redirect);
}
diff --git a/core/includes/common.inc b/core/includes/common.inc
index 1379b4e..f46c9a9 100644
--- a/core/includes/common.inc
+++ b/core/includes/common.inc
@@ -639,6 +639,9 @@ function _format_date_callback(array $matches = NULL, $new_langcode = NULL) {
* alternative than url().
*
* @see \Drupal\Core\Routing\UrlGeneratorInterface::generateFromPath().
+ *
+ * @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0.
+ * System paths should not be used - use route names and parameters.
*/
function url($path = NULL, array $options = array()) {
return \Drupal::urlGenerator()->generateFromPath($path, $options);
diff --git a/core/lib/Drupal/Core/Menu/MenuLinkBase.php b/core/lib/Drupal/Core/Menu/MenuLinkBase.php
index b09bfa9..6edbcc0 100644
--- a/core/lib/Drupal/Core/Menu/MenuLinkBase.php
+++ b/core/lib/Drupal/Core/Menu/MenuLinkBase.php
@@ -141,8 +141,7 @@ public function getUrlObject($title_attribute = TRUE) {
return new Url($this->pluginDefinition['route_name'], $this->pluginDefinition['route_parameters'], $options);
}
else {
- $url = Url::createFromPath($this->pluginDefinition['url']);
- $url->setOptions($options);
+ $url = Url::unrouted($this->pluginDefinition['url'], $options);
return $url;
}
}
diff --git a/core/lib/Drupal/Core/Path/PathValidator.php b/core/lib/Drupal/Core/Path/PathValidator.php
index 1b519c7..d7506c6 100644
--- a/core/lib/Drupal/Core/Path/PathValidator.php
+++ b/core/lib/Drupal/Core/Path/PathValidator.php
@@ -99,7 +99,7 @@ public function getUrlIfValid($path) {
if (empty($parsed_url['path'])) {
return FALSE;
}
- return Url::createFromPath($path);
+ return Url::unrouted($path);
}
$path = ltrim($path, '/');
diff --git a/core/lib/Drupal/Core/Url.php b/core/lib/Drupal/Core/Url.php
index d97f017..4ee3692 100644
--- a/core/lib/Drupal/Core/Url.php
+++ b/core/lib/Drupal/Core/Url.php
@@ -7,10 +7,10 @@
namespace Drupal\Core;
-use Drupal\Component\Utility\UrlHelper;
use Drupal\Core\DependencyInjection\DependencySerializationTrait;
use Drupal\Core\Routing\UrlGeneratorInterface;
use Drupal\Core\Session\AccountInterface;
+use Drupal\Core\Utility\UnroutedUrlAssemblerInterface;
use Symfony\Cmf\Component\Routing\RouteObjectInterface;
use Symfony\Component\HttpFoundation\Request;
@@ -28,6 +28,13 @@ class Url {
protected $urlGenerator;
/**
+ * The URL builder.
+ *
+ * @var \Drupal\Core\Utility\UnroutedUrlAssemblerInterface
+ */
+ protected $urlAssember;
+
+ /**
* The access manager
*
* @var \Drupal\Core\Access\AccessManagerInterface
@@ -56,20 +63,27 @@ class Url {
protected $options = array();
/**
- * Indicates whether this URL is external.
+ * Indicates whether this object contains an external URL.
*
* @var bool
*/
protected $external = FALSE;
/**
- * The external path.
+ * Indicates whether this URL is a URI instead of a route.
+ *
+ * @var bool
+ */
+ protected $unrouted = FALSE;
+
+ /**
+ * The non-route uri.
*
- * Only used if self::$external is TRUE.
+ * Only used if self::$unrouted is TRUE.
*
* @var string
*/
- protected $path;
+ protected $uri;
/**
* Constructs a new Url object.
@@ -102,46 +116,27 @@ public function __construct($route_name, $route_parameters = array(), $options =
$this->options = $options;
}
+ public static function routed($route_name, $route_parameters = array(), $options = array()) {
+ return new static($route_name, $route_parameters, $options);
+ }
+
+ public static function unrouted($uri, $options = array()) {
+ $url = new static($uri, array(), $options);
+ $url->setUnrouted();
+ return $url;
+ }
+
/**
- * Returns the Url object matching a path. READ THE FOLLOWING SECURITY NOTE.
+ * Returns the Url object matching a request.
*
- * SECURITY NOTE: The path is not checked to be valid and accessible by the
- * current user to allow storing and reusing Url objects by different users.
- * The 'path.validator' service getUrlIfValid() method should be used instead
- * of this one if validation and access check is desired. Otherwise,
+ * SECURITY NOTE: The request path is not checked to be valid and accessible
+ * by the current user to allow storing and reusing Url objects by different
+ * users. The 'path.validator' service getUrlIfValid() method should be used
+ * instead of this one if validation and access check is desired. Otherwise,
* 'access_manager' service checkNamedRoute() method should be used on the
* router name and parameters stored in the Url object returned by this
* method.
*
- * @param string $path
- * A path (e.g. 'node/1', 'http://drupal.org').
- *
- * @return static
- * An Url object. Warning: the object is created even if the current user
- * can not access the path.
- *
- * @throws \Drupal\Core\Routing\MatchingRouteNotFoundException
- * Thrown when the path cannot be matched.
- */
- public static function createFromPath($path) {
- if (UrlHelper::isExternal($path)) {
- $url = new static($path);
- $url->setExternal();
- return $url;
- }
-
- // Special case the front page route.
- if ($path == '') {
- return new static($path);
- }
- else {
- return static::createFromRequest(Request::create("/$path"));
- }
- }
-
- /**
- * Returns the Url object matching a request. READ THE SECURITY NOTE ON createFromPath().
- *
* @param \Symfony\Component\HttpFoundation\Request $request
* A request object.
*
@@ -163,23 +158,18 @@ public static function createFromRequest(Request $request) {
}
/**
- * Sets this Url to be external.
+ * Sets this Url to encapselate an unrouted URI.
*
* @return $this
*/
- protected function setExternal() {
- $this->external = TRUE;
-
- // What was passed in as the route name is actually the path.
- $this->path = $this->routeName;
-
+ protected function setUnrouted() {
+ $this->unrouted = TRUE;
+ // What was passed in as the route name is actually the uri.
+ $this->uri = $this->routeName;
// Set empty route name and parameters.
$this->routeName = NULL;
$this->routeParameters = array();
- // Flag the path as external so the UrlGenerator does not need to check.
- $this->options['external'] = TRUE;
-
- return $this;
+ $this->external = strpos($this->uri, 'base://') !== 0;
}
/**
@@ -197,10 +187,10 @@ public function isExternal() {
* @return string
*
* @throws \UnexpectedValueException.
- * If this is an external URL with no corresponding route.
+ * If this is an URI with no corresponding route.
*/
public function getRouteName() {
- if ($this->isExternal()) {
+ if ($this->unrouted) {
throw new \UnexpectedValueException('External URLs do not have an internal route name.');
}
@@ -213,10 +203,10 @@ public function getRouteName() {
* @return array
*
* @throws \UnexpectedValueException.
- * If this is an external URL with no corresponding route.
+ * If this is an URI with no corresponding route.
*/
public function getRouteParameters() {
- if ($this->isExternal()) {
+ if ($this->unrouted) {
throw new \UnexpectedValueException('External URLs do not have internal route parameters.');
}
@@ -230,10 +220,13 @@ public function getRouteParameters() {
* The array of parameters.
*
* @return $this
+ *
+ * @throws \UnexpectedValueException.
+ * If this is an URI with no corresponding route.
*/
public function setRouteParameters($parameters) {
- if ($this->isExternal()) {
- throw new \Exception('External URLs do not have route parameters.');
+ if ($this->unrouted) {
+ throw new \UnexpectedValueException('External URLs do not have route parameters.');
}
$this->routeParameters = $parameters;
return $this;
@@ -248,10 +241,13 @@ public function setRouteParameters($parameters) {
* The route parameter.
*
* @return $this
+ *
+ * @throws \UnexpectedValueException.
+ * If this is an URI with no corresponding route.
*/
public function setRouteParameter($key, $value) {
- if ($this->isExternal()) {
- throw new \Exception('External URLs do not have route parameters.');
+ if ($this->unrouted) {
+ throw new \UnexpectedValueException('External URLs do not have route parameters.');
}
$this->routeParameters[$key] = $value;
return $this;
@@ -312,22 +308,22 @@ public function setOption($name, $value) {
}
/**
- * Returns the external path of the URL.
+ * Returns the URI of the URL.
*
- * Only to be used if self::$external is TRUE.
+ * Only to be used if self::$unrouted is TRUE.
*
* @return string
- * The external path.
+ * A URI not connected to a route. May be an external URL.
*
* @throws \UnexpectedValueException
- * Thrown when the path was requested for an internal URL.
+ * Thrown when the uri was requested for route.
*/
- public function getPath() {
- if (!$this->isExternal()) {
- throw new \UnexpectedValueException('Internal URLs do not have external paths.');
+ public function getUri() {
+ if (!$this->unrouted) {
+ throw new \UnexpectedValueException('Internal routes do not have uris.');
}
- return $this->path;
+ return $this->uri;
}
/**
@@ -344,11 +340,11 @@ public function setAbsolute($absolute = TRUE) {
}
/**
- * Generates the path for this Url object.
+ * Generates the uri for this Url object.
*/
public function toString() {
- if ($this->isExternal()) {
- return $this->urlGenerator()->generateFromPath($this->getPath(), $this->getOptions());
+ if ($this->unrouted) {
+ return $this->unroutedUrlAssembler()->assemble($this->getUri(), $this->getOptions());
}
return $this->urlGenerator()->generateFromRoute($this->getRouteName(), $this->getRouteParameters(), $this->getOptions());
@@ -363,7 +359,7 @@ public function toString() {
public function toArray() {
if ($this->isExternal()) {
return array(
- 'path' => $this->getPath(),
+ 'path' => $this->getUri(),
'options' => $this->getOptions(),
);
}
@@ -383,9 +379,9 @@ public function toArray() {
* An associative array suitable for a render array.
*/
public function toRenderArray() {
- if ($this->isExternal()) {
+ if ($this->unrouted) {
return array(
- '#href' => $this->getPath(),
+ '#href' => $this->getUri(),
'#options' => $this->getOptions(),
);
}
@@ -408,13 +404,13 @@ public function toRenderArray() {
* The internal path for this route.
*
* @throws \UnexpectedValueException.
- * If this is an external URL with no corresponding system path.
+ * If this is an URI with no corresponding system path.
*
* @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0.
* System paths should not be used - use route names and parameters.
*/
public function getInternalPath() {
- if ($this->isExternal()) {
+ if ($this->unrouted) {
throw new \UnexpectedValueException('External URLs do not have internal representations.');
}
return $this->urlGenerator()->getPathFromRoute($this->getRouteName(), $this->getRouteParameters());
@@ -473,6 +469,19 @@ protected function urlGenerator() {
}
/**
+ * Gets the URL builder for non-Drupal URLs.
+ *
+ * @return \Drupal\Core\Utility\UnroutedUrlAssemblerInterface
+ * The URL builder.
+ */
+ protected function unroutedUrlAssembler() {
+ if (!$this->urlAssember) {
+ $this->urlAssember = \Drupal::service('unrouted_url_assembler');
+ }
+ return $this->urlAssember;
+ }
+
+ /**
* Sets the URL generator.
*
* @param \Drupal\Core\Routing\UrlGeneratorInterface
@@ -485,4 +494,17 @@ public function setUrlGenerator(UrlGeneratorInterface $url_generator) {
return $this;
}
+ /**
+ * Sets the unrouted URL assembler.
+ *
+ * @param \Drupal\Core\Utility\UnroutedUrlAssemblerInterface
+ * The unrouted URL assembler.
+ *
+ * @return $this
+ */
+ public function setUnroutedUrlAssembler(UnroutedUrlAssemblerInterface $url_assembler) {
+ $this->urlAssember = $url_assembler;
+ return $this;
+ }
+
}
diff --git a/core/lib/Drupal/Core/Utility/UnroutedUrlAssembler.php b/core/lib/Drupal/Core/Utility/UnroutedUrlAssembler.php
new file mode 100644
index 0000000..05fb19f
--- /dev/null
+++ b/core/lib/Drupal/Core/Utility/UnroutedUrlAssembler.php
@@ -0,0 +1,152 @@
+get('system.filter')->get('protocols') ?: ['http', 'https'];
+ UrlHelper::setAllowedProtocols($allowed_protocols);
+ $this->requestStack = $request_stack;
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * 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 = []) {
+ // 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 (strpos($uri, 'base://') === 0) {
+ return $this->buildLocalUrl($uri, $options);
+ }
+ elseif (UrlHelper::isExternal($uri)) {
+ // UrlHelper::isExternal() only returns true for safe protocols.
+ return $this->buildExternalUrl($uri, $options);
+ }
+ throw new \InvalidArgumentException('You must use a valid URI scheme. Use base:// for a path e.g. to a Drupal file that needs the base path.');
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function buildExternalUrl($uri, array $options = []) {
+ $this->addOptionDefaults($options);
+ // Split off the fragment.
+ if (strpos($uri, '#') !== FALSE) {
+ list($uri, $old_fragment) = explode('#', $uri, 2);
+ // If $options contains no fragment, take it over from the path.
+ if (isset($old_fragment) && !$options['fragment']) {
+ $options['fragment'] = '#' . $old_fragment;
+ }
+ }
+
+ if (isset($options['https'])) {
+ if ($options['https'] === TRUE) {
+ $uri = str_replace('http://', 'https://', $uri);
+ }
+ elseif ($options['https'] === FALSE) {
+ $uri = str_replace('https://', 'http://', $uri);
+ }
+ }
+ // Append the query.
+ if ($options['query']) {
+ $uri .= (strpos($uri, '?') !== FALSE ? '&' : '?') . UrlHelper::buildQuery($options['query']);
+ }
+ // Reassemble.
+ return $uri . $options['fragment'];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function buildLocalUrl($uri, array $options = []) {
+ $this->addOptionDefaults($options);
+ $request = $this->requestStack->getCurrentRequest();
+
+ // Remove the base:// scheme.
+ $uri = substr($uri, 7);
+ // Add any subdirectory where Drupal is installed.
+ $current_base_path = $request->getBasePath() . '/';
+
+ if ($options['absolute']) {
+ $current_base_url = $request->getSchemeAndHttpHost() . $current_base_path;
+ if (isset($options['https'])) {
+ if (!empty($options['https'])) {
+ $base = str_replace('http://', 'https://', $current_base_url);
+ $options['absolute'] = TRUE;
+ }
+ else {
+ $base = str_replace('https://', 'http://', $current_base_url);
+ $options['absolute'] = TRUE;
+ }
+ }
+ else {
+ $base = $current_base_url;
+ }
+ }
+ else {
+ $base = $current_base_path;
+ }
+
+ $prefix = empty($uri) ? rtrim($options['prefix'], '/') : $options['prefix'];
+
+ $uri = str_replace('%2F', '/', rawurlencode($prefix . $uri));
+ $query = $options['query'] ? ('?' . UrlHelper::buildQuery($options['query'])) : '';
+ return $base . $options['script'] . $uri . $query . $options['fragment'];
+ }
+
+ /**
+ * Merges in default defaults
+ *
+ * @param array $options
+ * The options to merge in the defaults.
+ */
+ protected function addOptionDefaults(array &$options) {
+ // Merge in defaults.
+ $options += [
+ 'fragment' => '',
+ 'query' => [],
+ 'absolute' => FALSE,
+ 'prefix' => '',
+ 'script' => '',
+ ];
+
+ if (isset($options['fragment']) && $options['fragment'] !== '') {
+ $options['fragment'] = '#' . $options['fragment'];
+ }
+ }
+
+}
diff --git a/core/lib/Drupal/Core/Utility/UnroutedUrlAssemblerInterface.php b/core/lib/Drupal/Core/Utility/UnroutedUrlAssemblerInterface.php
new file mode 100644
index 0000000..d8d0e64
--- /dev/null
+++ b/core/lib/Drupal/Core/Utility/UnroutedUrlAssemblerInterface.php
@@ -0,0 +1,54 @@
+' . t('The Aggregator module is an on-site syndicator and news reader that gathers and displays fresh content from RSS-, RDF-, and Atom-based feeds made available across the web. Thousands of sites (particularly news sites and blogs) publish their latest headlines in feeds, using a number of standardized XML-based formats. For more information, see the online documentation for the Aggregator module.', array('!aggregator-module' => 'https://drupal.org/documentation/modules/aggregator')) . '
';
$output .= '' . t('Uses') . '
';
$output .= '';
+ // Check if the aggregator sources View is enabled.
if ($url = $path_validator->getUrlIfValid('aggregator/sources')) {
$output .= '- ' . t('Viewing feeds') . '
';
$output .= '- ' . t('Users view feed content in the main aggregator display, or by their source (usually via an RSS feed reader). The most recent content in a feed can be displayed as a block through the Blocks administration page.', array('!aggregator' => \Drupal::url('aggregator.page_last'), '!aggregator-sources' => $url->toString(), '!admin-block' => \Drupal::url('block.admin_display'))) . '
';
@@ -33,6 +34,7 @@ function aggregator_help($route_name, RouteMatchInterface $route_match) {
$output .= '- ' . t('Adding, editing, and deleting feeds') . '
';
$output .= '- ' . t('Administrators can add, edit, and delete feeds, and choose how often to check each feed for newly updated items on the Feed aggregator administration page.', array('!feededit' => \Drupal::url('aggregator.admin_overview'))) . '
';
$output .= '- ' . t('OPML integration') . '
';
+ // Check if the aggregator opml View is enabled.
if ($url = $path_validator->getUrlIfValid('aggregator/opml')) {
$output .= '- ' . t('A machine-readable OPML file of all feeds is available. OPML is an XML-based file format used to share outline-structured information such as a list of RSS feeds. Feeds can also be imported via an OPML file.', array('!aggregator-opml' => $url->toString(), '!import-opml' => \Drupal::url('aggregator.opml_add'))) . '
';
}
diff --git a/core/modules/aggregator/src/Entity/Item.php b/core/modules/aggregator/src/Entity/Item.php
index 5df7013..cb7f0b9 100644
--- a/core/modules/aggregator/src/Entity/Item.php
+++ b/core/modules/aggregator/src/Entity/Item.php
@@ -245,7 +245,7 @@ public function getListCacheTags() {
* Entity URI callback.
*/
public static function buildUri(ItemInterface $item) {
- return Url::createFromPath($item->getLink());
+ return Url::unrouted($item->getLink());
}
}
diff --git a/core/modules/field_ui/src/FieldUI.php b/core/modules/field_ui/src/FieldUI.php
index 77ec651..be74fff 100644
--- a/core/modules/field_ui/src/FieldUI.php
+++ b/core/modules/field_ui/src/FieldUI.php
@@ -51,15 +51,15 @@ public static function getNextDestination(array $destinations) {
$next_destination += array(
'route_parameters' => array(),
);
- $next_destination = new Url($next_destination['route_name'], $next_destination['route_parameters'], $next_destination['options']);
+ $next_destination = Url::routed($next_destination['route_name'], $next_destination['route_parameters'], $next_destination['options']);
}
else {
$options = UrlHelper::parse($next_destination);
if ($destinations) {
$options['query']['destinations'] = $destinations;
}
- $next_destination = Url::createFromPath($options['path']);
- $next_destination->setOptions($options);
+ // Redirect to any given path within the same domain.
+ $next_destination = Url::unrouted('base://' . $options['path']);
}
return $next_destination;
}
diff --git a/core/modules/link/src/Plugin/Field/FieldFormatter/LinkFormatter.php b/core/modules/link/src/Plugin/Field/FieldFormatter/LinkFormatter.php
index 05caf97..f1d179f 100644
--- a/core/modules/link/src/Plugin/Field/FieldFormatter/LinkFormatter.php
+++ b/core/modules/link/src/Plugin/Field/FieldFormatter/LinkFormatter.php
@@ -163,7 +163,7 @@ public function viewElements(FieldItemListInterface $items) {
'#options' => $url->getOptions(),
);
if ($url->isExternal()) {
- $element[$delta]['#href'] = $url->getPath();
+ $element[$delta]['#href'] = $url->getUri();
}
else {
$element[$delta]['#route_name'] = $url->getRouteName();
@@ -206,11 +206,10 @@ protected function buildUrl(LinkItemInterface $item) {
}
if ($item->isExternal()) {
- $url = Url::createFromPath($item->url);
- $url->setOptions($options);
+ $url = Url::unrouted($item->url, $options);
}
else {
- $url = new Url($item->route_name, (array) $item->route_parameters, (array) $options);
+ $url = Url::routed($item->route_name, (array) $item->route_parameters, (array) $options);
}
return $url;
diff --git a/core/modules/menu_link_content/src/Entity/MenuLinkContent.php b/core/modules/menu_link_content/src/Entity/MenuLinkContent.php
index c39d156..b44fffc 100644
--- a/core/modules/menu_link_content/src/Entity/MenuLinkContent.php
+++ b/core/modules/menu_link_content/src/Entity/MenuLinkContent.php
@@ -108,7 +108,7 @@ public function getUrlObject() {
else {
$path = $this->getUrl();
if (isset($path)) {
- $url = Url::createFromPath($path);
+ $url = Url::unrouted($path);
}
else {
$url = new Url('');
diff --git a/core/modules/menu_link_content/src/Form/MenuLinkContentForm.php b/core/modules/menu_link_content/src/Form/MenuLinkContentForm.php
index 2d37566..4fb93b2 100644
--- a/core/modules/menu_link_content/src/Form/MenuLinkContentForm.php
+++ b/core/modules/menu_link_content/src/Form/MenuLinkContentForm.php
@@ -198,7 +198,7 @@ public function extractFormValues(array &$form, FormStateInterface $form_state)
if ($extracted) {
if ($extracted->isExternal()) {
- $new_definition['url'] = $extracted->getPath();
+ $new_definition['url'] = $extracted->getUri();
}
else {
$new_definition['route_name'] = $extracted->getRouteName();
diff --git a/core/modules/shortcut/src/Entity/Shortcut.php b/core/modules/shortcut/src/Entity/Shortcut.php
index a3e15e7..fa1e1e0 100644
--- a/core/modules/shortcut/src/Entity/Shortcut.php
+++ b/core/modules/shortcut/src/Entity/Shortcut.php
@@ -14,6 +14,7 @@
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\Url;
use Drupal\shortcut\ShortcutInterface;
+use Symfony\Component\HttpFoundation\Request;
/**
* Defines the shortcut entity class.
@@ -135,7 +136,7 @@ public static function preCreate(EntityStorageInterface $storage, array &$values
public function preSave(EntityStorageInterface $storage) {
parent::preSave($storage);
- $url = Url::createFromPath($this->path->value);
+ $url = Url::createFromRequest(Request::create("/{$this->path->value}"));
$this->setRouteName($url->getRouteName());
$this->setRouteParams($url->getRouteParameters());
}
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index 3f19f14..bc88307 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -452,7 +452,7 @@ function system_authorized_get_url(array $options = array()) {
// Force HTTPS if available, regardless of what the caller specifies.
$options['https'] = TRUE;
// Prefix with $base_url so url() treats it as an external link.
- $url = Url::createFromPath($base_url . '/core/authorize.php');
+ $url = Url::unrouted('base://core/authorize.php');
$url_options = $url->getOptions();
$url->setOptions($options + $url_options);
return $url;
diff --git a/core/modules/system/tests/modules/form_test/src/Form/FormTestRedirectForm.php b/core/modules/system/tests/modules/form_test/src/Form/FormTestRedirectForm.php
index 7fc352e..3eb9cc4 100644
--- a/core/modules/system/tests/modules/form_test/src/Form/FormTestRedirectForm.php
+++ b/core/modules/system/tests/modules/form_test/src/Form/FormTestRedirectForm.php
@@ -54,7 +54,7 @@ public function buildForm(array $form, FormStateInterface $form_state) {
public function submitForm(array &$form, FormStateInterface $form_state) {
if (!$form_state->isValueEmpty('redirection')) {
if (!$form_state->isValueEmpty('destination')) {
- $form_state->setRedirectUrl(Url::createFromPath($GLOBALS['base_url'] . '/' . $form_state->getValue('destination')));
+ $form_state->setRedirectUrl(Url::unrouted('base://' . $form_state->getValue('destination')));
}
}
else {
diff --git a/core/modules/toolbar/toolbar.module b/core/modules/toolbar/toolbar.module
index b9174bc..815d1cf 100644
--- a/core/modules/toolbar/toolbar.module
+++ b/core/modules/toolbar/toolbar.module
@@ -298,7 +298,7 @@ function toolbar_menu_navigation_links(array $tree) {
$url = $link->getUrlObject();
if ($url->isExternal()) {
// This is an unusual case, so just get a distinct, safe string.
- $id = substr(Crypt::hashBase64($url->getPath()), 0, 16);
+ $id = substr(Crypt::hashBase64($url->getUri()), 0, 16);
}
else {
$id = str_replace(array('.', '<', '>'), array('-', '', ''), $url->getRouteName());
diff --git a/core/modules/update/update.report.inc b/core/modules/update/update.report.inc
index 9ee4dbf..e885ce5 100644
--- a/core/modules/update/update.report.inc
+++ b/core/modules/update/update.report.inc
@@ -135,7 +135,7 @@ function template_preprocess_update_project_status(&$variables) {
// Set the project title and URL.
$variables['title'] = (isset($project['title'])) ? $project['title'] : $project['name'];
- $variables['url'] = (isset($project['link'])) ? url($project['link']) : NULL;
+ $variables['url'] = (isset($project['link'])) ? \Drupal::service('unrouted_url_assembler')->assemble($project['link']) : NULL;
$variables['install_type'] = $project['install_type'];
if ($project['install_type'] == 'dev' && !empty($project['datestamp'])) {
diff --git a/core/modules/user/user.module b/core/modules/user/user.module
index e33cd28..65b4b04 100644
--- a/core/modules/user/user.module
+++ b/core/modules/user/user.module
@@ -590,9 +590,9 @@ function template_preprocess_username(&$variables) {
$variables['link_path'] = $account->homepage;
$variables['homepage'] = $account->homepage;
}
- // We have a link path, so we should generate a link using url().
+ // We have a link path, so we should generate a URL.
if (isset($variables['link_path'])) {
- $variables['attributes']['href'] = url($variables['link_path'], $variables['link_options']);
+ $variables['attributes']['href'] = \Drupal::service('unrouted_url_assembler')->assemble($variables['link_path'], $variables['link_options']);
}
}
diff --git a/core/modules/views_ui/src/ViewEditForm.php b/core/modules/views_ui/src/ViewEditForm.php
index 12083cf..d236756 100644
--- a/core/modules/views_ui/src/ViewEditForm.php
+++ b/core/modules/views_ui/src/ViewEditForm.php
@@ -322,10 +322,7 @@ public function save(array $form, FormStateInterface $form_state) {
$query->remove('destination');
}
}
- if (!UrlHelper::isExternal($destination)) {
- $destination = $GLOBALS['base_url'] . '/' . $destination;
- }
- $form_state->setRedirectUrl(Url::createFromPath($destination));
+ $form_state->setRedirectUrl(Url::createFromRequest(Request::create("/$destination")));
}
$view->save();
diff --git a/core/tests/Drupal/Tests/Core/ExternalUrlTest.php b/core/tests/Drupal/Tests/Core/ExternalUrlTest.php
index 57a51d4..5073689 100644
--- a/core/tests/Drupal/Tests/Core/ExternalUrlTest.php
+++ b/core/tests/Drupal/Tests/Core/ExternalUrlTest.php
@@ -20,11 +20,11 @@
class ExternalUrlTest extends UnitTestCase {
/**
- * The URL generator
+ * The URL assembler
*
* @var \Drupal\Core\Routing\UrlGeneratorInterface|\PHPUnit_Framework_MockObject_MockObject
*/
- protected $urlGenerator;
+ protected $urlAssembler;
/**
* The router.
@@ -46,15 +46,15 @@ class ExternalUrlTest extends UnitTestCase {
protected function setUp() {
parent::setUp();
- $this->urlGenerator = $this->getMock('Drupal\Core\Routing\UrlGeneratorInterface');
- $this->urlGenerator->expects($this->any())
- ->method('generateFromPath')
+ $this->urlAssembler = $this->getMock('Drupal\Core\Utility\UnroutedUrlAssemblerInterface');
+ $this->urlAssembler->expects($this->any())
+ ->method('assemble')
->will($this->returnArgument(0));
$this->router = $this->getMock('Drupal\Tests\Core\Routing\TestRouterInterface');
$container = new ContainerBuilder();
$container->set('router.no_access_checks', $this->router);
- $container->set('url_generator', $this->urlGenerator);
+ $container->set('unrouted_url_assembler', $this->urlAssembler);
\Drupal::setContainer($container);
}
@@ -65,7 +65,7 @@ protected function setUp() {
* @covers ::setExternal()
*/
public function testCreateFromPath() {
- $url = Url::createFromPath($this->path);
+ $url = Url::unrouted($this->path);
$this->assertInstanceOf('Drupal\Core\Url', $url);
$this->assertTrue($url->isExternal());
return $url;
@@ -172,8 +172,8 @@ public function testGetInternalPath(Url $url) {
*
* @covers ::getPath()
*/
- public function testGetPath(Url $url) {
- $this->assertNotNull($url->getPath());
+ public function testGetUri(Url $url) {
+ $this->assertNotNull($url->getUri());
}
/**
diff --git a/core/tests/Drupal/Tests/Core/UrlTest.php b/core/tests/Drupal/Tests/Core/UrlTest.php
index 133a73c..4f5108f 100644
--- a/core/tests/Drupal/Tests/Core/UrlTest.php
+++ b/core/tests/Drupal/Tests/Core/UrlTest.php
@@ -102,8 +102,8 @@ public function testCreateFromPath() {
$urls = array();
foreach ($this->map as $index => $values) {
- $path = trim(array_pop($values), '/');
- $url = Url::createFromPath($path);
+ $path = array_pop($values);
+ $url = Url::createFromRequest(Request::create("/$path"));
$this->assertSame($values, array_values($url->toArray()));
$urls[$index] = $url;
}
@@ -131,7 +131,7 @@ protected function getRequestConstraint($path) {
* @covers ::createFromPath()
*/
public function testCreateFromPathFront() {
- $url = Url::createFromPath('');
+ $url = Url::routed('');
$this->assertSame('', $url->getRouteName());
}
@@ -148,7 +148,7 @@ public function testCreateFromPathInvalid() {
->with($this->getRequestConstraint('/non-existent'))
->will($this->throwException(new ResourceNotFoundException()));
- $this->assertNull(Url::createFromPath('non-existent'));
+ $this->assertNull(Url::createFromRequest(Request::create('/non-existent')));
}
/**
@@ -227,8 +227,8 @@ public function testGetPathForInternalUrl($urls) {
* @covers ::getPath
*/
public function testGetPathForExternalUrl() {
- $url = Url::createFromPath('http://example.com/test');
- $this->assertEquals('http://example.com/test', $url->getPath());
+ $url = Url::unrouted('http://example.com/test');
+ $this->assertEquals('http://example.com/test', $url->getUri());
}
/**
@@ -292,7 +292,7 @@ public function testGetRouteName($urls) {
* @expectedException \UnexpectedValueException
*/
public function testGetRouteNameWithExternalUrl() {
- $url = Url::createFromPath('http://example.com');
+ $url = Url::unrouted('http://example.com');
$url->getRouteName();
}
@@ -319,7 +319,7 @@ public function testGetRouteParameters($urls) {
* @expectedException \UnexpectedValueException
*/
public function testGetRouteParametersWithExternalUrl() {
- $url = Url::createFromPath('http://example.com');
+ $url = Url::unrouted('http://example.com');
$url->getRouteParameters();
}
diff --git a/core/tests/Drupal/Tests/Core/Utility/LinkGeneratorTest.php b/core/tests/Drupal/Tests/Core/Utility/LinkGeneratorTest.php
index 7926b2f..72dd40b 100644
--- a/core/tests/Drupal/Tests/Core/Utility/LinkGeneratorTest.php
+++ b/core/tests/Drupal/Tests/Core/Utility/LinkGeneratorTest.php
@@ -151,7 +151,7 @@ public function testGenerateFromUrlExternal() {
->method('alter')
->with('link', $this->isType('array'));
- $url = Url::createFromPath('http://drupal.org');
+ $url = Url::unrouted('http://drupal.org');
$url->setUrlGenerator($this->urlGenerator);
$url->setOption('set_active_class', TRUE);
diff --git a/core/tests/Drupal/Tests/Core/Utility/UnroutedUrlAssemblerTest.php b/core/tests/Drupal/Tests/Core/Utility/UnroutedUrlAssemblerTest.php
new file mode 100644
index 0000000..492fb79
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/Utility/UnroutedUrlAssemblerTest.php
@@ -0,0 +1,129 @@
+requestStack = new RequestStack();
+ $this->configFactory = $this->getConfigFactoryStub(['system.filter' => []]);
+ $this->unroutedUrlAssembler = new UnroutedUrlAssembler($this->requestStack, $this->configFactory);
+ }
+
+ /**
+ * @covers ::assemble
+ * @expectedException \InvalidArgumentException
+ */
+ public function testAssembleWithNeitherExternalNokDomainLocalUri() {
+ $this->unroutedUrlAssembler->assemble('wrong-url');
+ }
+
+ /**
+ * @covers ::assemble
+ * @covers ::buildExternalUrl
+ *
+ * @dataProvider providerTestAssembleWithExternalUrl
+ */
+ public function testAssembleWithExternalUrl($uri, array $options, $expected) {
+ $this->assertEquals($expected, $this->unroutedUrlAssembler->assemble($uri, $options));
+ }
+
+ /**
+ * Provides test data for testAssembleWithExternalUrl
+ */
+ public function providerTestAssembleWithExternalUrl() {
+ return [
+ ['http://example.com/test', [], 'http://example.com/test'],
+ ['http://example.com/test', ['fragment' => 'example'], 'http://example.com/test#example'],
+ ['http://example.com/test', ['fragment' => 'example'], 'http://example.com/test#example'],
+ ['http://example.com/test', ['query' => ['foo' => 'bar']], 'http://example.com/test?foo=bar'],
+ ['http://example.com/test', ['https' => TRUE], 'https://example.com/test'],
+ ['https://example.com/test', ['https' => FALSE], 'http://example.com/test'],
+ ['https://example.com/test?foo=1#bar', [], 'https://example.com/test?foo=1#bar'],
+ ];
+ }
+
+ /**
+ * @covers ::assemble
+ * @covers::buildLocalUrl
+ *
+ * @dataProvider providerTestAssembleWithLocalUri
+ */
+ public function testAssembleWithLocalUri($uri, array $options, $subdir, $expected) {
+ $server = [];
+ if ($subdir) {
+ // Setup a fake request which looks like a Drupal installed under the
+ // subdir "subdir" on the domain www.example.com.
+ // To reproduce the values install Drupal like that and use a debugger.
+ $server = [
+ 'SCRIPT_NAME' => '/subdir/index.php',
+ 'SCRIPT_FILENAME' => DRUPAL_ROOT . '/index.php',
+ 'SERVER_NAME' => 'http://www.example.com',
+ ];
+ $request = Request::create('/subdir');
+ }
+ else {
+ $request = Request::create('/');
+ }
+ $request->server->add($server);
+ $this->requestStack->push($request);
+
+ $this->assertEquals($expected, $this->unroutedUrlAssembler->assemble($uri, $options));
+ }
+
+ /**
+ * @return array
+ */
+ public function providerTestAssembleWithLocalUri() {
+ return [
+ ['base://example', [], FALSE, '/example'],
+ ['base://example', ['query' => ['foo' => 'bar']], FALSE, '/example?foo=bar'],
+ ['base://example', ['fragment' => 'example', ], FALSE, '/example#example'],
+ ['base://example', [], TRUE, '/subdir/example'],
+ ['base://example', ['query' => ['foo' => 'bar']], TRUE, '/subdir/example?foo=bar'],
+ ['base://example', ['fragment' => 'example', ], TRUE, '/subdir/example#example'],
+ ];
+ }
+
+}
+