Browse Source

Also normalize relative URLs

microsub
J. King 5 years ago
parent
commit
0eb0fbcc0d
  1. 49
      lib/Misc/URL.php
  2. 12
      tests/cases/Misc/TestURL.php

49
lib/Misc/URL.php

@ -11,7 +11,7 @@ namespace JKingWeb\Arsse\Misc;
*/ */
class URL { class URL {
/** Normalizes an absolute URL /** Normalizes a URL
* *
* Normalizations performed are: * Normalizations performed are:
* *
@ -27,32 +27,35 @@ class URL {
* *
* It does NOT drop trailing slashes from paths, nor does it perform Unicode normalization or context-aware percent-encoding normalization * It does NOT drop trailing slashes from paths, nor does it perform Unicode normalization or context-aware percent-encoding normalization
* *
* @param string $url The URL to normalize. Relative URLs are returned unchanged * @param string $url The URL to normalize
* @param string $u Username to add to the URL, replacing any existing credentials * @param string $u Username to add to the URL, replacing any existing credentials
* @param string $p Password to add to the URL, if a username is specified * @param string $p Password to add to the URL, if a username is specified
*/ */
public static function normalize(string $url, string $u = null, string $p = null): string { public static function normalize(string $url, string $u = null, string $p = null): string {
extract(parse_url($url)); extract(parse_url($url));
if (!isset($scheme) || !isset($host) || !strlen($host)) { $out = "";
return $url; if (isset($scheme)) {
$out .= strtolower($scheme).":";
} }
$out = strtolower($scheme)."://"; if (isset($host)) {
if (strlen($u ?? "")) { $out .= "//";
$out .= self::normalizeEncoding(rawurlencode($u)); if (strlen($u ?? "")) {
if (strlen($p ?? "")) { $out .= self::normalizeEncoding(rawurlencode($u));
$out .= ":".self::normalizeEncoding(rawurlencode($p)); if (strlen($p ?? "")) {
} $out .= ":".self::normalizeEncoding(rawurlencode($p));
$out .= "@"; }
} elseif (strlen($user ?? "")) { $out .= "@";
$out .= self::normalizeEncoding($user); } elseif (strlen($user ?? "")) {
if (strlen($pass ?? "")) { $out .= self::normalizeEncoding($user);
$out .= ":".self::normalizeEncoding($pass); if (strlen($pass ?? "")) {
$out .= ":".self::normalizeEncoding($pass);
}
$out .= "@";
} }
$out .= "@"; $out .= self::normalizeHost($host);
$out .= isset($port) ? ":$port" : "";
} }
$out .= self::normalizeHost($host); $out .= self::normalizePath($path ?? "", isset($host));
$out .= isset($port) ? ":$port" : "";
$out .= self::normalizePath($path ?? "");
if (isset($query) && strlen($query)) { if (isset($query) && strlen($query)) {
$out .= "?".self::normalizeEncoding($query); $out .= "?".self::normalizeEncoding($query);
} }
@ -114,8 +117,10 @@ class URL {
} }
/** Normalizes the whole path segment to remove empty segments and relative segments */ /** Normalizes the whole path segment to remove empty segments and relative segments */
protected static function normalizePath(string $path): string { protected static function normalizePath(string $path, bool $hasHost): string {
$parts = explode("/", self::normalizeEncoding($path)); $parts = explode("/", self::normalizeEncoding($path));
$absolute = ($hasHost || $path[0] === "/");
$index = (substr($path, -1) === "/");
$out = []; $out = [];
foreach($parts as $p) { foreach($parts as $p) {
switch ($p) { switch ($p) {
@ -129,6 +134,8 @@ class URL {
$out[] = $p; $out[] = $p;
} }
} }
return str_replace("//", "/", "/".implode("/", $out).(substr($path, -1) === "/" ? "/" : "")); $out = implode("/", $out);
$out = ($absolute ? "/" : "").$out.($index ? "/" : "");
return str_replace("//", "/", $out);
} }
} }

12
tests/cases/Misc/TestURL.php

@ -21,10 +21,6 @@ class TestURL extends \JKingWeb\Arsse\Test\AbstractTest {
public function provideNormalizations() { public function provideNormalizations() {
return [ return [
["/", "/"],
["//example.com/", "//example.com/"],
["/ ", "/ "],
["//EXAMPLE.COM/", "//EXAMPLE.COM/"],
["http://example.com/", "http://example.com/"], ["http://example.com/", "http://example.com/"],
["HTTP://example.com/", "http://example.com/"], ["HTTP://example.com/", "http://example.com/"],
["http://example.com", "http://example.com/"], ["http://example.com", "http://example.com/"],
@ -46,6 +42,7 @@ class TestURL extends \JKingWeb\Arsse\Test\AbstractTest {
["http://user:pass@example.com/", "http://user:pass@example.com/", "", "p"], ["http://user:pass@example.com/", "http://user:pass@example.com/", "", "p"],
["http://example.com/", "http://example.com/", "", "p"], ["http://example.com/", "http://example.com/", "", "p"],
["http://example.com/path", "http://example.com/path"], ["http://example.com/path", "http://example.com/path"],
["http://example.com/PATH", "http://example.com/PATH"],
["http://example.com/path/", "http://example.com/path/"], ["http://example.com/path/", "http://example.com/path/"],
["http://example.com/path/.", "http://example.com/path"], ["http://example.com/path/.", "http://example.com/path"],
["http://example.com/path/./", "http://example.com/path/"], ["http://example.com/path/./", "http://example.com/path/"],
@ -69,6 +66,13 @@ class TestURL extends \JKingWeb\Arsse\Test\AbstractTest {
["http://example.com/%", "http://example.com/%25"], ["http://example.com/%", "http://example.com/%25"],
["http://example.com/%a", "http://example.com/%25a"], ["http://example.com/%a", "http://example.com/%25a"],
["http://example.com/%za", "http://example.com/%25za"], ["http://example.com/%za", "http://example.com/%25za"],
["//EXAMPLE.COM/", "//example.com/"],
["//EXAMPLE.COM/", "//u:p@example.com/", "u", "p"],
["/ ", "/%20"],
["/ ", "/%20", "u", "p"],
["EXAMPLE.COM/", "EXAMPLE.COM/"],
["EXAMPLE.COM", "EXAMPLE.COM"],
[" ", "%20"],
]; ];
} }
} }

Loading…
Cancel
Save