1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163: 164: 165: 166: 167: 168: 169: 170: 171: 172: 173: 174: 175: 176: 177: 178: 179: 180: 181: 182: 183: 184: 185: 186: 187: 188: 189: 190: 191: 192: 193: 194: 195: 196: 197: 198: 199: 200: 201: 202: 203: 204: 205: 206: 207: 208: 209: 210: 211: 212: 213: 214: 215: 216: 217: 218: 219: 220: 221: 222: 223: 224: 225: 226: 227: 228: 229: 230: 231: 232: 233: 234: 235: 236: 237: 238: 239: 240: 241: 242: 243: 244: 245: 246: 247: 248: 249: 250: 251: 252: 253: 254: 255: 256: 257: 258: 259: 260: 261: 262: 263: 264: 265: 266: 267: 268: 269: 270: 271: 272: 273: 274: 275: 276: 277: 278: 279: 280: 281: 282: 283: 284: 285: 286: 287: 288: 289: 290: 291: 292: 293: 294: 295: 296: 297: 298: 299: 300: 301: 302: 303: 304: 305: 306: 307: 308: 309: 310: 311: 312: 313: 314: 315: 316: 317: 318: 319: 320: 321: 322: 323: 324: 325: 326: 327: 328: 329: 330: 331: 332: 333: 334: 335: 336: 337: 338: 339: 340: 341: 342: 343: 344: 345: 346: 347: 348: 349: 350: 351: 352: 353: 354: 355: 356: 357: 358:
<?php
namespace esperecyan\url;
use esperecyan\webidl\TypeHinter;
use esperecyan\webidl\TypeError;
/**
* The URL class represent an object providing static methods used for creating object URLs.
* @link https://url.spec.whatwg.org/#URL URL Standard
* @link https://developer.mozilla.org/docs/Web/API/URL URL - Web API Interfaces | MDN
* @property string $href Is a USVString containing the whole URL.
* @property-read string $origin
* Returns a USVString containing the canonical form of the origin of the specific location.
* @property string $protocol Is a USVString containing the protocol scheme of the URL, including the final ‘:’.
* @property string $username Is a USVString containing the username specified before the domain name.
* @property string $password Is a USVString containing the password specified before the domain name.
* @property string $host Is a USVString containing the host, that is the hostname,
* and then, if the port of the URL is not empty (which can happen because it was not specified
* or because it was specified to be the default port of the URL’s scheme), a ‘:’, and the port of the URL.
* @property string $hostname Is a USVString containing the domain of the URL.
* @property string $port Is a USVString containing the port number of the URL.
* @property string $pathname Is a USVString containing an initial ‘/’ followed by the path of the URL.
* @property string $search Is a USVString containing a ‘?’ followed by the parameters of the URL.
* @property-read URLSearchParams $searchParams
* Returns a URLSearchParams object allowing to access the GET query arguments contained in the URL.
* @property string $hash Is a USVString containing a ‘#’ followed by the fragment identifier of the URL.
*/
class URL implements \JsonSerializable
{
/**
* @var lib\URL An associated URL.
* @link https://url.spec.whatwg.org/#concept-url-url URL Standard
*/
private $url;
/**
* @var URLSearchParams An associated query object.
* @link https://url.spec.whatwg.org/#concept-url-query-object URL Standard
*/
private $queryObject;
/**
* The constructor returns a newly created URL object representing the URL defined by the parameters.
* @link https://url.spec.whatwg.org/#dom-URL-URL URL Standard
* @link https://developer.mozilla.org/docs/Web/API/URL/URL URL() - Web API Interfaces | MDN
* @param string $url Is a DOMString representing an absolute or relative URL.
* If $url is a relative URL, $base which is present, will be used as the base URL.
* If $url is an absolute URL, $base are ignored.
* @param string $base Is a DOMString representing the base URL to use in case $url is a relative URL.
* If not specified, and no $base is passed in parameters, it default to 'about:blank'.
* @throws TypeError If the given base URL or the resulting URL are not valid URLs, a TypeError is thrown.
*/
public function __construct($url, $base = null)
{
$parsedBase = null;
if (!is_null($base)) {
$parsedBase = lib\URL::parseBasicURL(TypeHinter::to('USVString', $base, 1));
if (!$parsedBase) {
throw new TypeError(sprintf('<%s> is not a valid URL', $base));
}
}
$this->url = lib\URL::parseBasicURL(TypeHinter::to('USVString', $url, 0), $parsedBase);
if ($this->url === false) {
throw new TypeError(sprintf('<%s> is not a valid URL', $url));
}
$this->queryObject = \Closure::bind(function ($query) {
return URLSearchParams::createNewURLSearchParamsObject(null, $query);
}, $this, 'esperecyan\url\URLSearchParams')->__invoke($this->url->query);
\Closure::bind(function ($queryObject) {
$queryObject->urlObject = $this;
}, $this, $this->queryObject)->__invoke($this->queryObject);
}
/**
* Converts domain name to IDNA ASCII form.
* @deprecated 5.0.0 URL::domainToASCII() has been removed from the URL Standard specification.
* @see \esperecyan\url\lib\HostProcessing::domainToASCII()
* @link https://github.com/whatwg/url/commit/2bd0f59b98024921ab90e628b7a526cca5abcb5f
* Remove URL.domainToASCII and domainToUnicode · whatwg/url@2bd0f59y
* @param string $domain
* @return string Returns an empty string if $domain is an IPv6 address or an invalid domain.
*/
public static function domainToASCII($domain)
{
$asciiDomain = lib\HostProcessing::parseHost(TypeHinter::to('USVString', $domain), true);
return is_string($asciiDomain) ? $asciiDomain : '';
}
/**
* Converts domain name from IDNA ASCII to Unicode.
* @deprecated 5.0.0 URL::domainToUnicode() has been removed from the URL Standard specification.
* @see \esperecyan\url\lib\HostProcessing::domainToUnicode()
* @link https://github.com/whatwg/url/commit/2bd0f59b98024921ab90e628b7a526cca5abcb5f
* Remove URL.domainToASCII and domainToUnicode · whatwg/url@2bd0f59y
* @param string $domain
* @return string Returns an empty string if $domain is an IPv6 address or an invalid domain.
*/
public static function domainToUnicode($domain)
{
$asciiDomain = lib\HostProcessing::parseHost(TypeHinter::to('USVString', $domain), true);
return is_string($asciiDomain) ? lib\HostProcessing::domainToUnicode($asciiDomain) : '';
}
/**
* @param string $name
* @param string $value
* @throws TypeError
*/
public function __set($name, $value)
{
if (in_array(
$name,
['href', 'protocol', 'username', 'password', 'host', 'hostname', 'port', 'pathname', 'search', 'hash']
)) {
$input = TypeHinter::to('USVString', $value);
}
switch ($name) {
case 'href':
$parsedURL = lib\URL::parseBasicURL($input);
if (!$parsedURL) {
throw new TypeError(sprintf('<%s> is not a valid URL', $input));
}
$this->url = $parsedURL;
\Closure::bind(function ($list, $queryObject) {
array_splice($queryObject->list, 0, count($queryObject->list), $list);
}, null, $this->queryObject)
->__invoke(lib\URLencoding::parseURLencodedString($this->url->query), $this->queryObject);
break;
case 'protocol':
lib\URL::parseBasicURL($input . ':', null, null, [
'url' => $this->url,
'state override' => 'scheme start state'
]);
break;
case 'username':
if (!$this->url->cannotHaveUsernamePasswordPort()) {
$this->url->setUsername($input);
}
break;
case 'password':
if (!$this->url->cannotHaveUsernamePasswordPort()) {
$this->url->setPassword($input);
}
break;
case 'host':
if (!$this->url->cannotBeABaseURLFlag) {
lib\URL::parseBasicURL($input, null, null, [
'url' => $this->url,
'state override' => 'host state'
]);
}
break;
case 'hostname':
if (!$this->url->cannotBeABaseURLFlag) {
lib\URL::parseBasicURL($input, null, null, [
'url' => $this->url,
'state override' => 'hostname state'
]);
}
break;
case 'port':
if (!$this->url->cannotHaveUsernamePasswordPort()) {
if ($input === '') {
$this->url->port = null;
} else {
lib\URL::parseBasicURL($input, null, null, [
'url' => $this->url,
'state override' => 'port state'
]);
}
}
break;
case 'pathname':
if (!$this->url->cannotBeABaseURLFlag) {
$this->url->path = [];
lib\URL::parseBasicURL($input, null, null, [
'url' => $this->url,
'state override' => 'path start state'
]);
}
break;
case 'search':
if ($input === '') {
$this->url->query = null;
$list = [];
} else {
$query = $input[0] === '?' ? substr($input, 1) : $input;
$this->url->query = '';
lib\URL::parseBasicURL($query, null, null, [
'url' => $this->url,
'state override' => 'query state'
]);
$list = lib\URLencoding::parseURLencodedString($query);
}
\Closure::bind(function ($list, $queryObject) {
array_splice($queryObject->list, 0, count($queryObject->list), $list);
}, null, $this->queryObject)->__invoke($list, $this->queryObject);
break;
case 'hash':
if ($input === '') {
$this->url->fragment = null;
} else {
$fragment = $input[0] === '#' ? substr($input, 1) : $input;
$this->url->fragment = '';
lib\URL::parseBasicURL($fragment, null, null, [
'url' => $this->url,
'state override' => 'fragment state'
]);
}
break;
case 'origin':
case 'searchParams':
TypeHinter::throwReadonlyException();
break;
default:
TypeHinter::triggerVisibilityErrorOrDefineProperty();
}
}
/**
* @param string $name
* @return string|URLSearchParams
*/
public function __get($name)
{
switch ($name) {
case 'href':
$value = $this->url->serializeURL();
break;
case 'origin':
$value = self::serialiseOrigin($this->url->getOrigin());
break;
case 'protocol':
$value = $this->url->scheme . ':';
break;
case 'username':
$value = $this->url->username;
break;
case 'password':
$value = $this->url->password;
break;
case 'host':
$value = is_null($this->url->host)
? ''
: lib\HostProcessing::serializeHost($this->url->host)
. (is_null($this->url->port) ? '' : ':' . $this->url->port);
break;
case 'hostname':
$value = is_null($this->url->host) ? '' : lib\HostProcessing::serializeHost($this->url->host);
break;
case 'port':
$value = is_null($this->url->port) ? '' : (string)$this->url->port;
break;
case 'pathname':
if ($this->url->cannotBeABaseURLFlag) {
$value = $this->url->path[0];
} elseif (!$this->url->path) {
$value = '';
} else {
$value = '/' . implode('/', $this->url->path);
}
break;
case 'search':
$value = is_null($this->url->query) || $this->url->query === '' ? '' : '?' . $this->url->query;
break;
case 'searchParams':
$value = $this->queryObject;
break;
case 'hash':
$value = is_null($this->url->fragment) || $this->url->fragment === '' ? '' : '#' . $this->url->fragment;
break;
default:
TypeHinter::triggerVisibilityErrorOrUndefinedNotice();
$value = null;
}
return $value;
}
/**
* The serialization of an origin.
* @link https://html.spec.whatwg.org/multipage/browsers.html#ascii-serialisation-of-an-origin HTML Standard
* @param string[]|string $origin
* @return string
*/
private static function serialiseOrigin($origin)
{
if (!is_array($origin)) {
$result = 'null';
} else {
$result = $origin[0] . '://' . lib\HostProcessing::serializeHost($origin[1]);
if (isset(lib\URL::$specialSchemes[$origin[0]]) && $origin[2] !== lib\URL::$specialSchemes[$origin[0]]) {
$result .= ':' . $origin[2];
}
}
return $result;
}
/**
* Returns a USVString containing the whole URL. It is a synonym for URL::$href.
* @link https://url.spec.whatwg.org/#URL-stringification-behavior URL Standard
* @return string USVString.
*/
public function __toString()
{
return $this->__get('href');
}
/**
* Returns the href property value.
*
* The URL Standard defines the URL::toJSON() method as to return data which should be serialized to JSON
* for the JSON::stringify() method on ECMAScript.
* However, in PHP, a toJSON() method is often defined as to return JSON string,
* for example the Zend\Json\Json::encode() and the lluminate\Contracts\Support\Jsonable::toJson() method.
* @link https://url.spec.whatwg.org/#dom-url-tojson URL Standard
* @return string USVString.
*/
public function jsonSerialize()
{
return $this->__get('href');
}
/**
* @param string $name
* @return bool
*/
public function __isset($name)
{
return in_array($name, ['href', 'origin', 'protocol', 'username', 'password', 'host', 'hostname', 'port', 'pathname', 'search', 'searchParams', 'hash']);
}
}