123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303 |
- <?php
- /**
- * URL helper class.
- *
- * [!!] You need to setup the list of trusted hosts in the `url.php` config file, before starting using this helper class.
- *
- * @package KO7
- * @category Helpers
- *
- * @copyright (c) 2007-2016 Kohana Team
- * @copyright (c) since 2016 Koseven Team
- * @license https://koseven.dev/LICENSE
- */
- class KO7_URL {
- /**
- * Gets the base URL to the application.
- * To specify a protocol, provide the protocol as a string or request object.
- * If a protocol is used, a complete URL will be generated using the
- * `$_SERVER['HTTP_HOST']` variable, which will be validated against RFC 952
- * and RFC 2181, as well as against the list of trusted hosts you have set
- * in the `url.php` config file.
- *
- * // Absolute URL path with no host or protocol
- * echo URL::base();
- *
- * // Absolute URL path with host, https protocol and index.php if set
- * echo URL::base('https', TRUE);
- *
- * // Absolute URL path with host, https protocol and subdomain part
- * // prepended or replaced with given value
- * echo URL::base('https', FALSE, 'subdomain');
- *
- * // Absolute URL path with host and protocol from $request
- * echo URL::base($request);
- *
- * @param mixed $protocol Protocol string, [Request], or boolean
- * @param boolean $index Add index file to URL?
- * @param string $subdomain Subdomain string
- * @return string
- * @uses KO7::$index_file
- * @uses Request::protocol()
- */
- public static function base($protocol = NULL, $index = FALSE, $subdomain = NULL)
- {
- // Start with the configured base URL
- $base_url = KO7::$base_url;
- if ($protocol === TRUE)
- {
- // Use the initial request to get the protocol
- $protocol = Request::$initial;
- }
- if ($protocol instanceof Request)
- {
- if ( ! $protocol->secure())
- {
- // Use the current protocol
- list($protocol) = explode('/', strtolower($protocol->protocol()));
- }
- else
- {
- $protocol = 'https';
- }
- }
- if ( ! $protocol)
- {
- // Use the configured default protocol
- $protocol = parse_url($base_url, PHP_URL_SCHEME);
- }
- if ($index === TRUE AND ! empty(KO7::$index_file))
- {
- // Add the index file to the URL
- $base_url .= KO7::$index_file.'/';
- }
- if (is_string($protocol))
- {
- if ($port = parse_url($base_url, PHP_URL_PORT))
- {
- // Found a port, make it usable for the URL
- $port = ':'.$port;
- }
- if ($host = parse_url($base_url, PHP_URL_HOST))
- {
- // Remove everything but the path from the URL
- $base_url = parse_url($base_url, PHP_URL_PATH);
- }
- else
- {
- // Attempt to use HTTP_HOST and fallback to SERVER_NAME
- $host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : $_SERVER['SERVER_NAME'];
- }
- // If subdomain passed, then prepend to host or replace existing subdomain
- if (NULL !== $subdomain)
- {
- if (FALSE === strstr($host, '.'))
- {
- $host = $subdomain.'.'.$host;
- }
- else
- {
- // Get the domain part of host eg. example.com, then prepend subdomain
- $host = $subdomain.'.'.implode('.', array_slice(explode('.', $host), -2));
- }
- }
- // make $host lowercase
- $host = strtolower($host);
- // check that host does not contain forbidden characters (see RFC 952 and RFC 2181)
- // use preg_replace() instead of preg_match() to prevent DoS attacks with long host names
- if ($host && '' !== preg_replace('/(?:^\[)?[a-zA-Z0-9-:\]_]+\.?/', '', $host)) {
- throw new KO7_Exception(
- 'Invalid host :host',
- [':host' => $host]
- );
- }
- // Validate $host, see if it matches trusted hosts
- if ( ! static::is_trusted_host($host))
- {
- throw new KO7_Exception(
- 'Untrusted host :host. If you trust :host, add it to the trusted hosts in the `url` config file.',
- [':host' => $host]
- );
- }
- // Add the protocol and domain to the base URL
- $base_url = $protocol.'://'.$host.$port.$base_url;
- }
- return $base_url;
- }
- /**
- * Fetches an absolute site URL based on a URI segment.
- *
- * echo URL::site('foo/bar');
- *
- * @param string $uri Site URI to convert
- * @param mixed $protocol Protocol string or [Request] class to use protocol from
- * @param boolean $index Include the index_page in the URL
- * @param string $subdomain Subdomain string
- * @return string
- * @uses URL::base
- */
- public static function site($uri = '', $protocol = NULL, $index = TRUE, $subdomain = NULL)
- {
- // Chop off possible scheme, host, port, user and pass parts
- $path = preg_replace('~^[-a-z0-9+.]++://[^/]++/?~', '', trim($uri, '/'));
- if ( ! UTF8::is_ascii($path))
- {
- // Encode all non-ASCII characters, as per RFC 1738
- $path = preg_replace_callback('~([^/#]+)~', 'URL::_rawurlencode_callback', $path);
- }
- // Concat the URL
- return URL::base($protocol, $index, $subdomain).$path;
- }
- /**
- * Callback used for encoding all non-ASCII characters, as per RFC 1738
- * Used by URL::site()
- *
- * @param array $matches Array of matches from preg_replace_callback()
- * @return string Encoded string
- */
- protected static function _rawurlencode_callback($matches)
- {
- return rawurlencode($matches[0]);
- }
- /**
- * Merges the current GET parameters with an array of new or overloaded
- * parameters and returns the resulting query string.
- *
- * // Returns "?sort=title&limit=10" combined with any existing GET values
- * $query = URL::query(array('sort' => 'title', 'limit' => 10));
- *
- * Typically you would use this when you are sorting query results,
- * or something similar.
- *
- * [!!] Parameters with a NULL value are left out.
- *
- * @param array $params Array of GET parameters
- * @param boolean $use_get Include current request GET parameters
- * @return string
- */
- public static function query(array $params = NULL, $use_get = TRUE)
- {
- if ($use_get)
- {
- if ($params === NULL)
- {
- // Use only the current parameters
- $params = $_GET;
- }
- else
- {
- // Merge the current and new parameters
- $params = Arr::merge($_GET, $params);
- }
- }
- if (empty($params))
- {
- // No query parameters
- return '';
- }
- // Note: http_build_query returns an empty string for a params array with only NULL values
- $query = http_build_query($params, '', '&');
- // Don't prepend '?' to an empty string
- return ($query === '') ? '' : ('?'.$query);
- }
- /**
- * Convert a phrase to a URL-safe title.
- *
- * echo URL::title('My Blog Post'); // "my-blog-post"
- *
- * @param string $title Phrase to convert
- * @param string $separator Word separator (any single character)
- * @param boolean $ascii_only Transliterate to ASCII?
- * @return string
- * @uses UTF8::transliterate_to_ascii
- */
- public static function title($title, $separator = '-', $ascii_only = FALSE)
- {
- if ($ascii_only)
- {
- // Transliterate non-ASCII characters
- if (extension_loaded('intl'))
- {
- $title = transliterator_transliterate('Any-Latin;Latin-ASCII', $title);
- }
- else
- {
- $title = UTF8::transliterate_to_ascii($title);
- }
- // Remove all characters that are not the separator, a-z, 0-9, or whitespace
- $title = preg_replace('![^'.preg_quote($separator).'a-z0-9\s]+!', '', strtolower($title));
- }
- else
- {
- // Remove all characters that are not the separator, letters, numbers, or whitespace
- $title = preg_replace('![^'.preg_quote($separator).'\pL\pN\s]+!u', '', UTF8::strtolower($title));
- }
- // Replace all separator characters and whitespace by a single separator
- $title = preg_replace('!['.preg_quote($separator).'\s]+!u', $separator, $title);
- // Trim separators from the beginning and end
- return trim($title, $separator);
- }
- /**
- * Test if given $host should be trusted.
- *
- * Tests against given $trusted_hosts
- * or looks for key `trusted_hosts` in `url` config
- *
- * @param string $host
- * @param array $trusted_hosts
- * @return boolean TRUE if $host is trustworthy
- */
- public static function is_trusted_host($host, array $trusted_hosts = NULL)
- {
- // If list of trusted hosts is not directly provided read from config
- if (empty($trusted_hosts))
- {
- $trusted_hosts = (array) KO7::$config->load('url')->get('trusted_hosts');
- }
- // loop through the $trusted_hosts array for a match
- foreach ($trusted_hosts as $trusted_host)
- {
- // make sure we fully match the trusted hosts
- $pattern = '#^'.$trusted_host.'$#uD';
- // return TRUE if there is match
- if (preg_match($pattern, $host)) {
- return TRUE;
- }
- }
- // return FALSE as nothing is matched
- return FALSE;
- }
- }
|