1 <?php
2 /**
3 * @package Joomla.Platform
4 * @subpackage Uri
5 *
6 * @copyright Copyright (C) 2005 - 2017 Open Source Matters, Inc. All rights reserved.
7 * @license GNU General Public License version 2 or later; see LICENSE
8 */
9
10 defined('JPATH_PLATFORM') or die;
11
12 use Joomla\Uri\Uri;
13
14 /**
15 * JUri Class
16 *
17 * This class serves two purposes. First it parses a URI and provides a common interface
18 * for the Joomla Platform to access and manipulate a URI. Second it obtains the URI of
19 * the current executing script from the server regardless of server.
20 *
21 * @since 11.1
22 */
23 class JUri extends Uri
24 {
25 /**
26 * @var JUri[] An array of JUri instances.
27 * @since 11.1
28 */
29 protected static $instances = array();
30
31 /**
32 * @var array The current calculated base url segments.
33 * @since 11.1
34 */
35 protected static $base = array();
36
37 /**
38 * @var array The current calculated root url segments.
39 * @since 11.1
40 */
41 protected static $root = array();
42
43 /**
44 * @var string The current url.
45 * @since 11.1
46 */
47 protected static $current;
48
49 /**
50 * Returns the global JUri object, only creating it if it doesn't already exist.
51 *
52 * @param string $uri The URI to parse. [optional: if null uses script URI]
53 *
54 * @return JUri The URI object.
55 *
56 * @since 11.1
57 */
58 public static function getInstance($uri = 'SERVER')
59 {
60 if (empty(static::$instances[$uri]))
61 {
62 // Are we obtaining the URI from the server?
63 if ($uri == 'SERVER')
64 {
65 // Determine if the request was over SSL (HTTPS).
66 if (isset($_SERVER['HTTPS']) && !empty($_SERVER['HTTPS']) && (strtolower($_SERVER['HTTPS']) != 'off'))
67 {
68 $https = 's://';
69 }
70 elseif ((isset($_SERVER['HTTP_X_FORWARDED_PROTO']) &&
71 !empty($_SERVER['HTTP_X_FORWARDED_PROTO']) &&
72 (strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) !== 'http')))
73 {
74 $https = 's://';
75 }
76 else
77 {
78 $https = '://';
79 }
80
81 /*
82 * Since we are assigning the URI from the server variables, we first need
83 * to determine if we are running on apache or IIS. If PHP_SELF and REQUEST_URI
84 * are present, we will assume we are running on apache.
85 */
86
87 if (!empty($_SERVER['PHP_SELF']) && !empty($_SERVER['REQUEST_URI']))
88 {
89 // To build the entire URI we need to prepend the protocol, and the http host
90 // to the URI string.
91 $theURI = 'http' . $https . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
92 }
93 else
94 {
95 /*
96 * Since we do not have REQUEST_URI to work with, we will assume we are
97 * running on IIS and will therefore need to work some magic with the SCRIPT_NAME and
98 * QUERY_STRING environment variables.
99 *
100 * IIS uses the SCRIPT_NAME variable instead of a REQUEST_URI variable... thanks, MS
101 */
102 $theURI = 'http' . $https . $_SERVER['HTTP_HOST'] . $_SERVER['SCRIPT_NAME'];
103
104 // If the query string exists append it to the URI string
105 if (isset($_SERVER['QUERY_STRING']) && !empty($_SERVER['QUERY_STRING']))
106 {
107 $theURI .= '?' . $_SERVER['QUERY_STRING'];
108 }
109 }
110
111 // Extra cleanup to remove invalid chars in the URL to prevent injections through the Host header
112 $theURI = str_replace(array("'", '"', '<', '>'), array('%27', '%22', '%3C', '%3E'), $theURI);
113 }
114 else
115 {
116 // We were given a URI
117 $theURI = $uri;
118 }
119
120 static::$instances[$uri] = new static($theURI);
121 }
122
123 return static::$instances[$uri];
124 }
125
126 /**
127 * Returns the base URI for the request.
128 *
129 * @param boolean $pathonly If false, prepend the scheme, host and port information. Default is false.
130 *
131 * @return string The base URI string
132 *
133 * @since 11.1
134 */
135 public static function base($pathonly = false)
136 {
137 // Get the base request path.
138 if (empty(static::$base))
139 {
140 $config = JFactory::getConfig();
141 $uri = static::getInstance();
142 $live_site = ($uri->isSsl()) ? str_replace('http://', 'https://', $config->get('live_site')) : $config->get('live_site');
143
144 if (trim($live_site) != '')
145 {
146 $uri = static::getInstance($live_site);
147 static::$base['prefix'] = $uri->toString(array('scheme', 'host', 'port'));
148 static::$base['path'] = rtrim($uri->toString(array('path')), '/\\');
149
150 if (defined('JPATH_BASE') && defined('JPATH_ADMINISTRATOR'))
151 {
152 if (JPATH_BASE == JPATH_ADMINISTRATOR)
153 {
154 static::$base['path'] .= '/administrator';
155 }
156 }
157 }
158 else
159 {
160 static::$base['prefix'] = $uri->toString(array('scheme', 'host', 'port'));
161
162 if (strpos(php_sapi_name(), 'cgi') !== false && !ini_get('cgi.fix_pathinfo') && !empty($_SERVER['REQUEST_URI']))
163 {
164 // PHP-CGI on Apache with "cgi.fix_pathinfo = 0"
165
166 // We shouldn't have user-supplied PATH_INFO in PHP_SELF in this case
167 // because PHP will not work with PATH_INFO at all.
168 $script_name = $_SERVER['PHP_SELF'];
169 }
170 else
171 {
172 // Others
173 $script_name = $_SERVER['SCRIPT_NAME'];
174 }
175
176 static::$base['path'] = rtrim(dirname($script_name), '/\\');
177 }
178 }
179
180 return $pathonly === false ? static::$base['prefix'] . static::$base['path'] . '/' : static::$base['path'];
181 }
182
183 /**
184 * Returns the root URI for the request.
185 *
186 * @param boolean $pathonly If false, prepend the scheme, host and port information. Default is false.
187 * @param string $path The path
188 *
189 * @return string The root URI string.
190 *
191 * @since 11.1
192 */
193 public static function root($pathonly = false, $path = null)
194 {
195 // Get the scheme
196 if (empty(static::$root))
197 {
198 $uri = static::getInstance(static::base());
199 static::$root['prefix'] = $uri->toString(array('scheme', 'host', 'port'));
200 static::$root['path'] = rtrim($uri->toString(array('path')), '/\\');
201 }
202
203 // Get the scheme
204 if (isset($path))
205 {
206 static::$root['path'] = $path;
207 }
208
209 return $pathonly === false ? static::$root['prefix'] . static::$root['path'] . '/' : static::$root['path'];
210 }
211
212 /**
213 * Returns the URL for the request, minus the query.
214 *
215 * @return string
216 *
217 * @since 11.1
218 */
219 public static function current()
220 {
221 // Get the current URL.
222 if (empty(static::$current))
223 {
224 $uri = static::getInstance();
225 static::$current = $uri->toString(array('scheme', 'host', 'port', 'path'));
226 }
227
228 return static::$current;
229 }
230
231 /**
232 * Method to reset class static members for testing and other various issues.
233 *
234 * @return void
235 *
236 * @since 11.1
237 */
238 public static function reset()
239 {
240 static::$instances = array();
241 static::$base = array();
242 static::$root = array();
243 static::$current = '';
244 }
245
246 /**
247 * Set the URI path string. Note we keep this method here so it uses the old _cleanPath function
248 *
249 * @param string $path The URI path string.
250 *
251 * @return void
252 *
253 * @since 11.1
254 * @deprecated 4.0 Use {@link \Joomla\Uri\Uri::setPath()}
255 * @note Present to proxy calls to the deprecated {@link JUri::_cleanPath()} method.
256 */
257 public function setPath($path)
258 {
259 $this->path = $this->_cleanPath($path);
260 }
261
262 /**
263 * Checks if the supplied URL is internal
264 *
265 * @param string $url The URL to check.
266 *
267 * @return boolean True if Internal.
268 *
269 * @since 11.1
270 */
271 public static function isInternal($url)
272 {
273 $uri = static::getInstance($url);
274 $base = $uri->toString(array('scheme', 'host', 'port', 'path'));
275 $host = $uri->toString(array('scheme', 'host', 'port'));
276
277 // @see JUriTest
278 if (empty($host) && strpos($uri->path, 'index.php') === 0
279 || !empty($host) && preg_match('#' . preg_quote(static::base(), '#') . '#', $base)
280 || !empty($host) && $host === static::getInstance(static::base())->host && strpos($uri->path, 'index.php') !== false
281 || !empty($host) && $base === $host && preg_match('#' . preg_quote($base, '#') . '#', static::base()))
282 {
283 return true;
284 }
285
286 return false;
287 }
288
289 /**
290 * Build a query from an array (reverse of the PHP parse_str()).
291 *
292 * @param array $params The array of key => value pairs to return as a query string.
293 *
294 * @return string The resulting query string.
295 *
296 * @see parse_str()
297 * @since 11.1
298 * @note The parent method is protected, this exposes it as public for B/C
299 */
300 public static function buildQuery(array $params)
301 {
302 return parent::buildQuery($params);
303 }
304
305 /**
306 * Parse a given URI and populate the class fields.
307 *
308 * @param string $uri The URI string to parse.
309 *
310 * @return boolean True on success.
311 *
312 * @since 11.1
313 * @note The parent method is protected, this exposes it as public for B/C
314 */
315 public function parse($uri)
316 {
317 return parent::parse($uri);
318 }
319
320 /**
321 * Resolves //, ../ and ./ from a path and returns
322 * the result. Eg:
323 *
324 * /foo/bar/../boo.php => /foo/boo.php
325 * /foo/bar/../../boo.php => /boo.php
326 * /foo/bar/.././/boo.php => /foo/boo.php
327 *
328 * @param string $path The URI path to clean.
329 *
330 * @return string Cleaned and resolved URI path.
331 *
332 * @since 11.1
333 * @deprecated 4.0 Use {@link \Joomla\Uri\Uri::cleanPath()} instead
334 */
335 protected function _cleanPath($path)
336 {
337 return parent::cleanPath($path);
338 }
339 }
340