Mercurial > hg > isophonics-drupal-site
comparison core/lib/Drupal/Core/Url.php @ 0:4c8ae668cc8c
Initial import (non-working)
author | Chris Cannam |
---|---|
date | Wed, 29 Nov 2017 16:09:58 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:4c8ae668cc8c |
---|---|
1 <?php | |
2 | |
3 namespace Drupal\Core; | |
4 | |
5 use Drupal\Component\Utility\NestedArray; | |
6 use Drupal\Component\Utility\UrlHelper; | |
7 use Drupal\Core\DependencyInjection\DependencySerializationTrait; | |
8 use Drupal\Core\Routing\RouteMatchInterface; | |
9 use Drupal\Core\Routing\UrlGeneratorInterface; | |
10 use Drupal\Core\Session\AccountInterface; | |
11 use Drupal\Core\Utility\UnroutedUrlAssemblerInterface; | |
12 use Symfony\Cmf\Component\Routing\RouteObjectInterface; | |
13 use Symfony\Component\HttpFoundation\Request; | |
14 | |
15 /** | |
16 * Defines an object that holds information about a URL. | |
17 */ | |
18 class Url { | |
19 use DependencySerializationTrait; | |
20 | |
21 /** | |
22 * The URL generator. | |
23 * | |
24 * @var \Drupal\Core\Routing\UrlGeneratorInterface | |
25 */ | |
26 protected $urlGenerator; | |
27 | |
28 /** | |
29 * The unrouted URL assembler. | |
30 * | |
31 * @var \Drupal\Core\Utility\UnroutedUrlAssemblerInterface | |
32 */ | |
33 protected $urlAssembler; | |
34 | |
35 /** | |
36 * The access manager | |
37 * | |
38 * @var \Drupal\Core\Access\AccessManagerInterface | |
39 */ | |
40 protected $accessManager; | |
41 | |
42 /** | |
43 * The route name. | |
44 * | |
45 * @var string | |
46 */ | |
47 protected $routeName; | |
48 | |
49 /** | |
50 * The route parameters. | |
51 * | |
52 * @var array | |
53 */ | |
54 protected $routeParameters = []; | |
55 | |
56 /** | |
57 * The URL options. | |
58 * | |
59 * See \Drupal\Core\Url::fromUri() for details on the options. | |
60 * | |
61 * @var array | |
62 */ | |
63 protected $options = []; | |
64 | |
65 /** | |
66 * Indicates whether this object contains an external URL. | |
67 * | |
68 * @var bool | |
69 */ | |
70 protected $external = FALSE; | |
71 | |
72 /** | |
73 * Indicates whether this URL is for a URI without a Drupal route. | |
74 * | |
75 * @var bool | |
76 */ | |
77 protected $unrouted = FALSE; | |
78 | |
79 /** | |
80 * The non-route URI. | |
81 * | |
82 * Only used if self::$unrouted is TRUE. | |
83 * | |
84 * @var string | |
85 */ | |
86 protected $uri; | |
87 | |
88 /** | |
89 * Stores the internal path, if already requested by getInternalPath(). | |
90 * | |
91 * @var string | |
92 */ | |
93 protected $internalPath; | |
94 | |
95 /** | |
96 * Constructs a new Url object. | |
97 * | |
98 * In most cases, use Url::fromRoute() or Url::fromUri() rather than | |
99 * constructing Url objects directly in order to avoid ambiguity and make your | |
100 * code more self-documenting. | |
101 * | |
102 * @param string $route_name | |
103 * The name of the route | |
104 * @param array $route_parameters | |
105 * (optional) An associative array of parameter names and values. | |
106 * @param array $options | |
107 * See \Drupal\Core\Url::fromUri() for details. | |
108 * | |
109 * @see static::fromRoute() | |
110 * @see static::fromUri() | |
111 * | |
112 * @todo Update this documentation for non-routed URIs in | |
113 * https://www.drupal.org/node/2346787 | |
114 */ | |
115 public function __construct($route_name, $route_parameters = [], $options = []) { | |
116 $this->routeName = $route_name; | |
117 $this->routeParameters = $route_parameters; | |
118 $this->options = $options; | |
119 } | |
120 | |
121 /** | |
122 * Creates a new Url object for a URL that has a Drupal route. | |
123 * | |
124 * This method is for URLs that have Drupal routes (that is, most pages | |
125 * generated by Drupal). For non-routed local URIs relative to the base | |
126 * path (like robots.txt) use Url::fromUri() with the base: scheme. | |
127 * | |
128 * @param string $route_name | |
129 * The name of the route | |
130 * @param array $route_parameters | |
131 * (optional) An associative array of route parameter names and values. | |
132 * @param array $options | |
133 * See \Drupal\Core\Url::fromUri() for details. | |
134 * | |
135 * @return \Drupal\Core\Url | |
136 * A new Url object for a routed (internal to Drupal) URL. | |
137 * | |
138 * @see \Drupal\Core\Url::fromUserInput() | |
139 * @see \Drupal\Core\Url::fromUri() | |
140 */ | |
141 public static function fromRoute($route_name, $route_parameters = [], $options = []) { | |
142 return new static($route_name, $route_parameters, $options); | |
143 } | |
144 | |
145 /** | |
146 * Creates a new URL object from a route match. | |
147 * | |
148 * @param \Drupal\Core\Routing\RouteMatchInterface $route_match | |
149 * The route match. | |
150 * | |
151 * @return $this | |
152 */ | |
153 public static function fromRouteMatch(RouteMatchInterface $route_match) { | |
154 if ($route_match->getRouteObject()) { | |
155 return new static($route_match->getRouteName(), $route_match->getRawParameters()->all()); | |
156 } | |
157 else { | |
158 throw new \InvalidArgumentException('Route required'); | |
159 } | |
160 } | |
161 | |
162 /** | |
163 * Creates a Url object for a relative URI reference submitted by user input. | |
164 * | |
165 * Use this method to create a URL for user-entered paths that may or may not | |
166 * correspond to a valid Drupal route. | |
167 * | |
168 * @param string $user_input | |
169 * User input for a link or path. The first character must be one of the | |
170 * following characters: | |
171 * - '/': A path within the current site. This path might be to a Drupal | |
172 * route (e.g., '/admin'), to a file (e.g., '/README.txt'), or to | |
173 * something processed by a non-Drupal script (e.g., | |
174 * '/not/a/drupal/page'). If the path matches a Drupal route, then the | |
175 * URL generation will include Drupal's path processors (e.g., | |
176 * language-prefixing and aliasing). Otherwise, the URL generation will | |
177 * just append the passed-in path to Drupal's base path. | |
178 * - '?': A query string for the current page or resource. | |
179 * - '#': A fragment (jump-link) on the current page or resource. | |
180 * This helps reduce ambiguity for user-entered links and paths, and | |
181 * supports user interfaces where users may normally use auto-completion | |
182 * to search for existing resources, but also may type one of these | |
183 * characters to link to (e.g.) a specific path on the site. | |
184 * (With regard to the URI specification, the user input is treated as a | |
185 * @link https://tools.ietf.org/html/rfc3986#section-4.2 relative URI reference @endlink | |
186 * where the relative part is of type | |
187 * @link https://tools.ietf.org/html/rfc3986#section-3.3 path-abempty @endlink.) | |
188 * @param array $options | |
189 * (optional) An array of options. See Url::fromUri() for details. | |
190 * | |
191 * @return static | |
192 * A new Url object based on user input. | |
193 * | |
194 * @throws \InvalidArgumentException | |
195 * Thrown when the user input does not begin with one of the following | |
196 * characters: '/', '?', or '#'. | |
197 */ | |
198 public static function fromUserInput($user_input, $options = []) { | |
199 // Ensuring one of these initial characters also enforces that what is | |
200 // passed is a relative URI reference rather than an absolute URI, | |
201 // because these are URI reserved characters that a scheme name may not | |
202 // start with. | |
203 if ((strpos($user_input, '/') !== 0) && (strpos($user_input, '#') !== 0) && (strpos($user_input, '?') !== 0)) { | |
204 throw new \InvalidArgumentException("The user-entered string '$user_input' must begin with a '/', '?', or '#'."); | |
205 } | |
206 | |
207 // fromUri() requires an absolute URI, so prepend the appropriate scheme | |
208 // name. | |
209 return static::fromUri('internal:' . $user_input, $options); | |
210 } | |
211 | |
212 /** | |
213 * Creates a new Url object from a URI. | |
214 * | |
215 * This method is for generating URLs for URIs that: | |
216 * - do not have Drupal routes: both external URLs and unrouted local URIs | |
217 * like base:robots.txt | |
218 * - do have a Drupal route but have a custom scheme to simplify linking. | |
219 * Currently, there is only the entity: scheme (This allows URIs of the | |
220 * form entity:{entity_type}/{entity_id}. For example: entity:node/1 | |
221 * resolves to the entity.node.canonical route with a node parameter of 1.) | |
222 * | |
223 * For URLs that have Drupal routes (that is, most pages generated by Drupal), | |
224 * use Url::fromRoute(). | |
225 * | |
226 * @param string $uri | |
227 * The URI of the resource including the scheme. For user input that may | |
228 * correspond to a Drupal route, use internal: for the scheme. For paths | |
229 * that are known not to be handled by the Drupal routing system (such as | |
230 * static files), use base: for the scheme to get a link relative to the | |
231 * Drupal base path (like the <base> HTML element). For a link to an entity | |
232 * you may use entity:{entity_type}/{entity_id} URIs. The internal: scheme | |
233 * should be avoided except when processing actual user input that may or | |
234 * may not correspond to a Drupal route. Normally use Url::fromRoute() for | |
235 * code linking to any any Drupal page. | |
236 * @param array $options | |
237 * (optional) An associative array of additional URL options, with the | |
238 * following elements: | |
239 * - 'query': An array of query key/value-pairs (without any URL-encoding) | |
240 * to append to the URL. | |
241 * - 'fragment': A fragment identifier (named anchor) to append to the URL. | |
242 * Do not include the leading '#' character. | |
243 * - 'absolute': Defaults to FALSE. Whether to force the output to be an | |
244 * absolute link (beginning with http:). Useful for links that will be | |
245 * displayed outside the site, such as in an RSS feed. | |
246 * - 'attributes': An associative array of HTML attributes that will be | |
247 * added to the anchor tag if you use the \Drupal\Core\Link class to make | |
248 * the link. | |
249 * - 'language': An optional language object used to look up the alias | |
250 * for the URL. If $options['language'] is omitted, it defaults to the | |
251 * current language for the language type LanguageInterface::TYPE_URL. | |
252 * - 'https': Whether this URL should point to a secure location. If not | |
253 * defined, the current scheme is used, so the user stays on HTTP or HTTPS | |
254 * respectively. TRUE enforces HTTPS and FALSE enforces HTTP. | |
255 * | |
256 * @return \Drupal\Core\Url | |
257 * A new Url object with properties depending on the URI scheme. Call the | |
258 * access() method on this to do access checking. | |
259 * | |
260 * @throws \InvalidArgumentException | |
261 * Thrown when the passed in path has no scheme. | |
262 * | |
263 * @see \Drupal\Core\Url::fromRoute() | |
264 * @see \Drupal\Core\Url::fromUserInput() | |
265 */ | |
266 public static function fromUri($uri, $options = []) { | |
267 // parse_url() incorrectly parses base:number/... as hostname:port/... | |
268 // and not the scheme. Prevent that by prefixing the path with a slash. | |
269 if (preg_match('/^base:\d/', $uri)) { | |
270 $uri = str_replace('base:', 'base:/', $uri); | |
271 } | |
272 $uri_parts = parse_url($uri); | |
273 if ($uri_parts === FALSE) { | |
274 throw new \InvalidArgumentException("The URI '$uri' is malformed."); | |
275 } | |
276 // We support protocol-relative URLs. | |
277 if (strpos($uri, '//') === 0) { | |
278 $uri_parts['scheme'] = ''; | |
279 } | |
280 elseif (empty($uri_parts['scheme'])) { | |
281 throw new \InvalidArgumentException("The URI '$uri' is invalid. You must use a valid URI scheme."); | |
282 } | |
283 $uri_parts += ['path' => '']; | |
284 // Discard empty fragment in $options for consistency with parse_url(). | |
285 if (isset($options['fragment']) && strlen($options['fragment']) == 0) { | |
286 unset($options['fragment']); | |
287 } | |
288 // Extract query parameters and fragment and merge them into $uri_options, | |
289 // but preserve the original $options for the fallback case. | |
290 $uri_options = $options; | |
291 if (isset($uri_parts['fragment'])) { | |
292 $uri_options += ['fragment' => $uri_parts['fragment']]; | |
293 unset($uri_parts['fragment']); | |
294 } | |
295 | |
296 if (!empty($uri_parts['query'])) { | |
297 $uri_query = []; | |
298 parse_str($uri_parts['query'], $uri_query); | |
299 $uri_options['query'] = isset($uri_options['query']) ? $uri_options['query'] + $uri_query : $uri_query; | |
300 unset($uri_parts['query']); | |
301 } | |
302 | |
303 if ($uri_parts['scheme'] === 'entity') { | |
304 $url = static::fromEntityUri($uri_parts, $uri_options, $uri); | |
305 } | |
306 elseif ($uri_parts['scheme'] === 'internal') { | |
307 $url = static::fromInternalUri($uri_parts, $uri_options); | |
308 } | |
309 elseif ($uri_parts['scheme'] === 'route') { | |
310 $url = static::fromRouteUri($uri_parts, $uri_options, $uri); | |
311 } | |
312 else { | |
313 $url = new static($uri, [], $options); | |
314 if ($uri_parts['scheme'] !== 'base') { | |
315 $url->external = TRUE; | |
316 $url->setOption('external', TRUE); | |
317 } | |
318 $url->setUnrouted(); | |
319 } | |
320 | |
321 return $url; | |
322 } | |
323 | |
324 /** | |
325 * Create a new Url object for entity URIs. | |
326 * | |
327 * @param array $uri_parts | |
328 * Parts from an URI of the form entity:{entity_type}/{entity_id} as from | |
329 * parse_url(). | |
330 * @param array $options | |
331 * An array of options, see \Drupal\Core\Url::fromUri() for details. | |
332 * @param string $uri | |
333 * The original entered URI. | |
334 * | |
335 * @return \Drupal\Core\Url | |
336 * A new Url object for an entity's canonical route. | |
337 * | |
338 * @throws \InvalidArgumentException | |
339 * Thrown if the entity URI is invalid. | |
340 */ | |
341 protected static function fromEntityUri(array $uri_parts, array $options, $uri) { | |
342 list($entity_type_id, $entity_id) = explode('/', $uri_parts['path'], 2); | |
343 if ($uri_parts['scheme'] != 'entity' || $entity_id === '') { | |
344 throw new \InvalidArgumentException("The entity URI '$uri' is invalid. You must specify the entity id in the URL. e.g., entity:node/1 for loading the canonical path to node entity with id 1."); | |
345 } | |
346 | |
347 return new static("entity.$entity_type_id.canonical", [$entity_type_id => $entity_id], $options); | |
348 } | |
349 | |
350 /** | |
351 * Creates a new Url object for 'internal:' URIs. | |
352 * | |
353 * Important note: the URI minus the scheme can NOT simply be validated by a | |
354 * \Drupal\Core\Path\PathValidatorInterface implementation. The semantics of | |
355 * the 'internal:' URI scheme are different: | |
356 * - PathValidatorInterface accepts paths without a leading slash (e.g. | |
357 * 'node/add') as well as 2 special paths: '<front>' and '<none>', which are | |
358 * mapped to the correspondingly named routes. | |
359 * - 'internal:' URIs store paths with a leading slash that represents the | |
360 * root — i.e. the front page — (e.g. 'internal:/node/add'), and doesn't | |
361 * have any exceptions. | |
362 * | |
363 * To clarify, a few examples of path plus corresponding 'internal:' URI: | |
364 * - 'node/add' -> 'internal:/node/add' | |
365 * - 'node/add?foo=bar' -> 'internal:/node/add?foo=bar' | |
366 * - 'node/add#kitten' -> 'internal:/node/add#kitten' | |
367 * - '<front>' -> 'internal:/' | |
368 * - '<front>foo=bar' -> 'internal:/?foo=bar' | |
369 * - '<front>#kitten' -> 'internal:/#kitten' | |
370 * - '<none>' -> 'internal:' | |
371 * - '<none>foo=bar' -> 'internal:?foo=bar' | |
372 * - '<none>#kitten' -> 'internal:#kitten' | |
373 * | |
374 * Therefore, when using a PathValidatorInterface to validate 'internal:' | |
375 * URIs, we must map: | |
376 * - 'internal:' (path component is '') to the special '<none>' path | |
377 * - 'internal:/' (path component is '/') to the special '<front>' path | |
378 * - 'internal:/some-path' (path component is '/some-path') to 'some-path' | |
379 * | |
380 * @param array $uri_parts | |
381 * Parts from an URI of the form internal:{path} as from parse_url(). | |
382 * @param array $options | |
383 * An array of options, see \Drupal\Core\Url::fromUri() for details. | |
384 * | |
385 * @return \Drupal\Core\Url | |
386 * A new Url object for a 'internal:' URI. | |
387 * | |
388 * @throws \InvalidArgumentException | |
389 * Thrown when the URI's path component doesn't have a leading slash. | |
390 */ | |
391 protected static function fromInternalUri(array $uri_parts, array $options) { | |
392 // Both PathValidator::getUrlIfValidWithoutAccessCheck() and 'base:' URIs | |
393 // only accept/contain paths without a leading slash, unlike 'internal:' | |
394 // URIs, for which the leading slash means "relative to Drupal root" and | |
395 // "relative to Symfony app root" (just like in Symfony/Drupal 8 routes). | |
396 if (empty($uri_parts['path'])) { | |
397 $uri_parts['path'] = '<none>'; | |
398 } | |
399 elseif ($uri_parts['path'] === '/') { | |
400 $uri_parts['path'] = '<front>'; | |
401 } | |
402 else { | |
403 if ($uri_parts['path'][0] !== '/') { | |
404 throw new \InvalidArgumentException("The internal path component '{$uri_parts['path']}' is invalid. Its path component must have a leading slash, e.g. internal:/foo."); | |
405 } | |
406 // Remove the leading slash. | |
407 $uri_parts['path'] = substr($uri_parts['path'], 1); | |
408 | |
409 if (UrlHelper::isExternal($uri_parts['path'])) { | |
410 throw new \InvalidArgumentException("The internal path component '{$uri_parts['path']}' is external. You are not allowed to specify an external URL together with internal:/."); | |
411 } | |
412 } | |
413 | |
414 $url = \Drupal::pathValidator() | |
415 ->getUrlIfValidWithoutAccessCheck($uri_parts['path']) ?: static::fromUri('base:' . $uri_parts['path'], $options); | |
416 // Allow specifying additional options. | |
417 $url->setOptions($options + $url->getOptions()); | |
418 | |
419 return $url; | |
420 } | |
421 | |
422 /** | |
423 * Creates a new Url object for 'route:' URIs. | |
424 * | |
425 * @param array $uri_parts | |
426 * Parts from an URI of the form route:{route_name};{route_parameters} as | |
427 * from parse_url(), where the path is the route name optionally followed by | |
428 * a ";" followed by route parameters in key=value format with & separators. | |
429 * @param array $options | |
430 * An array of options, see \Drupal\Core\Url::fromUri() for details. | |
431 * @param string $uri | |
432 * The original passed in URI. | |
433 * | |
434 * @return \Drupal\Core\Url | |
435 * A new Url object for a 'route:' URI. | |
436 * | |
437 * @throws \InvalidArgumentException | |
438 * Thrown when the route URI does not have a route name. | |
439 */ | |
440 protected static function fromRouteUri(array $uri_parts, array $options, $uri) { | |
441 $route_parts = explode(';', $uri_parts['path'], 2); | |
442 $route_name = $route_parts[0]; | |
443 if ($route_name === '') { | |
444 throw new \InvalidArgumentException("The route URI '$uri' is invalid. You must have a route name in the URI. e.g., route:system.admin"); | |
445 } | |
446 $route_parameters = []; | |
447 if (!empty($route_parts[1])) { | |
448 parse_str($route_parts[1], $route_parameters); | |
449 } | |
450 | |
451 return new static($route_name, $route_parameters, $options); | |
452 } | |
453 | |
454 /** | |
455 * Returns the Url object matching a request. | |
456 * | |
457 * SECURITY NOTE: The request path is not checked to be valid and accessible | |
458 * by the current user to allow storing and reusing Url objects by different | |
459 * users. The 'path.validator' service getUrlIfValid() method should be used | |
460 * instead of this one if validation and access check is desired. Otherwise, | |
461 * 'access_manager' service checkNamedRoute() method should be used on the | |
462 * router name and parameters stored in the Url object returned by this | |
463 * method. | |
464 * | |
465 * @param \Symfony\Component\HttpFoundation\Request $request | |
466 * A request object. | |
467 * | |
468 * @return static | |
469 * A Url object. Warning: the object is created even if the current user | |
470 * would get an access denied running the same request via the normal page | |
471 * flow. | |
472 * | |
473 * @throws \Drupal\Core\Routing\MatchingRouteNotFoundException | |
474 * Thrown when the request cannot be matched. | |
475 */ | |
476 public static function createFromRequest(Request $request) { | |
477 // We use the router without access checks because URL objects might be | |
478 // created and stored for different users. | |
479 $result = \Drupal::service('router.no_access_checks')->matchRequest($request); | |
480 $route_name = $result[RouteObjectInterface::ROUTE_NAME]; | |
481 $route_parameters = $result['_raw_variables']->all(); | |
482 return new static($route_name, $route_parameters); | |
483 } | |
484 | |
485 /** | |
486 * Sets this Url to encapsulate an unrouted URI. | |
487 * | |
488 * @return $this | |
489 */ | |
490 protected function setUnrouted() { | |
491 $this->unrouted = TRUE; | |
492 // What was passed in as the route name is actually the URI. | |
493 // @todo Consider fixing this in https://www.drupal.org/node/2346787. | |
494 $this->uri = $this->routeName; | |
495 // Set empty route name and parameters. | |
496 $this->routeName = NULL; | |
497 $this->routeParameters = []; | |
498 return $this; | |
499 } | |
500 | |
501 /** | |
502 * Generates a URI string that represents the data in the Url object. | |
503 * | |
504 * The URI will typically have the scheme of route: even if the object was | |
505 * constructed using an entity: or internal: scheme. A internal: URI that | |
506 * does not match a Drupal route with be returned here with the base: scheme, | |
507 * and external URLs will be returned in their original form. | |
508 * | |
509 * @return string | |
510 * A URI representation of the Url object data. | |
511 */ | |
512 public function toUriString() { | |
513 if ($this->isRouted()) { | |
514 $uri = 'route:' . $this->routeName; | |
515 if ($this->routeParameters) { | |
516 $uri .= ';' . UrlHelper::buildQuery($this->routeParameters); | |
517 } | |
518 } | |
519 else { | |
520 $uri = $this->uri; | |
521 } | |
522 $query = !empty($this->options['query']) ? ('?' . UrlHelper::buildQuery($this->options['query'])) : ''; | |
523 $fragment = isset($this->options['fragment']) && strlen($this->options['fragment']) ? '#' . $this->options['fragment'] : ''; | |
524 return $uri . $query . $fragment; | |
525 } | |
526 | |
527 /** | |
528 * Indicates if this Url is external. | |
529 * | |
530 * @return bool | |
531 */ | |
532 public function isExternal() { | |
533 return $this->external; | |
534 } | |
535 | |
536 /** | |
537 * Indicates if this Url has a Drupal route. | |
538 * | |
539 * @return bool | |
540 */ | |
541 public function isRouted() { | |
542 return !$this->unrouted; | |
543 } | |
544 | |
545 /** | |
546 * Returns the route name. | |
547 * | |
548 * @return string | |
549 * | |
550 * @throws \UnexpectedValueException. | |
551 * If this is a URI with no corresponding route. | |
552 */ | |
553 public function getRouteName() { | |
554 if ($this->unrouted) { | |
555 throw new \UnexpectedValueException('External URLs do not have an internal route name.'); | |
556 } | |
557 | |
558 return $this->routeName; | |
559 } | |
560 | |
561 /** | |
562 * Returns the route parameters. | |
563 * | |
564 * @return array | |
565 * | |
566 * @throws \UnexpectedValueException. | |
567 * If this is a URI with no corresponding route. | |
568 */ | |
569 public function getRouteParameters() { | |
570 if ($this->unrouted) { | |
571 throw new \UnexpectedValueException('External URLs do not have internal route parameters.'); | |
572 } | |
573 | |
574 return $this->routeParameters; | |
575 } | |
576 | |
577 /** | |
578 * Sets the route parameters. | |
579 * | |
580 * @param array $parameters | |
581 * The array of parameters. | |
582 * | |
583 * @return $this | |
584 * | |
585 * @throws \UnexpectedValueException. | |
586 * If this is a URI with no corresponding route. | |
587 */ | |
588 public function setRouteParameters($parameters) { | |
589 if ($this->unrouted) { | |
590 throw new \UnexpectedValueException('External URLs do not have route parameters.'); | |
591 } | |
592 $this->routeParameters = $parameters; | |
593 return $this; | |
594 } | |
595 | |
596 /** | |
597 * Sets a specific route parameter. | |
598 * | |
599 * @param string $key | |
600 * The key of the route parameter. | |
601 * @param mixed $value | |
602 * The route parameter. | |
603 * | |
604 * @return $this | |
605 * | |
606 * @throws \UnexpectedValueException. | |
607 * If this is a URI with no corresponding route. | |
608 */ | |
609 public function setRouteParameter($key, $value) { | |
610 if ($this->unrouted) { | |
611 throw new \UnexpectedValueException('External URLs do not have route parameters.'); | |
612 } | |
613 $this->routeParameters[$key] = $value; | |
614 return $this; | |
615 } | |
616 | |
617 /** | |
618 * Returns the URL options. | |
619 * | |
620 * @return array | |
621 * The array of options. See \Drupal\Core\Url::fromUri() for details on what | |
622 * it contains. | |
623 */ | |
624 public function getOptions() { | |
625 return $this->options; | |
626 } | |
627 | |
628 /** | |
629 * Gets a specific option. | |
630 * | |
631 * See \Drupal\Core\Url::fromUri() for details on the options. | |
632 * | |
633 * @param string $name | |
634 * The name of the option. | |
635 * | |
636 * @return mixed | |
637 * The value for a specific option, or NULL if it does not exist. | |
638 */ | |
639 public function getOption($name) { | |
640 if (!isset($this->options[$name])) { | |
641 return NULL; | |
642 } | |
643 | |
644 return $this->options[$name]; | |
645 } | |
646 | |
647 /** | |
648 * Sets the URL options. | |
649 * | |
650 * @param array $options | |
651 * The array of options. See \Drupal\Core\Url::fromUri() for details on what | |
652 * it contains. | |
653 * | |
654 * @return $this | |
655 */ | |
656 public function setOptions($options) { | |
657 $this->options = $options; | |
658 return $this; | |
659 } | |
660 | |
661 /** | |
662 * Sets a specific option. | |
663 * | |
664 * See \Drupal\Core\Url::fromUri() for details on the options. | |
665 * | |
666 * @param string $name | |
667 * The name of the option. | |
668 * @param mixed $value | |
669 * The option value. | |
670 * | |
671 * @return $this | |
672 */ | |
673 public function setOption($name, $value) { | |
674 $this->options[$name] = $value; | |
675 return $this; | |
676 } | |
677 | |
678 /** | |
679 * Merges the URL options with any currently set. | |
680 * | |
681 * In the case of conflict with existing options, the new options will replace | |
682 * the existing options. | |
683 * | |
684 * @param array $options | |
685 * The array of options. See \Drupal\Core\Url::fromUri() for details on what | |
686 * it contains. | |
687 * | |
688 * @return $this | |
689 */ | |
690 public function mergeOptions($options) { | |
691 $this->options = NestedArray::mergeDeep($this->options, $options); | |
692 return $this; | |
693 } | |
694 | |
695 /** | |
696 * Returns the URI value for this Url object. | |
697 * | |
698 * Only to be used if self::$unrouted is TRUE. | |
699 * | |
700 * @return string | |
701 * A URI not connected to a route. May be an external URL. | |
702 * | |
703 * @throws \UnexpectedValueException | |
704 * Thrown when the URI was requested for a routed URL. | |
705 */ | |
706 public function getUri() { | |
707 if (!$this->unrouted) { | |
708 throw new \UnexpectedValueException('This URL has a Drupal route, so the canonical form is not a URI.'); | |
709 } | |
710 | |
711 return $this->uri; | |
712 } | |
713 | |
714 /** | |
715 * Sets the value of the absolute option for this Url. | |
716 * | |
717 * @param bool $absolute | |
718 * (optional) Whether to make this Url absolute or not. Defaults to TRUE. | |
719 * | |
720 * @return $this | |
721 */ | |
722 public function setAbsolute($absolute = TRUE) { | |
723 $this->options['absolute'] = $absolute; | |
724 return $this; | |
725 } | |
726 | |
727 /** | |
728 * Generates the string URL representation for this Url object. | |
729 * | |
730 * For an external URL, the string will contain the input plus any query | |
731 * string or fragment specified by the options array. | |
732 * | |
733 * If this Url object was constructed from a Drupal route or from an internal | |
734 * URI (URIs using the internal:, base:, or entity: schemes), the returned | |
735 * string will either be a relative URL like /node/1 or an absolute URL like | |
736 * http://example.com/node/1 depending on the options array, plus any | |
737 * specified query string or fragment. | |
738 * | |
739 * @param bool $collect_bubbleable_metadata | |
740 * (optional) Defaults to FALSE. When TRUE, both the generated URL and its | |
741 * associated bubbleable metadata are returned. | |
742 * | |
743 * @return string|\Drupal\Core\GeneratedUrl | |
744 * A string URL. | |
745 * When $collect_bubbleable_metadata is TRUE, a GeneratedUrl object is | |
746 * returned, containing the generated URL plus bubbleable metadata. | |
747 */ | |
748 public function toString($collect_bubbleable_metadata = FALSE) { | |
749 if ($this->unrouted) { | |
750 return $this->unroutedUrlAssembler()->assemble($this->getUri(), $this->getOptions(), $collect_bubbleable_metadata); | |
751 } | |
752 | |
753 return $this->urlGenerator()->generateFromRoute($this->getRouteName(), $this->getRouteParameters(), $this->getOptions(), $collect_bubbleable_metadata); | |
754 } | |
755 | |
756 /** | |
757 * Returns the route information for a render array. | |
758 * | |
759 * @return array | |
760 * An associative array suitable for a render array. | |
761 */ | |
762 public function toRenderArray() { | |
763 $render_array = [ | |
764 '#url' => $this, | |
765 '#options' => $this->getOptions(), | |
766 ]; | |
767 if (!$this->unrouted) { | |
768 $render_array['#access_callback'] = [get_class(), 'renderAccess']; | |
769 } | |
770 return $render_array; | |
771 } | |
772 | |
773 /** | |
774 * Returns the internal path (system path) for this route. | |
775 * | |
776 * This path will not include any prefixes, fragments, or query strings. | |
777 * | |
778 * @return string | |
779 * The internal path for this route. | |
780 * | |
781 * @throws \UnexpectedValueException. | |
782 * If this is a URI with no corresponding system path. | |
783 */ | |
784 public function getInternalPath() { | |
785 if ($this->unrouted) { | |
786 throw new \UnexpectedValueException('Unrouted URIs do not have internal representations.'); | |
787 } | |
788 | |
789 if (!isset($this->internalPath)) { | |
790 $this->internalPath = $this->urlGenerator()->getPathFromRoute($this->getRouteName(), $this->getRouteParameters()); | |
791 } | |
792 return $this->internalPath; | |
793 } | |
794 | |
795 /** | |
796 * Checks this Url object against applicable access check services. | |
797 * | |
798 * Determines whether the route is accessible or not. | |
799 * | |
800 * @param \Drupal\Core\Session\AccountInterface $account | |
801 * (optional) Run access checks for this account. Defaults to the current | |
802 * user. | |
803 * | |
804 * @return bool | |
805 * Returns TRUE if the user has access to the url, otherwise FALSE. | |
806 */ | |
807 public function access(AccountInterface $account = NULL) { | |
808 if ($this->isRouted()) { | |
809 return $this->accessManager()->checkNamedRoute($this->getRouteName(), $this->getRouteParameters(), $account); | |
810 } | |
811 return TRUE; | |
812 } | |
813 | |
814 /** | |
815 * Checks a Url render element against applicable access check services. | |
816 * | |
817 * @param array $element | |
818 * A render element as returned from \Drupal\Core\Url::toRenderArray(). | |
819 * | |
820 * @return bool | |
821 * Returns TRUE if the current user has access to the url, otherwise FALSE. | |
822 */ | |
823 public static function renderAccess(array $element) { | |
824 return $element['#url']->access(); | |
825 } | |
826 | |
827 /** | |
828 * @return \Drupal\Core\Access\AccessManagerInterface | |
829 */ | |
830 protected function accessManager() { | |
831 if (!isset($this->accessManager)) { | |
832 $this->accessManager = \Drupal::service('access_manager'); | |
833 } | |
834 return $this->accessManager; | |
835 } | |
836 | |
837 /** | |
838 * Gets the URL generator. | |
839 * | |
840 * @return \Drupal\Core\Routing\UrlGeneratorInterface | |
841 * The URL generator. | |
842 */ | |
843 protected function urlGenerator() { | |
844 if (!$this->urlGenerator) { | |
845 $this->urlGenerator = \Drupal::urlGenerator(); | |
846 } | |
847 return $this->urlGenerator; | |
848 } | |
849 | |
850 /** | |
851 * Gets the unrouted URL assembler for non-Drupal URLs. | |
852 * | |
853 * @return \Drupal\Core\Utility\UnroutedUrlAssemblerInterface | |
854 * The unrouted URL assembler. | |
855 */ | |
856 protected function unroutedUrlAssembler() { | |
857 if (!$this->urlAssembler) { | |
858 $this->urlAssembler = \Drupal::service('unrouted_url_assembler'); | |
859 } | |
860 return $this->urlAssembler; | |
861 } | |
862 | |
863 /** | |
864 * Sets the URL generator. | |
865 * | |
866 * @param \Drupal\Core\Routing\UrlGeneratorInterface $url_generator | |
867 * (optional) The URL generator, specify NULL to reset it. | |
868 * | |
869 * @return $this | |
870 */ | |
871 public function setUrlGenerator(UrlGeneratorInterface $url_generator = NULL) { | |
872 $this->urlGenerator = $url_generator; | |
873 $this->internalPath = NULL; | |
874 return $this; | |
875 } | |
876 | |
877 /** | |
878 * Sets the unrouted URL assembler. | |
879 * | |
880 * @param \Drupal\Core\Utility\UnroutedUrlAssemblerInterface $url_assembler | |
881 * The unrouted URL assembler. | |
882 * | |
883 * @return $this | |
884 */ | |
885 public function setUnroutedUrlAssembler(UnroutedUrlAssemblerInterface $url_assembler) { | |
886 $this->urlAssembler = $url_assembler; | |
887 return $this; | |
888 } | |
889 | |
890 } |