diff --git a/core/lib/Drupal/Core/Url.php b/core/lib/Drupal/Core/Url.php index 246fe72..92626d7 100644 --- a/core/lib/Drupal/Core/Url.php +++ b/core/lib/Drupal/Core/Url.php @@ -19,6 +19,22 @@ /** * Defines an object that holds information about a URL. + * + * This class supports: + * - External URLs. + * - Internal URLs that correspond to routes handled by Drupal's routing system. + * - Base-relative and host-relative URLs not handled by Drupal. + * - URLs for user input. + * - URLs for entities. + * + * New URLs should generally be constructed using one of the static factory + * methods. See: + * - Url::fromRoute() + * - Url::fromRouteMatch() + * - Url::fromUri() + * - Url::fromUserInput() + * - Url::fromBaseRelativePath() + * - Url::fromHostRelativePath() */ class Url { @@ -28,21 +44,38 @@ class Url { * Scheme for base-relative, unrouted URIs. * * This scheme should only be used for unrouted URIs, like base:robots.txt - * or base:update.php. To create such a base-relative URI from a path, use - * Url::fromBaseRelativePath(). + * or base:core/update.php. To create such a base-relative URI from a path, + * use Url::fromBaseRelativePath(). To create a host-relative URI instead + * (for example, for http://example.com/index.html when Drupal is installed + * in http://example.com/drupal/), use Url::fromHostRelativePath(). * * @see static::fromBaseRelativePath() + * @see static::fromHostRelativePath() * @see static::fromRoute() * @see static::fromUri() */ const SCHEME_BASE = 'base'; /** + * Scheme for URIs from user input. + * + * To create a URL for paths from user input that may or may not correspond + * to a valid Drupal route, use Url::fromUserInput(). + * + * @see static::fromUserInput() + * @see static::fromUserPathUri() + * + * @todo Rename this constant depending on the outcome of: + * https://www.drupal.org/node/2417567 + */ + const SCHEME_USER_PATH = 'user-path'; + + /** * Scheme for entity URIs. * * This scheme should be used for entity URIs, which are given in the form * entity:{entity_type}/{entity_id}. To get the URL for an entity, generally - * you should use \Drupal\core\Entity\Entity::url() rather than creating a + * you should use \Drupal\Core\Entity\Entity::url() rather than creating a * URI directly. * * @see static::fromEntityUri() @@ -52,6 +85,9 @@ class Url { /** * Scheme for routed URIs. * + * This scheme is used internally for handling routes as URIs. To get the + * URL for a route, use Url::fromRoute(). + * * @see static::fromRouteUri() * * @todo What exactly is the point of this scheme? See: @@ -138,8 +174,11 @@ class Url { * constructing Url objects directly, in order to avoid ambiguity and make * your code more self-documenting: * - Url::fromRoute() + * - Url::fromRouteMatch() * - Url::fromUri() + * - Url::fromUserInput() * - Url::fromBaseRelativePath() + * - Url::fromHostRelativePath() * * @param string $route_name_or_uri * The name of the route or URI. @@ -161,10 +200,6 @@ class Url { * - 'https': Whether this URL should point to a secure location. If not * defined, the current scheme is used, so the user stays on HTTP or HTTPS * respectively. TRUE enforces HTTPS and FALSE enforces HTTP. - * - * @see static::fromRoute() - * @see static::fromUri() - * @see static::fromBaseRelativePath() */ public function __construct($route_name_or_uri, $route_parameters = array(), $options = array()) { $this->routeName = $route_name_or_uri; @@ -176,8 +211,9 @@ public function __construct($route_name_or_uri, $route_parameters = array(), $op * Creates a new Url object for a URL that has a Drupal route. * * This method is for URLs that have Drupal routes (that is, most pages - * generated by Drupal). For non-routed local URIs relative to the base - * path (like robots.txt) use Url::fromBaseRelativePath(). + * generated by Drupal). To generate a URL for a route match instead (that + * is, the route corresponding to a particular request), use + * Url::fromRouteMatch(). * * @param string $route_name * The name of the route @@ -203,8 +239,10 @@ public function __construct($route_name_or_uri, $route_parameters = array(), $op * @return \Drupal\Core\Url * A new Url object for a routed (internal to Drupal) URL. * - * @see static::fromUri() + * @see static::fromRouteMatch() + * @see static::fromUserInput() * @see static::fromBaseRelativePath() + * @see static::fromUri() */ public static function fromRoute($route_name, $route_parameters = array(), $options = array()) { return new static($route_name, $route_parameters, $options); @@ -215,7 +253,7 @@ public static function fromRoute($route_name, $route_parameters = array(), $opti * * This method should only be used for paths within the base directory of the * site installation that do not have a Drupal route. For example, robots.txt - * and update.php are unrouted, base-relative paths. + * and core/update.php are unrouted, base-relative paths. * * Do not use this method for URLs that do have a Drupal route (that is, most * pages generated by Drupal) because this will circumvent the routing system. @@ -229,6 +267,7 @@ public static function fromRoute($route_name, $route_parameters = array(), $opti * A new Url object for an unrouted, base-relative URL not internal to * Drupal, but within the site directory. * + * @see static::fromHostRelativePath() * @see static::fromUri() * @see static::fromRoute() */ @@ -237,12 +276,50 @@ public static function fromBaseRelativePath($path, $options = array()) { } /** - * Creates a new URL object from a route match. + * Creates a new URL object from an unrouted, host-relative path. + * + * This method should only be used for paths within the base directory of the + * site installation that do not have a Drupal route. For example, robots.txt + * and core/update.php are unrouted, base-relative paths. + * + * Do not use this method for URLs that do have a Drupal route (that is, most + * pages generated by Drupal) because this will circumvent the routing system. + * + * @param string $path + * The host-relative path. For example, if Drupal is installed in the + * subdirectory http://example.com/drupal/, use 'index.html' for a URL to + * http://example.com/index.html. + * @param array $options + * (optional) An array of options. See static::fromUri() for details. + * + * @return \Drupal\Core\Url + * A new Url object for an unrouted, host-relative URL not internal to + * Drupal, but relative to the site host. + * + * @see static::fromBaseRelativePath() + * @see static::fromUri() + * @see static::fromRoute() + */ + public static function fromHostRelativePath($path, $options = array()) { + return static::fromUri(static::SCHEME_BASE . ':/' . $path, $options); + } + + /** + * Creates a new URL object from a route match (e.g. for the current request). + * + * Use this method to create a URL from a specific route match, that is, + * the route that best corresponds to a particular request. This is often + * used to generate URLs based on the URL for the current request. To + * generate a URL for a specific route (rather than from a request), use + * Url::fromRoute(). * * @param \Drupal\Core\Routing\RouteMatchInterface $route_match * The route match. * * @return $this + * + * @see static::fromRoute() + * @see \Drupal\Core\Routing\RouteMatchInterface */ public static function fromRouteMatch(RouteMatchInterface $route_match) { if ($route_match->getRouteObject()) { @@ -256,24 +333,28 @@ public static function fromRouteMatch(RouteMatchInterface $route_match) { /** * Creates a new Url object from a URI. * - * This method is for generating URLs for URIs that: - * - do not have Drupal routes: both external URLs and unrouted local URIs, - * like base:robots.txt. - * - do have a Drupal route, but have a custom scheme to simplify linking. - * Currently, there is only the entity: scheme (This allows URIs of the - * form entity:{entity_type}/{entity_id}. For example: entity:node/1 - * resolves to the entity.node.canonical route with a node parameter of 1.) + * This method is used to create URLs for internal and external URIs with a + * unified API and storage format. * - * For URLs that have Drupal routes (that is, most pages generated by Drupal), - * use Url::fromRoute(). + * - For URIs that have Drupal routes (that is, most pages generated by + * Drupal), use Url::fromRoute() whenever possible, to avoid circumventing + * the routing system. + * - For paths from user input that may or may not correspond to a valid + * Drupal route, use Url::fromUserInput(). + * - For base-relative and host-relative paths that are not created by Drupal, + * use Url::fromBaseRelativePath() and Url::fromHostRelativePath(), + * respectively. + * - For entity URIs, use \Drupal\core\Entity\EntityInterface::url(). + * + * You can call Url::access() on the returned object to do access checking. * * @param string $uri - * The URI of the resource including the scheme. For user input that may - * correspond to a Drupal route, use user-path: for the scheme. For paths - * that are known not to be handled by the Drupal routing system (such as - * static files), use base: for the scheme to get a link relative to the - * Drupal base path (like the HTML element). For a link to an entity - * you may use entity:{entity_type}/{entity_id} URIs. + * The URI of the resource including the scheme. Several special internal + * schemes are supported. See the documentation of each for details: + * - Url::SCHEME_BASE + * - Url::SCHEME_ROUTE + * - Url::SCHEME_ENTITY + * - Url::SCHEME_USER_PATH * @param array $options * (optional) An associative array of additional URL options, with the * following elements: @@ -291,12 +372,6 @@ public static function fromRouteMatch(RouteMatchInterface $route_match) { * defined, the current scheme is used, so the user stays on HTTP or HTTPS * respectively. TRUE enforces HTTPS and FALSE enforces HTTP. * - * Note: the user-path: scheme should be avoided except when processing actual - * user input that may or may not correspond to a Drupal route. Normally use - * Url::fromRoute() for code linking to any any Drupal page. - * - * You can call access() on the returned object to do access checking. - * * @return \Drupal\Core\Url * A new Url object with properties depending on the URI scheme. * @@ -304,9 +379,10 @@ public static function fromRouteMatch(RouteMatchInterface $route_match) { * Thrown when the passed in path has no scheme. * * @see static::fromRoute() - * @see static::fromBaseRelativeUri() - * - * @todo Update this documentation for the new constants and methods. + * @see static::fromUserInput() + * @see static::fromBaseRelativePath() + * @see static::fromHostRelativePath() + * @see \Drupal\Core\Entity\EntityInterface::url() */ public static function fromUri($uri, $options = []) { $uri_parts = parse_url($uri); @@ -314,8 +390,7 @@ public static function fromUri($uri, $options = []) { throw new \InvalidArgumentException(String::format('The URI "@uri" is malformed.', ['@uri' => $uri])); } if (empty($uri_parts['scheme'])) { - // @todo Update this exception message for the new constants. - throw new \InvalidArgumentException(String::format('The URI "@uri" is invalid. You must use a valid URI scheme. Use base: for items like a static file that needs the base path. Use user-path: for user input without a scheme. Use entity: for referencing the canonical route of a content entity. Use route: for directly representing a route name and parameters.', ['@uri' => $uri])); + throw new \InvalidArgumentException(String::format('The URI "@uri" is invalid. You must use a valid URI scheme.', ['@uri' => $uri])); } $uri_parts += ['path' => '']; // Discard empty fragment in $options for consistency with parse_url(). @@ -340,7 +415,7 @@ public static function fromUri($uri, $options = []) { if ($uri_parts['scheme'] === static::SCHEME_ENTITY) { $url = static::fromEntityUri($uri_parts, $uri_options, $uri); } - elseif ($uri_parts['scheme'] === 'user-path') { + elseif ($uri_parts['scheme'] === static::SCHEME_USER_PATH) { $url = static::fromUserPathUri($uri_parts, $uri_options); } elseif ($uri_parts['scheme'] === static::SCHEME_ROUTE) { @@ -395,6 +470,9 @@ protected static function fromEntityUri(array $uri_parts, array $options, $uri) * * @return \Drupal\Core\Url * A new Url object for a 'user-path:' URI. + * + * @todo Rename this method depending on the outcome of: + * https://www.drupal.org/node/2417567 */ protected static function fromUserPathUri(array $uri_parts, array $options) { $url = \Drupal::pathValidator() @@ -498,7 +576,7 @@ protected function setUnrouted() { */ public function toUriString() { if ($this->isRouted()) { - $uri = static::SCHEME_ROUTE . $this->routeName; + $uri = static::SCHEME_ROUTE . ':' . $this->routeName; if ($this->routeParameters) { $uri .= ';' . UrlHelper::buildQuery($this->routeParameters); }