![]() Server : Apache System : Linux server2.corals.io 4.18.0-348.2.1.el8_5.x86_64 #1 SMP Mon Nov 15 09:17:08 EST 2021 x86_64 User : corals ( 1002) PHP Version : 7.4.33 Disable Function : exec,passthru,shell_exec,system Directory : /home/corals/mautic.corals.io/app/bundles/CoreBundle/Helper/ |
<?php namespace Mautic\CoreBundle\Helper; class UrlHelper { /** * Append query string to URL. * * @param string $url * @param string $appendQueryString * * @return string */ public static function appendQueryToUrl($url, $appendQueryString) { $query = parse_url($url, PHP_URL_QUERY); if ($query) { $appendQueryString = '&'.$appendQueryString; } else { $appendQueryString = '?'.$appendQueryString; } $anchorParts = explode('#', $url); // join url without anchor + $appendQueryString $url = $anchorParts[0].$appendQueryString; // prevent & or ? twice $url = str_replace(['&&', '??'], ['&', '?'], $url); // anchor if (isset($anchorParts[1])) { $url = sprintf('%s#%s', $url, $anchorParts[1]); } return $url; } /** * @return string */ public static function rel2abs($rel) { $path = $host = $scheme = ''; $ssl = !empty($_SERVER['HTTPS']) && 'on' == $_SERVER['HTTPS']; $scheme = strtolower($_SERVER['SERVER_PROTOCOL']); $scheme = substr($scheme, 0, strpos($scheme, '/')).($ssl ? 's' : ''); $port = $_SERVER['SERVER_PORT']; $port = ((!$ssl && '80' == $port) || ($ssl && '443' == $port)) ? '' : ":$port"; $host = $_SERVER['HTTP_HOST'] ?? null; $host ??= $_SERVER['SERVER_NAME'].$port; $base = "$scheme://$host".$_SERVER['REQUEST_URI']; $base = str_replace('/index.php', '', $base); /* return if already absolute URL */ if ('' != parse_url($rel, PHP_URL_SCHEME)) { return $rel; } /* queries and anchors */ if ('#' == $rel[0] || '?' == $rel[0]) { return $base.$rel; } /* after parse base URL and convert to local variables: $scheme, $host, $path */ $urlPartsArray = parse_url($base); // We should have a valid URL by this point. If not, just return the original value if (false === $urlPartsArray) { return $rel; } extract($urlPartsArray); /* remove non-directory element from path */ $path = preg_replace('#/[^/]*$#', '', $path); /* destroy path if relative url points to root */ if ('/' == $rel[0]) { $path = ''; } /* dirty absolute URL // with port number if exists */ if ('' != parse_url($base, PHP_URL_PORT)) { $abs = "$host:".parse_url($base, PHP_URL_PORT)."$path/$rel"; } else { $abs = "$host$path/$rel"; } /* replace '//' or '/./' or '/foo/../' with '/' */ $re = ['#(/\.?/)#', '#/(?!\.\.)[^/]+/\.\./#']; for ($n = 1; $n > 0;) { $abs = preg_replace($re, '/', $abs, -1, $n); } /* absolute URL is ready! */ return $scheme.'://'.$abs; } /** * Takes a plaintext, finds all URLs in it and return the array of those URLs. * With exception of URLs used as a token default values. * * @param string $text */ public static function getUrlsFromPlaintext($text, array $contactUrlFields = []): array { $urls = []; // Check if there are any tokens that URL based fields foreach ($contactUrlFields as $field) { if (str_contains($text, "{contactfield=$field}")) { $urls[] = "{contactfield=$field}"; } } $regex = '_(?:(?:https?|ftp)://)(?:\S+(?::\S*)?@)?(?:(?!10(?:\.\d{1,3}){3})(?!127(?:\.\d{1,3}){3})(?!169\.254(?:\.\d{1,3}){2})(?!192\.168(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\x{00a1}-\x{ffff}0-9]+-?)*[a-z\x{00a1}-\x{ffff}0-9]+)(?:\.(?:[a-z\x{00a1}-\x{ffff}0-9]+-?)*[a-z\x{00a1}-\x{ffff}0-9]+)*(?:\.(?:[a-z\x{00a1}-\x{ffff}]{2,})))(?::\d{2,5})?(?:/[^\s]*)?_ius'; if (!preg_match_all($regex, $text, $matches)) { return $urls; } $urls = array_merge($urls, $matches[0]); foreach ($urls as $key => $url) { // Remove dangling punctuation $urls[$key] = $url = self::removeTrailingNonAlphaNumeric($url); // We don't want to match URLs in token default values // like {contactfield=website|http://ignore.this.url} if (preg_match_all("/{(.*?)\|".preg_quote($url, '/').'}/', $text, $matches)) { unset($urls[$key]); // We know this is a URL due to the default so let's include it as a trackable foreach ($matches[1] as $tokenKey => $tokenContent) { $urls[] = $matches[0][$tokenKey]; } } } return $urls; } /** * Sanitize parts of the URL to make sure the URL query values are HTTP encoded. * * @param string $url * * @return string */ public static function sanitizeAbsoluteUrl($url) { if (!$url) { return $url; } $url = self::sanitizeUrlScheme($url); $url = self::sanitizeUrlPath($url); return self::sanitizeUrlQuery($url); } /** * Make sure the URL has a scheme. Defaults to HTTP if not provided. * * @param string $url * * @return string */ private static function sanitizeUrlScheme($url) { $isRelative = str_starts_with($url, '//'); if ($isRelative) { return $url; } $isMailto = str_starts_with($url, 'mailto:'); if ($isMailto) { return $url; } $containSlashes = str_contains($url, '://'); if (!$containSlashes) { $url = sprintf('://%s', $url); } $scheme = parse_url($url, PHP_URL_SCHEME); // Set default scheme to http if missing if (empty($scheme)) { $url = sprintf('http%s', $url); } return $url; } /** * @param string $url * * @return string */ private static function sanitizeUrlPath($url) { $path = parse_url($url, PHP_URL_PATH); if (!empty($path)) { $sanitizedPath = str_replace(' ', '%20', $path); $url = str_replace($path, $sanitizedPath, $url); } return $url; } /** * @param string $url * * @return string */ private static function sanitizeUrlQuery($url) { $query = parse_url($url, PHP_URL_QUERY); if (!empty($query)) { parse_str($query, $parsedQuery); if ($parsedQuery) { $encodedQuery = http_build_query($parsedQuery); $url = str_replace('?'.$query, '?'.$encodedQuery, $url); } } return $url; } /** * @param string $string * * @return string */ private static function removeTrailingNonAlphaNumeric($string) { // Special handling of closing bracket if (str_ends_with($string, '}') && preg_match('/^[^{\r\n]*\}.*?$/', $string)) { $string = substr($string, 0, -1); return self::removeTrailingNonAlphaNumeric($string); } // Ensure only alphanumeric allowed if (!preg_match("/^.*?[a-zA-Z0-9}\/]$/i", $string)) { $string = substr($string, 0, -1); return self::removeTrailingNonAlphaNumeric($string); } return $string; } /** * This method return true with special characters in URL, for example https://domain.tld/é.pdf * filter_var($url, FILTER_VALIDATE_URL) allow only alphanumerics [0-9a-zA-Z], the special characters "$-_.+!*'()," [not including the quotes - ed]. * * @param string $url */ public static function isValidUrl($url): bool { $path = parse_url($url, PHP_URL_PATH); $encodedPath = array_map('urlencode', explode('/', $path)); $url = str_replace($path, implode('/', $encodedPath), $url); return (bool) filter_var($url, FILTER_VALIDATE_URL); } /** * Decode & (HTML), & (decimal) and & (hex) ampersands. * This even works with double encoded ampersands. * * @param string $url * * @return string */ public static function decodeAmpersands($url) { while (str_contains($url, '&') || str_contains($url, '&') || str_contains($url, '&')) { $url = str_replace(['&', '&', '&'], '&', $url); } return $url; } /** * This method implements unicode slugs instead of transliteration. */ public static function stringURLUnicodeSlug(string $string): string { // Replace double byte whitespaces by single byte (East Asian languages) $str = preg_replace('/\xE3\x80\x80/', ' ', $string); // Remove any '-' from the string as they will be used as concatenator. // Would be great to let the spaces in but only Firefox is friendly with this $str = str_replace('-', ' ', $str); // Replace forbidden characters by whitespaces $str = preg_replace('#[:\#\*"@+=;!><&\.%()\]\/\'\\\\|\[]#', "\x20", $str); // Delete all '?' $str = str_replace('?', '', $str); // Trim white spaces at beginning and end of alias and make lowercase $str = trim(strtolower($str)); // Remove any duplicate whitespace and replace whitespaces by hyphens return preg_replace('#\x20+#', '-', $str); } }