4
* This file is part of the Nette Framework (http://nette.org)
6
* Copyright (c) 2004 David Grudl (http://davidgrudl.com)
8
* For the full copyright and license information, please view
9
* the file license.txt that was distributed with this source code.
18
* URI Syntax (RFC 3986).
21
* scheme user password host port basePath relativeUrl
23
* /--\ /--\ /------\ /-------\ /--\/--\/----------------------------\
24
* http://john:x0y17575@nette.org:8042/en/manual.php?name=param#fragment <-- absoluteUrl
25
* \__________________________/\____________/^\________/^\______/
27
* authority path query fragment
30
* - authority: [user[:password]@]host[:port]
31
* - hostUrl: http://user:password@nette.org:8042
32
* - basePath: /en/ (everything before relative URI not including the script name)
33
* - baseUrl: http://user:password@nette.org:8042/en/
34
* - relativeUrl: manual.php
38
* @property string $scheme
39
* @property string $user
40
* @property string $password
41
* @property string $host
42
* @property string $port
43
* @property string $path
44
* @property string $query
45
* @property string $fragment
46
* @property-read string $absoluteUrl
47
* @property-read string $authority
48
* @property-read string $hostUrl
49
* @property-read string $basePath
50
* @property-read string $baseUrl
51
* @property-read string $relativeUrl
53
class Url extends Nette\FreezableObject
56
public static $defaultPorts = array(
86
private $fragment = '';
91
* @throws Nette\InvalidArgumentException
93
public function __construct($url = NULL)
95
if (is_string($url)) {
96
$parts = @parse_url($url); // @ - is escalated to exception
97
if ($parts === FALSE) {
98
throw new Nette\InvalidArgumentException("Malformed or unsupported URI '$url'.");
101
foreach ($parts as $key => $val) {
105
if (!$this->port && isset(self::$defaultPorts[$this->scheme])) {
106
$this->port = self::$defaultPorts[$this->scheme];
109
if ($this->path === '' && ($this->scheme === 'http' || $this->scheme === 'https')) {
113
} elseif ($url instanceof self) {
114
foreach ($this as $key => $val) {
115
$this->$key = $url->$key;
122
* Sets the scheme part of URI.
126
public function setScheme($value)
129
$this->scheme = (string) $value;
135
* Returns the scheme part of URI.
138
public function getScheme()
140
return $this->scheme;
145
* Sets the user name part of URI.
149
public function setUser($value)
152
$this->user = (string) $value;
158
* Returns the user name part of URI.
161
public function getUser()
168
* Sets the password part of URI.
172
public function setPassword($value)
175
$this->pass = (string) $value;
181
* Returns the password part of URI.
184
public function getPassword()
191
* Sets the host part of URI.
195
public function setHost($value)
198
$this->host = (string) $value;
204
* Returns the host part of URI.
207
public function getHost()
214
* Sets the port part of URI.
218
public function setPort($value)
221
$this->port = (int) $value;
227
* Returns the port part of URI.
230
public function getPort()
237
* Sets the path part of URI.
241
public function setPath($value)
244
$this->path = (string) $value;
250
* Returns the path part of URI.
253
public function getPath()
260
* Sets the query part of URI.
261
* @param string|array
264
public function setQuery($value)
267
$this->query = (string) (is_array($value) ? http_build_query($value, '', '&') : $value);
273
* Appends the query part of URI.
274
* @param string|array
277
public function appendQuery($value)
280
$value = (string) (is_array($value) ? http_build_query($value, '', '&') : $value);
281
$this->query .= ($this->query === '' || $value === '') ? $value : '&' . $value;
286
* Returns the query part of URI.
289
public function getQuery()
296
* Sets the fragment part of URI.
300
public function setFragment($value)
303
$this->fragment = (string) $value;
309
* Returns the fragment part of URI.
312
public function getFragment()
314
return $this->fragment;
319
* Returns the entire URI including query string and fragment.
322
public function getAbsoluteUrl()
324
return $this->getHostUrl() . $this->path
325
. ($this->query === '' ? '' : '?' . $this->query)
326
. ($this->fragment === '' ? '' : '#' . $this->fragment);
331
* Returns the [user[:pass]@]host[:port] part of URI.
334
public function getAuthority()
336
$authority = $this->host;
337
if ($this->port && (!isset(self::$defaultPorts[$this->scheme]) || $this->port !== self::$defaultPorts[$this->scheme])) {
338
$authority .= ':' . $this->port;
341
if ($this->user !== '' && $this->scheme !== 'http' && $this->scheme !== 'https') {
342
$authority = $this->user . ($this->pass === '' ? '' : ':' . $this->pass) . '@' . $authority;
350
* Returns the scheme and authority part of URI.
353
public function getHostUrl()
355
return ($this->scheme ? $this->scheme . ':' : '') . '//' . $this->getAuthority();
360
* Returns the base-path.
363
public function getBasePath()
365
$pos = strrpos($this->path, '/');
366
return $pos === FALSE ? '' : substr($this->path, 0, $pos + 1);
371
* Returns the base-URI.
374
public function getBaseUrl()
376
return $this->getHostUrl() . $this->getBasePath();
381
* Returns the relative-URI.
384
public function getRelativeUrl()
386
return (string) substr($this->getAbsoluteUrl(), strlen($this->getBaseUrl()));
391
* URI comparsion (this object must be in canonical form).
395
public function isEqual($url)
397
// compare host + path
398
$part = self::unescape(strtok($url, '?#'), '%/');
399
if (strncmp($part, '//', 2) === 0) { // absolute URI without scheme
400
if ($part !== '//' . $this->getAuthority() . $this->path) {
404
} elseif (strncmp($part, '/', 1) === 0) { // absolute path
405
if ($part !== $this->path) {
410
if ($part !== $this->getHostUrl() . $this->path) {
415
// compare query strings
416
$part = preg_split('#[&;]#', self::unescape(strtr((string) strtok('?#'), '+', ' '), '%&;=+'));
418
$query = preg_split('#[&;]#', $this->query);
420
return $part === $query;
425
* Transform to canonical form.
428
public function canonicalize()
431
$this->path = $this->path === '' ? '/' : self::unescape($this->path, '%/');
432
$this->host = strtolower(rawurldecode($this->host));
433
$this->query = self::unescape(strtr($this->query, '+', ' '), '%&;=+');
440
public function __toString()
442
return $this->getAbsoluteUrl();
447
* Similar to rawurldecode, but preserve reserved chars encoded.
448
* @param string to decode
449
* @param string reserved characters
452
public static function unescape($s, $reserved = '%;/?:@&=+$,')
454
// reserved (@see RFC 2396) = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | ","
455
// within a path segment, the characters "/", ";", "=", "?" are reserved
456
// within a query component, the characters ";", "/", "?", ":", "@", "&", "=", "+", ",", "$" are reserved.
457
preg_match_all('#(?<=%)[a-f0-9][a-f0-9]#i', $s, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER);
458
foreach (array_reverse($matches) as $match) {
459
$ch = chr(hexdec($match[0][0]));
460
if (strpos($reserved, $ch) === FALSE) {
461
$s = substr_replace($s, $ch, $match[0][1] - 1, 3);
469
function getRelativeUri()
471
trigger_error(__METHOD__ . '() is deprecated; use ' . __CLASS__ . '::getRelativeUrl() instead.', E_USER_WARNING);
472
return $this->getRelativeUrl();
476
function getAbsoluteUri()
478
trigger_error(__METHOD__ . '() is deprecated; use ' . __CLASS__ . '::getAbsoluteUrl() instead.', E_USER_WARNING);
479
return $this->getAbsoluteUrl();
483
function getHostUri()
485
trigger_error(__METHOD__ . '() is deprecated; use ' . __CLASS__ . '::getHostUrl() instead.', E_USER_WARNING);
486
return $this->getHostUrl();
490
function getBaseUri()
492
trigger_error(__METHOD__ . '() is deprecated; use ' . __CLASS__ . '::getBaseUrl() instead.', E_USER_WARNING);
493
return $this->getBaseUrl();