0x7E || $d == 0x25) { // these characters are always encoded $out .= "%".strtoupper(dechex($d)); } elseif (preg_match("/[a-zA-Z0-9\._~-]/", $dc)) { // these characters are never encoded $out .= $dc; } else { // these characters are passed through as-is if ($c === "%") { $out .= "%".strtoupper(dechex($d)); } else { $out .= $c; } } $pos++; } return $out; } /** Normalizes a hostname per IDNA:2008 */ protected static function normalizeHost(string $host): string { if ($host[0] === "[" && substr($host, -1) === "]") { // normalize IPv6 addresses $addr = @inet_pton(substr($host, 1, strlen($host) - 2)); if ($addr !== false) { return "[".inet_ntop($addr)."]"; } } $idn = idn_to_ascii($host, \IDNA_NONTRANSITIONAL_TO_ASCII, \INTL_IDNA_VARIANT_UTS46); return $idn !== false ? idn_to_utf8($idn, \IDNA_NONTRANSITIONAL_TO_UNICODE, \INTL_IDNA_VARIANT_UTS46) : $host; } /** Normalizes the whole path segment to remove empty segments and relative segments */ protected static function normalizePath(string $path): string { $parts = explode("/", self::normalizeEncoding($path)); $out = []; foreach($parts as $p) { switch ($p) { case "": case ".": break; case "..": array_pop($out); break; default: $out[] = $p; } } return str_replace("//", "/", "/".implode("/", $out).(substr($path, -1) === "/" ? "/" : "")); } }