80 if (is_string($uri)) {
81 $uri = $this->parseURI($uri,
false);
84 if (!is_array($uri)) {
85 throw new \InvalidArgumentException(
'Invalid URI');
88 if (!isset($uri[
'userinfo']) && isset($uri[
'user'])) {
89 $uri[
'userinfo'] = $uri[
'user'];
90 if (isset($uri[
'pass'])) {
91 $uri[
'userinfo'] .=
':'.$uri[
'pass'];
105 foreach ($components as $component) {
106 $tmp = strtolower($component);
107 $setter =
'set'.$component;
108 if (isset($uri[$tmp])) {
109 $this->$setter($uri[$tmp]);
111 $this->$setter(null);
142 $pos = strpos($uri,
':');
145 throw new \InvalidArgumentException(
'No scheme found');
148 $result[
'scheme'] = substr($uri, 0, $pos);
149 $uri = (string) substr($uri, $pos + 1);
153 $pos = strpos($uri,
'#');
154 if ($pos !==
false) {
155 $result[
'fragment'] = (string) substr($uri, $pos + 1);
156 $uri = (string) substr($uri, 0, $pos);
160 $pos = strpos($uri,
'?');
161 if ($pos !==
false) {
162 $result[
'query'] = (string) substr($uri, $pos + 1);
163 $uri = (string) substr($uri, 0, $pos);
168 $result[
'path'] =
'';
173 if (substr($uri, 0, 2) ==
'//') {
175 $uri = (string) substr($uri, 2);
178 $result[
'path'] =
'';
179 $pos = strpos($uri,
'/');
180 if ($pos !==
false) {
181 $result[
'path'] = substr($uri, $pos);
182 $uri = (string) substr($uri, 0, $pos);
186 $pos = strpos($uri,
'@');
187 if ($pos !==
false) {
188 $result[
'userinfo'] = (string) substr($uri, 0, $pos);
189 $uri = (string) substr($uri, $pos + 1);
193 $rpos = strcspn(strrev($uri),
':]');
195 if ($rpos != 0 && $rpos < $len && $uri[$len - $rpos - 1] !=
"]") {
196 $result[
'port'] = (string) substr($uri, -1 * $rpos);
197 $uri = (string) substr($uri, 0, -1 * $rpos - 1);
200 $result[
'host'] = $uri;
205 $result[
'path'] = $uri;
225 return preg_replace_callback(
226 '/%([[:xdigit:]]{2})/',
227 array(
'self',
'normalizePercentReal'),
246 $unreserved =
'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.
247 'abcdefghijklmnopqrstuvwxyz'.
250 $chr = chr(hexdec($hexchr[1]));
251 if (strpos($unreserved, $chr) !==
false) {
254 return '%' . strtoupper($hexchr[1]);
257 public function toURI($raw =
false, $credentials =
true)
266 if ($this->scheme !== null) {
267 $result .= $this->getScheme($raw).
':';
270 if ($this->host !== null) {
272 if ($this->userinfo !== null && $credentials) {
273 $result .= $this->getUserInfo($raw).
"@";
276 $result .= $this->getHost($raw);
277 $port = $this->getPort($raw);
278 if ($port !== null) {
279 $result .=
':'.$port;
283 $result .= $this->getPath($raw);
285 if ($this->query !== null) {
286 $result .=
'?'.$this->getQuery($raw);
289 if ($this->fragment !== null) {
290 $result .=
'#'.$this->getFragment($raw);
298 return $this->toURI();
306 return $this->scheme;
308 return strtolower($this->scheme);
314 if (!preg_match(
'/^[-[:alpha:][:alnum:]\\+\\.]*$/Di', $scheme)) {
315 throw new \InvalidArgumentException(
'Invalid scheme');
317 $this->scheme = $scheme;
323 return $this->userinfo;
325 return ($this->userinfo === null)
327 : $this->normalizePercent($this->userinfo);
340 '[-[:alnum:]\\._~!\\$&\'\\(\\)\\*\\+,;=:]|'.
343 if ($userinfo !== null && !preg_match(
'/^'.$pattern.
'$/Di', $userinfo)) {
344 throw new \InvalidArgumentException(
'Invalid user information');
346 $this->userinfo = $userinfo;
356 return ($this->host !== null)
357 ? strtolower($this->normalizePercent($this->host))
392 $decOctet =
'(?:\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])';
393 $ipv4Address = $decOctet.
'(?:\\.'.$decOctet.
'){3}';
394 $h16 =
'[[:xdigit:]]{1,4}';
395 $ls32 =
'(?:'.$h16.
':'.$h16.
'|'.$ipv4Address.
')';
398 '(?:'.$h16.
':){6}'.$ls32.
'|'.
399 '::(?:'.$h16.
':){5}'.$ls32.
'|'.
400 '(?:'.$h16.
')?::(?:'.$h16.
':){4}'.$ls32.
'|'.
401 '(?:(?:'.$h16.
':)?'.$h16.
')?::(?:'.$h16.
':){3}'.$ls32.
'|'.
402 '(?:(?:'.$h16.
':){0,2}'.$h16.
')?::(?:'.$h16.
':){2}'.$ls32.
'|'.
403 '(?:(?:'.$h16.
':){0,3}'.$h16.
')?::'.$h16.
':'.$ls32.
'|'.
404 '(?:(?:'.$h16.
':){0,4}'.$h16.
')?::'.$ls32.
'|'.
405 '(?:(?:'.$h16.
':){0,5}'.$h16.
')?::'.$h16.
'|'.
406 '(?:(?:'.$h16.
':){0,6}'.$h16.
')?::'.
408 $ipFuture =
'v[[:xdigit:]]+\\.'.
409 '[-[:alnum:]\\._~!\\$&\'\\(\\)*\\+,;=]+';
410 $unreserved =
'-[:alnum:]\\._~';
411 $subDelims =
'!\\$&\'\\(\\)*\\+,;=';
412 $pctEncoded =
'%[[:xdigit:]]{2}';
413 $zoneID =
"(?:[$unreserved]|$pctEncoded)+";
414 $ipv6Addrz =
"$ipv6Address%25$zoneID";
415 $regName =
"(?:[$unreserved$subDelims]|$pctEncoded)*";
416 $ipLiteral =
'\\[(?:'.$ipv6Addrz.
'|'.$ipv6Address.
'|'.$ipFuture.
')\\]';
417 $pattern =
'(?:'.$ipLiteral.
'|'.$ipv4Address.
'|'.$regName.
')';
418 if ($host !== null && !preg_match(
'/^'.$pattern.
'$/Di', $host)) {
419 throw new \InvalidArgumentException(
'Invalid host');
431 if ($this->port ==
'') {
435 $port = (int) $this->port;
438 $tcp = getservbyname($this->scheme,
'tcp');
439 $udp = getservbyname($this->scheme,
'udp');
441 if ($tcp == $port && ($udp ===
false || $udp == $tcp)) {
445 if ($udp == $port && ($tcp ===
false || $udp == $tcp)) {
456 $port = (string) $port;
458 if ($port !== null && strspn($port,
'0123456789') != strlen($port)) {
459 throw new \InvalidArgumentException(
'Invalid port');
476 if ($path === null) {
477 throw new \InvalidArgumentException(
'Path not set');
484 while ($input !=
'') {
485 if (substr($input, 0, 3) ==
'../') {
486 $input = (string) substr($input, 3);
487 } elseif (substr($input, 0, 2) ==
'./') {
488 $input = (string) substr($input, 2);
489 } elseif (substr($input, 0, 3) ==
'/./') {
490 $input = substr($input, 2);
491 } elseif ($input ==
'/.') {
493 } elseif (substr($input, 0, 4) ==
'/../') {
494 $input = substr($input, 3);
495 $pos = strrpos($output,
'/');
496 if ($pos ===
false) {
499 $output = substr($output, 0, $pos);
501 } elseif ($input ==
'/..') {
503 $pos = strrpos($output,
'/');
504 if ($pos ===
false) {
507 $output = substr($output, 0, $pos);
509 } elseif ($input ==
'.' || $input ==
'..') {
512 $pos = strpos($input,
'/', 1);
513 if ($pos ===
false) {
517 $output .= substr($input, 0, $pos);
518 $input = substr($input, $pos);
542 if ($this->host !== null && $this->path ==
'') {
546 $pos = strrpos($this->path,
'/');
547 if ($pos ===
false) {
550 return substr($this->path, 0, $pos + 1).$path;
560 return $this->removeDotSegments(
561 $this->normalizePercent($this->path)
605 '[-[:alnum:]\\._~!\\$&\'\\(\\)\\*\\+,;=:@]|'.
608 $segment =
'(?:'.$pchar.
'*)';
609 $segmentNz =
'(?:'.$pchar.
'+)';
610 $segmentNzNc =
'(?:'.
611 '[-[:alnum:]\\._~!\\$&\'\\(\\)\\*\\+,;=@]|'.
614 $pathAbempty =
'(?:/'.$segment.
')*';
615 $pathAbsolute =
'/(?:'.$segmentNz.
'(?:/'.$segment.
')*)?';
616 $pathNoscheme = $segmentNzNc.
'(?:/'.$segment.
')*';
617 $pathRootless = $segmentNz.
'(?:/'.$segment.
')*';
618 $pathEmpty =
'(?!'.$pchar.
')';
620 $pattern =
'(?:' . $pathAbempty.
'|'.$pathAbsolute;
622 $pattern .=
'|'.$pathNoscheme;
624 $pattern .=
'|'.$pathRootless;
626 $pattern .=
'|'.$pathEmpty .
')';
628 return (
bool) preg_match(
'#^'.$pattern.
'$#Di', $path);
646 if (!is_string($path) || !$this->validatePath($path, $relative)) {
647 throw new \InvalidArgumentException(
648 'Invalid path; use relative() for relative paths' 656 $this->realSetPath($path,
false);
664 return $this->normalizePercent($this->query);
678 '[-[:alnum:]\\._~!\\$&\'\\(\\)\\*\\+,;=/\\?]|'.
681 if ($query !== null && !preg_match(
'#^'.$pattern.
'$#Di', $query)) {
682 throw new \InvalidArgumentException(
'Invalid query');
684 $this->query = $query;
690 return $this->fragment;
692 return $this->normalizePercent($this->fragment);
706 '[-[:alnum:]\\._~!\\$&\'\\(\\)\\*\\+,;=/\\?]|'.
709 if ($fragment !== null && !preg_match(
'#^'.$pattern.
'$#Di', $fragment)) {
710 throw new \InvalidArgumentException(
'Invalid fragment');
712 $this->fragment = $fragment;
722 foreach (array(
'scheme',
'host',
'port') as $field) {
723 if ($this->$field !== null) {
724 $result[$field] = $this->$field;
729 if (isset($result[
'port'])) {
730 if (strspn($result[
'port'],
'0123456789') != strlen($result[
'port'])) {
731 unset($result[
'port']);
733 $result[
'port'] = (int) $result[
'port'];
737 if ($this->userinfo !== null) {
738 $pos = strpos($this->userinfo,
':');
739 if ($pos !==
false) {
740 $result[
'user'] = (string) substr($this->userinfo, 0, $pos);
741 $result[
'pass'] = (string) substr($this->userinfo, $pos + 1);
743 $result[
'user'] = $this->userinfo;
749 foreach (array(
'path',
'query',
'fragment') as $field) {
750 if ($this->$field !== null) {
751 $result[$field] = $this->$field;
756 if (!isset($result[
'host']) || $result[
'host'] ===
'' ||
757 !isset($result[
'scheme']) || $result[
'scheme'] ===
'') {
762 $fields = array(
'scheme',
'host',
'port',
'user',
'pass',
'path',
'query',
'fragment');
763 switch ($component) {
774 case PHP_URL_FRAGMENT:
775 return isset($result[$fields[$component]])
776 ? $result[$fields[$component]]
788 $result =
new $cls($reference);
790 }
catch (\InvalidArgumentException $e) {
796 $result = clone $this;
800 $parsed = $this->parseURI($reference,
true);
807 $result->setFragment(
808 isset($parsed[
'fragment'])
809 ? $parsed[
'fragment']
815 if (isset($parsed[
'host'])) {
816 $result->setHost(isset($parsed[
'host']) ? $parsed[
'host'] : null);
817 $result->setPort(isset($parsed[
'port']) ? $parsed[
'port'] : null);
818 $result->setUserInfo(
819 isset($parsed[
'userinfo'])
820 ? $parsed[
'userinfo']
823 $result->realSetPath($parsed[
'path'],
true);
825 isset($parsed[
'query'])
835 if ($parsed[
'path'] ==
'') {
836 if (isset($parsed[
'query'])) {
837 $result->setQuery($parsed[
'query']);
842 if (substr($parsed[
'path'], 0, 1) ==
'/') {
843 $result->realSetPath(
844 $result->removeDotSegments($parsed[
'path']),
848 $result->realSetPath(
849 $result->removeDotSegments($result->merge($parsed[
'path'])),
853 $result->setQuery(isset($parsed[
'query']) ? $parsed[
'query'] : null);
859 if (!strncasecmp(PHP_OS,
"Win", 3)) {
860 $isUnc = (substr($abspath, 0, 2) ==
'\\\\');
862 $abspath = ltrim($abspath,
'\\');
864 $parts = explode(
'\\', $abspath);
866 if ($isUnc && $parts[0] ==
'?') {
869 if (strpos($parts[0],
':') !==
false) {
871 $path = implode(
'\\', $parts);
872 } elseif ($parts[0] !=
'UNC') {
873 throw new \InvalidArgumentException(
'Invalid UNC path');
875 array_shift($parts[0]);
876 $host = array_shift($parts[0]);
877 $path = implode(
'\\', $parts);
881 $host = array_shift($parts[0]);
882 $path = implode(
'\\', $parts);
886 $path = implode(
'\\', $parts);
890 $path = str_replace(
'/',
'\\', $path);
892 $path = str_replace(
'/',
'%2F', $path);
893 $path = str_replace(
'\\',
'/', $path);
894 $path = ltrim($path,
'/');
898 if (DIRECTORY_SEPARATOR !=
'/') {
900 $abspath = str_replace(
'/', DIRECTORY_SEPARATOR, $abspath);
902 $path = str_replace(
'/',
'%2F', $path);
903 $path = str_replace(DIRECTORY_SEPARATOR,
'/', $path);
905 $path = ltrim($abspath,
'/');
908 $host = strtolower(self::normalizePercent($host));
910 $url =
'file://' . ($host ==
'localhost' ?
'' : $host) .
'/' . $path;
911 return new $cls($url);
Interface for a Uniform Resource Identifier parser/generator compatible with RFC 3986.
$fragment
Fragment component.
toURI($raw=false, $credentials=true)
parseURI($uri, $relative)
validatePath($path, $relative)
asParsedURL($component=-1)
Simple parser/generator for Uniform Resource Identifiers as defined in RFC 3986.
static fromAbsPath($abspath, $strict=true)
$userinfo
User information component (such as a "username:password" pair).
static normalizePercentReal($hexchr)
__construct($uri=array())
static normalizePercent($data)
$scheme
Scheme component (sometimes also erroneously called a "protocol").
realSetPath($path, $relative)
$host
Host component ("authority", even though "authority" is more general).