-
Notifications
You must be signed in to change notification settings - Fork 327
/
URL.php
275 lines (243 loc) · 7.68 KB
/
URL.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
<?php defined('SYSPATH') OR die('No direct script access.');
/**
* 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 Kohana
* @category Helpers
* @author Kohana Team
* @copyright (c) 2007-2012 Kohana Team
* @license http://kohanaframework.org/license
*/
class Kohana_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 and protocol from $request
* echo URL::base($request);
*
* @param mixed $protocol Protocol string, [Request], or boolean
* @param boolean $index Add index file to URL?
* @return string
* @uses Kohana::$index_file
* @uses Request::protocol()
*/
public static function base($protocol = NULL, $index = FALSE)
{
// Start with the configured base URL
$base_url = Kohana::$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(Kohana::$index_file))
{
// Add the index file to the URL
$base_url .= Kohana::$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'];
// 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 Kohana_Exception(
'Invalid host :host',
array(':host' => $host)
);
}
// Validate $host, see if it matches trusted hosts
if ( ! static::is_trusted_host($host))
{
throw new Kohana_Exception(
'Untrusted host :host. If you trust :host, add it to the trusted hosts in the `url` config file.',
array(':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
* @return string
* @uses URL::base
*/
public static function site($uri = '', $protocol = NULL, $index = TRUE)
{
// 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).$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 === TRUE)
{
// Transliterate non-ASCII characters
$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) Kohana::$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;
}
}