Page MenuHomeDevCentral

No OneTemporary

diff --git a/src/HTTP/URL.php b/src/HTTP/URL.php
index 67dc3f1..80a8992 100644
--- a/src/HTTP/URL.php
+++ b/src/HTTP/URL.php
@@ -1,191 +1,208 @@
<?php
declare(strict_types=1);
namespace Keruald\OmniTools\HTTP;
use Keruald\OmniTools\Strings\Multibyte\OmniString;
class URL {
///
/// Constants
///
/**
* Encode the query using RFC 3986, but keep / intact as a separators.
* As such, everything will be encoded excepted ~ - _ . / characters.
*/
const ENCODE_RFC3986_SLASH_EXCEPTED = 1;
/**
* Encode the query using RFC 3986, including the /.
* As such, everything will be encoded excepted ~ - _ . characters.
*/
const ENCODE_RFC3986_PURE = 2;
/**
* Consider the query already encoded.
*/
const ENCODE_AS_IS = 3;
///
/// Private members
///
/**
* @var string
*/
private $url;
/**
* @var int
*/
private $queryEncoding;
///
/// Constructors
///
public function __construct ($url,
$queryEncoding = self::ENCODE_RFC3986_SLASH_EXCEPTED) {
$this->url = $url;
$this->queryEncoding = $queryEncoding;
}
public static function compose (string $protocol, string $domain,
string $query,
$queryEncoding = self::ENCODE_RFC3986_SLASH_EXCEPTED
) : self {
return (new URL("", $queryEncoding))
->update($protocol, $domain, $query);
}
///
/// Getters and setters
///
public function getProtocol () : string {
if (preg_match("@(.*?)://.*@", $this->url, $matches)) {
return $matches[1];
}
return "";
}
+ private function getUrlParts() : array {
+ preg_match("@://(.*)@", $this->url, $matches);
+ return explode("/", $matches[1], 2);
+ }
+
public function getDomain () : string {
- if (preg_match("@://(.*?)/@", $this->url, $matches)) {
- return self::beautifyDomain($matches[1]);
+ if (strpos($this->url, "://") === false) {
+ return "";
}
- return "";
+ $domain = $this->getUrlParts()[0];
+
+ if ($domain === "") {
+ return "";
+ }
+
+ return self::beautifyDomain($domain);
}
public function getQuery () : string {
- if (preg_match("@(://.*?)?(/.*)@", $this->url, $matches)) {
- return $this->beautifyQuery($matches[2]);
+ if (strpos($this->url, "://") === false) {
+ return $this->url;
}
- return "";
+ $parts = $this->getUrlParts();
+
+ if (count($parts) < 2 || $parts[1] === "" || $parts[1] === "/") {
+ return "/";
+ }
+
+ return "/" . $this->beautifyQuery($parts[1]);
}
public function setProtocol ($protocol) : self {
$this->update($protocol, $this->getDomain(), $this->getQuery());
return $this;
}
public function setDomain ($domain) : self {
$this->update($this->getProtocol(), $domain, $this->getQuery());
return $this;
}
public function setQuery ($query,
$encodeMode = self::ENCODE_RFC3986_SLASH_EXCEPTED
) : self {
$this->queryEncoding = $encodeMode;
$this->update($this->getProtocol(), $this->getDomain(), $query);
return $this;
}
private function isRootQuery($query) : bool {
return $this->queryEncoding !== self::ENCODE_RFC3986_PURE
&& $query !== ""
&& $query[0] === '/';
}
private function update (string $protocol, string $domain, string $query) : self {
$url = "";
if ($domain !== "") {
if ($protocol !== "") {
$url = $protocol;
}
$url .= "://" . self::normalizeDomain($domain);
if (!$this->isRootQuery($query)) {
$url .= "/";
}
}
$url .= $this->normalizeQuery($query);
$this->url = $url;
return $this;
}
public function normalizeQuery (string $query) : string {
switch ($this->queryEncoding) {
case self::ENCODE_RFC3986_SLASH_EXCEPTED:
return (new OmniString($query))
->explode("/")
->map("rawurlencode")
->implode("/")
->__toString();
case self::ENCODE_AS_IS:
return $query;
case self::ENCODE_RFC3986_PURE:
return rawurlencode($query);
}
throw new \Exception('Unexpected encoding value');
}
public function beautifyQuery (string $query) : string {
switch ($this->queryEncoding) {
case self::ENCODE_RFC3986_SLASH_EXCEPTED:
return (new OmniString($query))
->explode("/")
->map("rawurldecode")
->implode("/")
->__toString();
case self::ENCODE_AS_IS:
return $query;
case self::ENCODE_RFC3986_PURE:
return rawurldecode($query);
}
throw new \Exception('Unexpected encoding value');
}
public static function normalizeDomain (string $domain) : string {
return idn_to_ascii($domain, 0, INTL_IDNA_VARIANT_UTS46);
}
public static function beautifyDomain (string $domain) : string {
return idn_to_utf8($domain, 0, INTL_IDNA_VARIANT_UTS46);
}
public function __toString () {
return $this->url;
}
}
diff --git a/tests/HTTP/URLTest.php b/tests/HTTP/URLTest.php
index 1c43e15..3b8802c 100644
--- a/tests/HTTP/URLTest.php
+++ b/tests/HTTP/URLTest.php
@@ -1,84 +1,90 @@
<?php
declare(strict_types=1);
namespace Keruald\OmniTools\Tests\HTTP;
use Keruald\OmniTools\HTTP\URL;
use PHPUnit\Framework\TestCase;
class URLTest extends TestCase {
/**
* @dataProvider provideURLsAndComponents
*/
public function testGetDomain ($url, $expectedDomain) : void {
$url = new URL($url);
$this->assertEquals($expectedDomain, $url->getDomain());
}
/**
* @dataProvider provideURLsAndComponents
*/
public function testGetProtocol ($url, $_, $expectedProtocol) : void {
$url = new URL($url);
$this->assertEquals($expectedProtocol, $url->getProtocol());
}
/**
* @dataProvider provideURLsAndComponents
*/
public function testGetQuery ($url, $_, $__, $expectedQuery) : void {
$url = new URL($url);
$this->assertEquals($expectedQuery, $url->getQuery());
}
public function testSetProtocol () : void {
$url = new URL("https://acme.tld/foo");
$url->setProtocol("xizzy");
$this->assertEquals("xizzy", $url->getProtocol());
}
public function testSetDomain () : void {
$url = new URL("https://acme.tld/foo");
$url->setDomain("xizzy");
$this->assertEquals("xizzy", $url->getDomain());
}
public function testSetQuery () : void {
$url = new URL("https://acme.tld/foo");
$url->setQuery("/xizzy");
$this->assertEquals("/xizzy", $url->getQuery());
}
public function testSetQueryWithSlashForgotten () : void {
$url = new URL("https://acme.tld/foo");
$url->setQuery("xizzy");
$this->assertEquals("/xizzy", $url->getQuery());
}
/**
* @dataProvider provideURLsAndComponents
*/
- public function testCompose ($expectedUrl, $domain, $protocol, $query) {
+ public function testCompose ($url, $domain, $protocol, $query,
+ $expectedUrl = null) {
$this->assertEquals(
- $expectedUrl,
+ $expectedUrl ?? $url,
URL::compose($protocol, $domain, $query)->__toString()
);
}
public function provideURLsAndComponents () : iterable {
+ // base URL, domain, protocol, query[, expected URL]
+ // When omitted, the expected URL is the base URL.
+
yield ["http://foo/bar", "foo", "http", "/bar"];
yield ["https://xn--dghrefn-mxa.nasqueron.org/", "dæghrefn.nasqueron.org", "https", "/"];
yield ["://foo/bar", "foo", "", "/bar"];
yield ["/bar", "", "", "/bar"];
yield ["http://foo/bar%20quux", "foo", "http", "/bar quux"];
+ yield ["https://foo/", "foo", "https", "/"];
+ yield ["https://foo", "foo", "https", "/", "https://foo/"];
}
}

File Metadata

Mime Type
text/x-diff
Expires
Thu, Sep 18, 11:17 (19 h, 31 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2990631
Default Alt Text
(8 KB)

Event Timeline