Permalink
Browse files

added UrlImmutable

  • Loading branch information...
dg committed Feb 11, 2019
1 parent aaf78b9 commit 12b5f95cd18f460d7c6bbcfa739cd52105e4fddc
@@ -86,7 +86,7 @@ class Url implements \JsonSerializable
/**
* @param string|self $url
* @param string|self|UrlImmutable $url
* @throws Nette\InvalidArgumentException if URL is malformed
*/
public function __construct($url = null)
@@ -106,10 +106,11 @@ public function __construct($url = null)
$this->setQuery($p['query'] ?? []);
$this->fragment = isset($p['fragment']) ? rawurldecode($p['fragment']) : '';
} elseif ($url instanceof self) {
foreach ($this as $key => $val) {
$this->$key = $url->$key;
}
} elseif ($url instanceof UrlImmutable || $url instanceof self) {
[$this->scheme, $this->user, $this->password, $this->host, $this->port, $this->path, $this->query, $this->fragment] = $url->export();
} elseif ($url !== null) {
throw new Nette\InvalidArgumentException;
}
}
@@ -449,6 +450,13 @@ public function jsonSerialize(): string
}
/** @internal */
final public function export(): array
{
return [$this->scheme, $this->user, $this->password, $this->host, $this->port, $this->path, $this->query, $this->fragment];
}
/**
* Similar to rawurldecode, but preserves reserved chars encoded.
*/
@@ -0,0 +1,224 @@
<?php
/**
* This file is part of the Nette Framework (https://nette.org)
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
*/
declare(strict_types=1);
namespace Nette\Http;
use Nette;
/**
* Object representation of a URL.
*
* <pre>
* scheme user password host port path query fragment
* | | | | | | | |
* /--\ /--\ /------\ /-------\ /--\/------------\ /--------\ /------\
* http://john:x0y17575@nette.org:8042/en/manual.php?name=param#fragment <-- absoluteUrl
* \______\__________________________/
* | |
* hostUrl authority
* </pre>
*
* @property-read string $scheme
* @property-read string $user
* @property-read string $password
* @property-read string $host
* @property-read int $port
* @property-read string $path
* @property-read string $query
* @property-read string $fragment
* @property-read string $absoluteUrl
* @property-read string $authority
* @property-read string $hostUrl
* @property-read array $queryParameters
*/
class UrlImmutable implements \JsonSerializable
{
use Nette\SmartObject;
/** @var string */
private $scheme = '';
/** @var string */
private $user = '';
/** @var string */
private $password = '';
/** @var string */
private $host = '';
/** @var int|null */
private $port;
/** @var string */
private $path = '';
/** @var array */
private $query = [];
/** @var string */
private $fragment = '';
/**
* @param string|self|Url $url
* @throws Nette\InvalidArgumentException if URL is malformed
*/
public function __construct($url)
{
if (is_string($url)) {
$url = new Url($url); // TODO
}
if ($url instanceof Url || $url instanceof self) {
[$this->scheme, $this->user, $this->password, $this->host, $this->port, $this->path, $this->query, $this->fragment] = $url->export();
} elseif ($url !== null) {
throw new Nette\InvalidArgumentException;
}
}
public function getScheme(): string
{
return $this->scheme;
}
public function getUser(): string
{
return $this->user;
}
public function getPassword(): string
{
return $this->password;
}
public function getHost(): string
{
return $this->host;
}
public function getDomain(int $level = 2): string
{
$parts = ip2long($this->host) ? [$this->host] : explode('.', $this->host);
$parts = $level >= 0 ? array_slice($parts, -$level) : array_slice($parts, 0, $level);
return implode('.', $parts);
}
public function getPort(): ?int
{
return $this->port ?: (Url::$defaultPorts[$this->scheme] ?? null);
}
public function getPath(): string
{
return $this->path;
}
public function getQuery(): string
{
return http_build_query($this->query, '', '&', PHP_QUERY_RFC3986);
}
public function getQueryParameters(): array
{
return $this->query;
}
/**
* @return array|string|null
*/
public function getQueryParameter(string $name)
{
return $this->query[$name] ?? null;
}
public function getFragment(): string
{
return $this->fragment;
}
/**
* Returns the entire URI including query string and fragment.
*/
public function getAbsoluteUrl(): string
{
return $this->getHostUrl() . $this->path
. (($tmp = $this->getQuery()) ? '?' . $tmp : '')
. ($this->fragment === '' ? '' : '#' . $this->fragment);
}
/**
* Returns the [user[:pass]@]host[:port] part of URI.
*/
public function getAuthority(): string
{
return $this->host === ''
? ''
: ($this->user !== ''
? rawurlencode($this->user) . ($this->password === '' ? '' : ':' . rawurlencode($this->password)) . '@'
: '')
. $this->host
. ($this->port && (!isset(Url::$defaultPorts[$this->scheme]) || $this->port !== Url::$defaultPorts[$this->scheme])
? ':' . $this->port
: '');
}
/**
* Returns the scheme and authority part of URI.
*/
public function getHostUrl(): string
{
return ($this->scheme ? $this->scheme . ':' : '')
. (($authority = $this->getAuthority()) || $this->scheme ? '//' . $authority : '');
}
public function __toString(): string
{
return $this->getAbsoluteUrl();
}
/**
* @param string|self $url
*/
public function isEqual($url): bool
{
return (new Url($this))->isEqual($url);
}
public function jsonSerialize(): string
{
return $this->getAbsoluteUrl();
}
/** @internal */
final public function export(): array
{
return [$this->scheme, $this->user, $this->password, $this->host, $this->port, $this->path, $this->query, $this->fragment];
}
}
@@ -0,0 +1,34 @@
<?php
/**
* Test: Nette\Http\UrlImmutable http://
*/
declare(strict_types=1);
use Nette\Http\Url;
use Nette\Http\UrlImmutable;
use Tester\Assert;
require __DIR__ . '/../bootstrap.php';
$url = new UrlImmutable(new Url('http://username%3A:password%3A@hostn%61me:60/p%61th/script.php?%61rg=value#%61nchor'));
Assert::same('http://username%3A:password%3A@hostname:60/p%61th/script.php?arg=value#anchor', (string) $url);
Assert::same('"http:\/\/username%3A:password%3A@hostname:60\/p%61th\/script.php?arg=value#anchor"', json_encode($url));
Assert::same('http', $url->scheme);
Assert::same('username:', $url->user);
Assert::same('password:', $url->password);
Assert::same('hostname', $url->host);
Assert::same(60, $url->port);
Assert::same('hostname', $url->getDomain());
Assert::same('hostname', $url->getDomain(0));
Assert::same('', $url->getDomain(-1));
Assert::same('/p%61th/script.php', $url->path);
Assert::same('arg=value', $url->query);
Assert::same(['arg' => 'value'], $url->getQueryParameters());
Assert::same('anchor', $url->fragment);
Assert::same('username%3A:password%3A@hostname:60', $url->authority);
Assert::same('http://username%3A:password%3A@hostname:60', $url->hostUrl);
Assert::same('http://username%3A:password%3A@hostname:60/p%61th/script.php?arg=value#anchor', $url->absoluteUrl);
@@ -0,0 +1,49 @@
<?php
/**
* Test: Nette\Http\UrlImmutable::isEqual()
*/
declare(strict_types=1);
use Nette\Http\UrlImmutable;
use Tester\Assert;
require __DIR__ . '/../bootstrap.php';
$url = new UrlImmutable('http://exampl%65.COM/p%61th?text=foo%20bar+foo&value');
Assert::true($url->isEqual('http://example.com/path?text=foo+bar%20foo&value'));
Assert::true($url->isEqual('http://example.com/%70ath?value&text=foo+bar%20foo'));
Assert::false($url->isEqual('http://example.com/Path?text=foo+bar%20foo&value'));
Assert::false($url->isEqual('http://example.com/path?value&text=foo+bar%20foo#abc'));
Assert::false($url->isEqual('http://example.com/path?text=foo+bar%20foo'));
Assert::false($url->isEqual('https://example.com/path?text=foo+bar%20foo&value'));
Assert::false($url->isEqual('http://example.org/path?text=foo+bar%20foo&value'));
$url = new UrlImmutable('http://example.com');
Assert::true($url->isEqual('http://example.com/'));
Assert::true($url->isEqual('http://example.com'));
$url = new UrlImmutable('http://example.com/?arr[]=item1&arr[]=item2');
Assert::true($url->isEqual('http://example.com/?arr[0]=item1&arr[1]=item2'));
Assert::false($url->isEqual('http://example.com/?arr[1]=item1&arr[0]=item2'));
$url = new UrlImmutable('http://example.com/?a=9999&b=127.0.0.1&c=1234&d=123456789');
Assert::true($url->isEqual('http://example.com/?d=123456789&a=9999&b=127.0.0.1&c=1234'));
$url = new UrlImmutable('http://example.com/?a=123&b=456');
Assert::false($url->isEqual('http://example.com/?a=456&b=123'));
$url = new UrlImmutable('http://user:pass@example.com');
Assert::false($url->isEqual('http://example.com'));
$url = new UrlImmutable('ftp://user:pass@example.com');
Assert::false($url->isEqual('ftp://example.com'));
@@ -0,0 +1,18 @@
<?php
/**
* Test: Nette\Http\UrlImmutable malformed URI.
*/
declare(strict_types=1);
use Nette\Http\UrlImmutable;
use Tester\Assert;
require __DIR__ . '/../bootstrap.php';
Assert::exception(function () {
new UrlImmutable('http:///');
}, InvalidArgumentException::class, "Malformed or unsupported URI 'http:///'.");
Oops, something went wrong.

0 comments on commit 12b5f95

Please sign in to comment.