1 <?php
2 /**
3 * Part of the Joomla Framework Uri Package
4 *
5 * @copyright Copyright (C) 2005 - 2016 Open Source Matters. All rights reserved.
6 * @license GNU General Public License version 2 or later; see LICENSE
7 */
8
9 namespace Joomla\Uri;
10
11 /**
12 * Uri Class
13 *
14 * Abstract base for out uri classes.
15 *
16 * This class should be considered an implementation detail. Typehint against UriInterface.
17 *
18 * @since 1.0
19 */
20 abstract class AbstractUri implements UriInterface
21 {
22 /**
23 * @var string Original URI
24 * @since 1.0
25 */
26 protected $uri = null;
27
28 /**
29 * @var string Protocol
30 * @since 1.0
31 */
32 protected $scheme = null;
33
34 /**
35 * @var string Host
36 * @since 1.0
37 */
38 protected $host = null;
39
40 /**
41 * @var integer Port
42 * @since 1.0
43 */
44 protected $port = null;
45
46 /**
47 * @var string Username
48 * @since 1.0
49 */
50 protected $user = null;
51
52 /**
53 * @var string Password
54 * @since 1.0
55 */
56 protected $pass = null;
57
58 /**
59 * @var string Path
60 * @since 1.0
61 */
62 protected $path = null;
63
64 /**
65 * @var string Query
66 * @since 1.0
67 */
68 protected $query = null;
69
70 /**
71 * @var string Anchor
72 * @since 1.0
73 */
74 protected $fragment = null;
75
76 /**
77 * @var array Query variable hash
78 * @since 1.0
79 */
80 protected $vars = array();
81
82 /**
83 * Constructor.
84 * You can pass a URI string to the constructor to initialise a specific URI.
85 *
86 * @param string $uri The optional URI string
87 *
88 * @since 1.0
89 */
90 public function __construct($uri = null)
91 {
92 if (!is_null($uri))
93 {
94 $this->parse($uri);
95 }
96 }
97
98 /**
99 * Magic method to get the string representation of the URI object.
100 *
101 * @return string
102 *
103 * @since 1.0
104 */
105 public function __toString()
106 {
107 return $this->toString();
108 }
109
110 /**
111 * Returns full uri string.
112 *
113 * @param array $parts An array specifying the parts to render.
114 *
115 * @return string The rendered URI string.
116 *
117 * @since 1.0
118 */
119 public function toString(array $parts = array('scheme', 'user', 'pass', 'host', 'port', 'path', 'query', 'fragment'))
120 {
121 // Make sure the query is created
122 $query = $this->getQuery();
123
124 $uri = '';
125 $uri .= in_array('scheme', $parts) ? (!empty($this->scheme) ? $this->scheme . '://' : '') : '';
126 $uri .= in_array('user', $parts) ? $this->user : '';
127 $uri .= in_array('pass', $parts) ? (!empty($this->pass) ? ':' : '') . $this->pass . (!empty($this->user) ? '@' : '') : '';
128 $uri .= in_array('host', $parts) ? $this->host : '';
129 $uri .= in_array('port', $parts) ? (!empty($this->port) ? ':' : '') . $this->port : '';
130 $uri .= in_array('path', $parts) ? $this->path : '';
131 $uri .= in_array('query', $parts) ? (!empty($query) ? '?' . $query : '') : '';
132 $uri .= in_array('fragment', $parts) ? (!empty($this->fragment) ? '#' . $this->fragment : '') : '';
133
134 return $uri;
135 }
136
137 /**
138 * Checks if variable exists.
139 *
140 * @param string $name Name of the query variable to check.
141 *
142 * @return boolean True if the variable exists.
143 *
144 * @since 1.0
145 */
146 public function hasVar($name)
147 {
148 return array_key_exists($name, $this->vars);
149 }
150
151 /**
152 * Returns a query variable by name.
153 *
154 * @param string $name Name of the query variable to get.
155 * @param string $default Default value to return if the variable is not set.
156 *
157 * @return array Query variables.
158 *
159 * @since 1.0
160 */
161 public function getVar($name, $default = null)
162 {
163 if (array_key_exists($name, $this->vars))
164 {
165 return $this->vars[$name];
166 }
167
168 return $default;
169 }
170
171 /**
172 * Returns flat query string.
173 *
174 * @param boolean $toArray True to return the query as a key => value pair array.
175 *
176 * @return string Query string.
177 *
178 * @since 1.0
179 */
180 public function getQuery($toArray = false)
181 {
182 if ($toArray)
183 {
184 return $this->vars;
185 }
186
187 // If the query is empty build it first
188 if (is_null($this->query))
189 {
190 $this->query = self::buildQuery($this->vars);
191 }
192
193 return $this->query;
194 }
195
196 /**
197 * Get URI scheme (protocol)
198 * ie. http, https, ftp, etc...
199 *
200 * @return string The URI scheme.
201 *
202 * @since 1.0
203 */
204 public function getScheme()
205 {
206 return $this->scheme;
207 }
208
209 /**
210 * Get URI username
211 * Returns the username, or null if no username was specified.
212 *
213 * @return string The URI username.
214 *
215 * @since 1.0
216 */
217 public function getUser()
218 {
219 return $this->user;
220 }
221
222 /**
223 * Get URI password
224 * Returns the password, or null if no password was specified.
225 *
226 * @return string The URI password.
227 *
228 * @since 1.0
229 */
230 public function getPass()
231 {
232 return $this->pass;
233 }
234
235 /**
236 * Get URI host
237 * Returns the hostname/ip or null if no hostname/ip was specified.
238 *
239 * @return string The URI host.
240 *
241 * @since 1.0
242 */
243 public function getHost()
244 {
245 return $this->host;
246 }
247
248 /**
249 * Get URI port
250 * Returns the port number, or null if no port was specified.
251 *
252 * @return integer The URI port number.
253 *
254 * @since 1.0
255 */
256 public function getPort()
257 {
258 return (isset($this->port)) ? $this->port : null;
259 }
260
261 /**
262 * Gets the URI path string.
263 *
264 * @return string The URI path string.
265 *
266 * @since 1.0
267 */
268 public function getPath()
269 {
270 return $this->path;
271 }
272
273 /**
274 * Get the URI archor string
275 * Everything after the "#".
276 *
277 * @return string The URI anchor string.
278 *
279 * @since 1.0
280 */
281 public function getFragment()
282 {
283 return $this->fragment;
284 }
285
286 /**
287 * Checks whether the current URI is using HTTPS.
288 *
289 * @return boolean True if using SSL via HTTPS.
290 *
291 * @since 1.0
292 */
293 public function isSSL()
294 {
295 return $this->getScheme() == 'https' ? true : false;
296 }
297
298 /**
299 * Build a query from an array (reverse of the PHP parse_str()).
300 *
301 * @param array $params The array of key => value pairs to return as a query string.
302 *
303 * @return string The resulting query string.
304 *
305 * @see parse_str()
306 * @since 1.0
307 */
308 protected static function buildQuery(array $params)
309 {
310 return urldecode(http_build_query($params, '', '&'));
311 }
312
313 /**
314 * Parse a given URI and populate the class fields.
315 *
316 * @param string $uri The URI string to parse.
317 *
318 * @return boolean True on success.
319 *
320 * @since 1.0
321 */
322 protected function parse($uri)
323 {
324 // Set the original URI to fall back on
325 $this->uri = $uri;
326
327 /*
328 * Parse the URI and populate the object fields. If URI is parsed properly,
329 * set method return value to true.
330 */
331
332 $parts = UriHelper::parse_url($uri);
333
334 $retval = ($parts) ? true : false;
335
336 // We need to replace & with & for parse_str to work right...
337 if (isset($parts['query']) && strpos($parts['query'], '&'))
338 {
339 $parts['query'] = str_replace('&', '&', $parts['query']);
340 }
341
342 $this->scheme = isset($parts['scheme']) ? $parts['scheme'] : null;
343 $this->user = isset($parts['user']) ? $parts['user'] : null;
344 $this->pass = isset($parts['pass']) ? $parts['pass'] : null;
345 $this->host = isset($parts['host']) ? $parts['host'] : null;
346 $this->port = isset($parts['port']) ? $parts['port'] : null;
347 $this->path = isset($parts['path']) ? $parts['path'] : null;
348 $this->query = isset($parts['query']) ? $parts['query'] : null;
349 $this->fragment = isset($parts['fragment']) ? $parts['fragment'] : null;
350
351 // Parse the query
352 if (isset($parts['query']))
353 {
354 parse_str($parts['query'], $this->vars);
355 }
356
357 return $retval;
358 }
359
360 /**
361 * Resolves //, ../ and ./ from a path and returns
362 * the result. Eg:
363 *
364 * /foo/bar/../boo.php => /foo/boo.php
365 * /foo/bar/../../boo.php => /boo.php
366 * /foo/bar/.././/boo.php => /foo/boo.php
367 *
368 * @param string $path The URI path to clean.
369 *
370 * @return string Cleaned and resolved URI path.
371 *
372 * @since 1.0
373 */
374 protected function cleanPath($path)
375 {
376 $path = explode('/', preg_replace('#(/+)#', '/', $path));
377
378 for ($i = 0, $n = count($path); $i < $n; $i++)
379 {
380 if ($path[$i] == '.' || $path[$i] == '..')
381 {
382 if (($path[$i] == '.') || ($path[$i] == '..' && $i == 1 && $path[0] == ''))
383 {
384 unset($path[$i]);
385 $path = array_values($path);
386 $i--;
387 $n--;
388 }
389 elseif ($path[$i] == '..' && ($i > 1 || ($i == 1 && $path[0] != '')))
390 {
391 unset($path[$i]);
392 unset($path[$i - 1]);
393 $path = array_values($path);
394 $i -= 2;
395 $n -= 2;
396 }
397 }
398 }
399
400 return implode('/', $path);
401 }
402 }
403