Use Glen Scotts UrlNormalizer to normalize the passed url.

@param  string $url
@return string
971777
public static function normalizeUrl($url) {
        if (!class_exists('URLNormalizer')) {
            require_once(realpath(__DIR__.'/../../classes').'/URLNormalizer.php'); 
        }

        $un = new \URLNormalizer();
        $un->setUrl($url);
        return $un->normalize();
    }
624098
public function getNormalizedUrl() {
		$parts = parse_url( $this->url );

		$scheme = isset( $parts['scheme'] ) ? $parts['scheme'] : 'http';
		$port = isset( $parts['port'] ) ?
			$parts['port'] : ( $scheme === 'https' ? '443' : '80' );
		$host = isset( $parts['host'] ) ? strtolower( $parts['host'] ) : '';
		$path = isset( $parts['path'] ) ? $parts['path'] : '';

		if ( ( $scheme === 'https' && $port != '443' ) ||
			( $scheme === 'http' && $port != '80' )
		) {
			$host = "{$host}:{$port}";
		}
		return "{$scheme}://{$host}{$path}";
	}
1048845
public static function url2source($url) {

        $mappingRules = array();
        foreach ( self::$urlMappingRules as $target => $source ) {
            $mappingRules['/' . $target . '/iU'] = $source;
        }
        return preg_replace(array_keys($mappingRules), $mappingRules, $url);

    }
514654
protected static function parseUrl($url)
    {
        $url_parts = Utils::multibyteParseUrl($url);

        if (isset($url_parts['scheme'])) {
            /** @var UniformResourceLocator $locator */
            $locator = Grav::instance()['locator'];

            // Special handling for the streams.
            if ($locator->schemeExists($url_parts['scheme'])) {
                if (isset($url_parts['host'])) {
                    // Merge host and path into a path.
                    $url_parts['path'] = $url_parts['host'] . (isset($url_parts['path']) ? '/' . $url_parts['path'] : '');
                    unset($url_parts['host']);
                }

                $url_parts['stream'] = true;
            }
        }

        return $url_parts;
    }
598117
public function urlencode(string $url): string
    {
        $replacements = [
            '%' => '%25',
            '&' => '%26',
            '<' => '%3C',
            '>' => '%3E',
            '|' => '%7C',
        ];

        return str_replace(array_keys($replacements), array_values($replacements), $url);
    }
311705
public function sanitize($url)
    {
        try {
            $url = Router::url($url);
        } catch (\Exception $ex) {
            return '';
        }

        if (!str_starts_with($url, '/')) {
            return $url;
        }

        if (str_starts_with($url, $this->baseUrl())) {
            $url = str_replace_once($this->baseUrl(), '', $url);
        }

        return $this->localePrefix($url);
    }
273147
public static function normalize($url)
    {
        $u = parse_url(preg_replace('#^file:/+([a-z]+:)#i', '$1', $url));

        if ($u === false) {
            return $url;
        }

        if (empty($u['scheme'])) {
            $u = null;
            return false;
        }

        $scheme = strtolower($u['scheme']);

        if (strlen($scheme) > 1) {
            $normalized = $scheme . '://';
        } else {
            $normalized = strtoupper($scheme) . ':';
        }

        if (isset($u['user'])) {
            $normalized .= $u['user'];
            $normalized .= isset($u['pass']) ? (':' . $u['pass']) : '';
            $normalized .= '@';
        }

        if (isset($u['host'])) {
            if (in_array($scheme, static::$defaultSchemes)) {
                $host = urldecode($u['host']);
                $normalized .= mb_strtolower($host, mb_detect_encoding($host));
            } else {
                $normalized .= $u['host'];
            }
        }

        if (isset($u['port']) && !in_array($scheme . ':' . $u['port'], static::$defaultPorts)) {
            $normalized .= ':' . $u['port'];
        }

        if (empty($u['path']) || $u['path'] === '/') {
            $normalized .= '/';
        } else {
            $normalized .= '/' . ltrim(self::canonpath($u['path']), '/');
        }

        if (isset($u['query'])) {
            $normalized .= self::canonquery($u['query'], '?');
        }

        if (isset($u['fragment'])) {
            $normalized .= '#' . $u['fragment'];
        }

        $u = null;
        return $normalized;
    }
142174
private function parseUrl($url)
	{
		$newUrl = preg_replace($this->patterns, $this->replacements, $url);
		$newUrl = trim($newUrl, '\/?');
		$newUrl = trim($newUrl, '\/');

		return $newUrl;
	}
1349670
private function transform($url) {
        $domains = App::acfg('domains@default');
        if (isset($domains[ VISITING_HOST ]) && $domains[ VISITING_HOST ]) {
            $dir = App::id2dir($domains[ VISITING_HOST ]);
            if ($url == 'index.html') {
                $url = $dir;
            } else {
                $url = $dir . '/' . $url;
            }
        }

        return $url;
    }
502252
protected function parseUrl($url = "")
    {
        $parsed = ($url) ? parse_url($url) : parse_url($this->_env['REQUEST_URI']);
        
        if (preg_match('/[a-zA-Z0-9_]+\.php/i', $parsed['path'], $matches)) {
            $parsed['path'] = preg_replace("/$matches[0]/", '/', $parsed['path']);
        }
        
        return $parsed;
    }
90346
private static function parse($url)
    {
        $enc_url = preg_replace_callback(
            '%[^:/@?&=#]+%usD',
            function ($matches) {
                return rawurlencode($matches[0]);
            },
            $url
        );

        $parts = parse_url($enc_url);

        if ($parts === false) {
            throw new \InvalidArgumentException('Malformed URL: ' . $url);
        }

        if (!empty($parts['scheme']) && !in_array($parts['scheme'], ['http', 'https', 'data'])) {
            throw new \InvalidArgumentException(sprintf('Invalid URL scheme: "%s"', $parts['scheme']));
        }

        foreach ($parts as $name => $value) {
            $parts[$name] = rawurldecode($value);
        }
        return $parts;
    }
662180
public static function sanitizeUrl($url)
    {
        $url = trim($url);

        if (!$url) {
            return null;
        }

        if (!preg_match("/^https?:\/\//i", $url)) {
            $url = "http://" . $url;
        }

        return self::sanitizeString($url);
    }
1009622
protected function _normalizedUrl($uri)
    {
        $out = $uri->getScheme() . '://';
        $out .= strtolower($uri->getHost());
        $out .= $uri->getPath();

        return $out;
    }
286853
public static function getUrlWithProtocol($url) {
		if(empty($url)) {
			return $url;
		}
		list($scheme, $host) = self::splitUrl($url);
		if(empty($scheme) || empty($host)) {
			return '';
		}
		return $scheme.'://'.$host;
	}
1073468
private function getUrlParametersSeparator($url)
    {
        /** @var \OxidEsales\Eshop\Core\StrRegular $oStr */
        $oStr = getStr();

        $urlSeparator = '&amp;';
        if ($oStr->preg_match('/(\?|&(amp;)?)$/i', $url)) {
            $urlSeparator = '';
        } elseif ($oStr->strpos($url, '?') === false) {
            $urlSeparator = '?';
        }

        return $urlSeparator;
    }
182470
public static function urlType($url) {
        $parsed = parse_url($url);
        if (isset($parsed['host'])) {
            $hostname = $parsed['host'];
        } else {
            $hostname = $url;
        }
        if (strpos($hostname, 'youtube') > 0) {
            return 'youtube';
        } elseif (strpos($hostname, 'vimeo') > 0) {
            return 'vimeo';
        } else {
            return 'unknown';
        }
    }
1339114
public function getNormalizedHttpUrl()
    {
        $parts = parse_url($this->httpUrl);

        $port = @$parts['port'];
        $scheme = $parts['scheme'];
        $host = $parts['host'];
        $path = @$parts['path'];

        $port or $port = ($scheme == 'https') ? '443' : '80';

        if (($scheme == 'https' && $port != '443')
            || ($scheme == 'http' && $port != '80')
        ) {
            $host = "$host:$port";
        }
        return "$scheme://$host$path";
    }