blob: d56548654d1147cab42d7c75ae6c8f5cf16ad167 [file] [log] [blame]
Derek Allard2067d1a2008-11-13 22:59:24 +00001<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
2/**
3 * CodeIgniter
4 *
Greg Aker741de1c2010-11-10 14:52:57 -06005 * An open source application development framework for PHP 5.1.6 or newer
Derek Allard2067d1a2008-11-13 22:59:24 +00006 *
7 * @package CodeIgniter
8 * @author ExpressionEngine Dev Team
Greg Aker0711dc82011-01-05 10:49:40 -06009 * @copyright Copyright (c) 2008 - 2011, EllisLab, Inc.
Derek Allard2067d1a2008-11-13 22:59:24 +000010 * @license http://codeigniter.com/user_guide/license.html
11 * @link http://codeigniter.com
12 * @since Version 1.0
13 * @filesource
14 */
15
16// ------------------------------------------------------------------------
17
18/**
19 * URI Class
20 *
21 * Parses URIs and determines routing
22 *
23 * @package CodeIgniter
24 * @subpackage Libraries
25 * @category URI
26 * @author ExpressionEngine Dev Team
27 * @link http://codeigniter.com/user_guide/libraries/uri.html
28 */
29class CI_URI {
30
Greg Akera9263282010-11-10 15:26:43 -060031 var $keyval = array();
Derek Allard2067d1a2008-11-13 22:59:24 +000032 var $uri_string;
33 var $segments = array();
34 var $rsegments = array();
35
36 /**
37 * Constructor
38 *
39 * Simply globalizes the $RTR object. The front
40 * loads the Router class early on so it's not available
41 * normally as other classes are.
42 *
43 * @access public
44 */
Greg Akera9263282010-11-10 15:26:43 -060045 function __construct()
Derek Allard2067d1a2008-11-13 22:59:24 +000046 {
Derek Jones7576a3b2010-03-02 14:00:36 -060047 $this->config =& load_class('Config', 'core');
Derek Allard2067d1a2008-11-13 22:59:24 +000048 log_message('debug', "URI Class Initialized");
49 }
50
51
52 // --------------------------------------------------------------------
53
54 /**
55 * Get the URI String
56 *
57 * @access private
58 * @return string
59 */
60 function _fetch_uri_string()
61 {
62 if (strtoupper($this->config->item('uri_protocol')) == 'AUTO')
63 {
Phil Sturgeondda07e92011-01-31 23:26:25 +000064 // Is the request coming from the command line?
65 if (defined('STDIN'))
Derek Allard2067d1a2008-11-13 22:59:24 +000066 {
Pascal Kriete73598e32011-04-05 15:01:05 -040067 $this->_set_uri_string($this->_parse_cli_args());
Phil Sturgeon48c718c2010-12-30 23:40:02 +000068 return;
69 }
70
Dan Horriganfea45ad2011-01-19 00:54:12 -050071 // Let's try the REQUEST_URI first, this will work in most situations
72 if ($uri = $this->_detect_uri())
73 {
Pascal Kriete73598e32011-04-05 15:01:05 -040074 $this->_set_uri_string($uri);
Derek Allard2067d1a2008-11-13 22:59:24 +000075 return;
76 }
77
78 // Is there a PATH_INFO variable?
79 // Note: some servers seem to have trouble with getenv() so we'll test it two ways
80 $path = (isset($_SERVER['PATH_INFO'])) ? $_SERVER['PATH_INFO'] : @getenv('PATH_INFO');
81 if (trim($path, '/') != '' && $path != "/".SELF)
82 {
Pascal Kriete73598e32011-04-05 15:01:05 -040083 $this->_set_uri_string($path);
Derek Allard2067d1a2008-11-13 22:59:24 +000084 return;
85 }
86
87 // No PATH_INFO?... What about QUERY_STRING?
88 $path = (isset($_SERVER['QUERY_STRING'])) ? $_SERVER['QUERY_STRING'] : @getenv('QUERY_STRING');
89 if (trim($path, '/') != '')
90 {
Pascal Kriete73598e32011-04-05 15:01:05 -040091 $this->_set_uri_string($path);
Derek Allard2067d1a2008-11-13 22:59:24 +000092 return;
93 }
94
Dan Horrigan65d603e2010-12-15 08:38:30 -050095 // As a last ditch effort lets try using the $_GET array
96 if (is_array($_GET) && count($_GET) == 1 && trim(key($_GET), '/') != '')
Derek Allard2067d1a2008-11-13 22:59:24 +000097 {
Pascal Kriete73598e32011-04-05 15:01:05 -040098 $this->_set_uri_string(key($_GET));
Derek Allard2067d1a2008-11-13 22:59:24 +000099 return;
100 }
101
102 // We've exhausted all our options...
103 $this->uri_string = '';
Pascal Kriete73598e32011-04-05 15:01:05 -0400104 return;
Derek Allard2067d1a2008-11-13 22:59:24 +0000105 }
Pascal Kriete73598e32011-04-05 15:01:05 -0400106
107 $uri = strtoupper($this->config->item('uri_protocol'));
108
109 if ($uri == 'REQUEST_URI')
Derek Allard2067d1a2008-11-13 22:59:24 +0000110 {
Pascal Kriete73598e32011-04-05 15:01:05 -0400111 $this->_set_uri_string($this->_detect_uri());
112 return;
113 }
114 elseif ($uri == 'CLI')
115 {
116 $this->_set_uri_string($this->_parse_cli_args());
117 return;
Derek Allard2067d1a2008-11-13 22:59:24 +0000118 }
119
Pascal Kriete73598e32011-04-05 15:01:05 -0400120 $path = (isset($_SERVER[$uri])) ? $_SERVER[$uri] : @getenv($uri);
121 $this->_set_uri_string($path);
122 }
Eric Barnes26eebdd2011-04-17 23:45:41 -0400123
Pascal Kriete73598e32011-04-05 15:01:05 -0400124 // --------------------------------------------------------------------
125
126 /**
127 * Set the URI String
128 *
129 * @access public
130 * @return string
131 */
132 function _set_uri_string($str)
133 {
134 // Filter out control characters
135 $str = remove_invisible_characters($str, FALSE);
Eric Barnes26eebdd2011-04-17 23:45:41 -0400136
Derek Allard2067d1a2008-11-13 22:59:24 +0000137 // If the URI contains only a slash we'll kill it
Pascal Kriete73598e32011-04-05 15:01:05 -0400138 $this->uri_string = ($str == '/') ? '' : $str;
Derek Allard2067d1a2008-11-13 22:59:24 +0000139 }
140
141 // --------------------------------------------------------------------
142
143 /**
Dan Horriganfea45ad2011-01-19 00:54:12 -0500144 * Detects the URI
Derek Allard2067d1a2008-11-13 22:59:24 +0000145 *
Dan Horriganfea45ad2011-01-19 00:54:12 -0500146 * This function will detect the URI automatically and fix the query string
147 * if necessary.
Derek Allard2067d1a2008-11-13 22:59:24 +0000148 *
149 * @access private
150 * @return string
151 */
Dan Horriganfea45ad2011-01-19 00:54:12 -0500152 private function _detect_uri()
Derek Allard2067d1a2008-11-13 22:59:24 +0000153 {
Eric Barnes26eebdd2011-04-17 23:45:41 -0400154 if ( ! isset($_SERVER['REQUEST_URI']) OR ! isset($_SERVER['SCRIPT_NAME']))
Derek Allard2067d1a2008-11-13 22:59:24 +0000155 {
156 return '';
157 }
158
Dan Horriganfea45ad2011-01-19 00:54:12 -0500159 $uri = $_SERVER['REQUEST_URI'];
160 if (strpos($uri, $_SERVER['SCRIPT_NAME']) === 0)
Derek Allard2067d1a2008-11-13 22:59:24 +0000161 {
Dan Horriganfea45ad2011-01-19 00:54:12 -0500162 $uri = substr($uri, strlen($_SERVER['SCRIPT_NAME']));
163 }
164 elseif (strpos($uri, dirname($_SERVER['SCRIPT_NAME'])) === 0)
165 {
166 $uri = substr($uri, strlen(dirname($_SERVER['SCRIPT_NAME'])));
Derek Allard2067d1a2008-11-13 22:59:24 +0000167 }
168
Dan Horriganfea45ad2011-01-19 00:54:12 -0500169 // This section ensures that even on servers that require the URI to be in the query string (Nginx) a correct
170 // URI is found, and also fixes the QUERY_STRING server var and $_GET array.
171 if (strncmp($uri, '?/', 2) === 0)
Derek Allard2067d1a2008-11-13 22:59:24 +0000172 {
Dan Horriganfea45ad2011-01-19 00:54:12 -0500173 $uri = substr($uri, 2);
Derek Allard2067d1a2008-11-13 22:59:24 +0000174 }
Dan Horriganfea45ad2011-01-19 00:54:12 -0500175 $parts = preg_split('#\?#i', $uri, 2);
176 $uri = $parts[0];
177 if (isset($parts[1]))
Derek Allard2067d1a2008-11-13 22:59:24 +0000178 {
Dan Horriganfea45ad2011-01-19 00:54:12 -0500179 $_SERVER['QUERY_STRING'] = $parts[1];
Dan Horrigan65d603e2010-12-15 08:38:30 -0500180 parse_str($_SERVER['QUERY_STRING'], $_GET);
Derek Allard2067d1a2008-11-13 22:59:24 +0000181 }
Dan Horriganfea45ad2011-01-19 00:54:12 -0500182 else
Derek Allard2067d1a2008-11-13 22:59:24 +0000183 {
Dan Horriganfea45ad2011-01-19 00:54:12 -0500184 $_SERVER['QUERY_STRING'] = '';
185 $_GET = array();
Derek Allard2067d1a2008-11-13 22:59:24 +0000186 }
Eric Barnes26eebdd2011-04-17 23:45:41 -0400187
ericbarnes@ericbarnes.locale58199b2011-02-02 22:40:36 -0500188 if ($uri == '/' || empty($uri))
189 {
190 return '/';
191 }
Eric Barnes26eebdd2011-04-17 23:45:41 -0400192
Dan Horriganfea45ad2011-01-19 00:54:12 -0500193 $uri = parse_url($uri, PHP_URL_PATH);
Derek Allard2067d1a2008-11-13 22:59:24 +0000194
Dan Horriganfea45ad2011-01-19 00:54:12 -0500195 // Do some final cleaning of the URI and return it
196 return str_replace(array('//', '../'), '/', trim($uri, '/'));
Derek Allard2067d1a2008-11-13 22:59:24 +0000197 }
198
199 // --------------------------------------------------------------------
200
201 /**
Phil Sturgeon48c718c2010-12-30 23:40:02 +0000202 * Parse cli arguments
203 *
204 * Take each command line argument and assume it is a URI segment.
205 *
206 * @access private
207 * @return string
208 */
209 private function _parse_cli_args()
210 {
211 $args = array_slice($_SERVER['argv'], 1);
212
213 return $args ? '/' . implode('/', $args) : '';
Derek Allard2067d1a2008-11-13 22:59:24 +0000214 }
215
216 // --------------------------------------------------------------------
217
218 /**
219 * Filter segments for malicious characters
220 *
221 * @access private
222 * @param string
223 * @return string
224 */
225 function _filter_uri($str)
226 {
227 if ($str != '' && $this->config->item('permitted_uri_chars') != '' && $this->config->item('enable_query_strings') == FALSE)
228 {
Derek Jonesf0a9b332009-07-29 14:19:18 +0000229 // preg_quote() in PHP 5.3 escapes -, so the str_replace() and addition of - to preg_quote() is to maintain backwards
230 // compatibility as many are unaware of how characters in the permitted_uri_chars will be parsed as a regex pattern
231 if ( ! preg_match("|^[".str_replace(array('\\-', '\-'), '-', preg_quote($this->config->item('permitted_uri_chars'), '-'))."]+$|i", $str))
Derek Allard2067d1a2008-11-13 22:59:24 +0000232 {
Derek Jones817163a2009-07-11 17:05:58 +0000233 show_error('The URI you submitted has disallowed characters.', 400);
Derek Allard2067d1a2008-11-13 22:59:24 +0000234 }
235 }
236
237 // Convert programatic characters to entities
Barry Mienydd671972010-10-04 16:33:58 +0200238 $bad = array('$', '(', ')', '%28', '%29');
Derek Allard2067d1a2008-11-13 22:59:24 +0000239 $good = array('&#36;', '&#40;', '&#41;', '&#40;', '&#41;');
240
241 return str_replace($bad, $good, $str);
242 }
243
244 // --------------------------------------------------------------------
245
246 /**
247 * Remove the suffix from the URL if needed
248 *
249 * @access private
250 * @return void
251 */
252 function _remove_url_suffix()
253 {
254 if ($this->config->item('url_suffix') != "")
255 {
256 $this->uri_string = preg_replace("|".preg_quote($this->config->item('url_suffix'))."$|", "", $this->uri_string);
257 }
258 }
259
260 // --------------------------------------------------------------------
261
262 /**
263 * Explode the URI Segments. The individual segments will
264 * be stored in the $this->segments array.
265 *
266 * @access private
267 * @return void
268 */
269 function _explode_segments()
270 {
Phil Sturgeonc8089152010-12-27 19:06:28 +0000271 foreach (explode("/", preg_replace("|/*(.+?)/*$|", "\\1", $this->uri_string)) as $val)
Derek Allard2067d1a2008-11-13 22:59:24 +0000272 {
273 // Filter segments for security
274 $val = trim($this->_filter_uri($val));
275
276 if ($val != '')
277 {
278 $this->segments[] = $val;
279 }
280 }
281 }
282
283 // --------------------------------------------------------------------
284 /**
285 * Re-index Segments
286 *
287 * This function re-indexes the $this->segment array so that it
288 * starts at 1 rather than 0. Doing so makes it simpler to
289 * use functions like $this->uri->segment(n) since there is
290 * a 1:1 relationship between the segment array and the actual segments.
291 *
292 * @access private
293 * @return void
294 */
295 function _reindex_segments()
296 {
297 array_unshift($this->segments, NULL);
298 array_unshift($this->rsegments, NULL);
299 unset($this->segments[0]);
300 unset($this->rsegments[0]);
301 }
302
303 // --------------------------------------------------------------------
304
305 /**
306 * Fetch a URI Segment
307 *
308 * This function returns the URI segment based on the number provided.
309 *
310 * @access public
311 * @param integer
312 * @param bool
313 * @return string
314 */
315 function segment($n, $no_result = FALSE)
316 {
317 return ( ! isset($this->segments[$n])) ? $no_result : $this->segments[$n];
318 }
319
320 // --------------------------------------------------------------------
321
322 /**
323 * Fetch a URI "routed" Segment
324 *
325 * This function returns the re-routed URI segment (assuming routing rules are used)
326 * based on the number provided. If there is no routing this function returns the
327 * same result as $this->segment()
328 *
329 * @access public
330 * @param integer
331 * @param bool
332 * @return string
333 */
334 function rsegment($n, $no_result = FALSE)
335 {
336 return ( ! isset($this->rsegments[$n])) ? $no_result : $this->rsegments[$n];
337 }
338
339 // --------------------------------------------------------------------
340
341 /**
342 * Generate a key value pair from the URI string
343 *
344 * This function generates and associative array of URI data starting
345 * at the supplied segment. For example, if this is your URI:
346 *
347 * example.com/user/search/name/joe/location/UK/gender/male
348 *
349 * You can use this function to generate an array with this prototype:
350 *
351 * array (
352 * name => joe
353 * location => UK
354 * gender => male
355 * )
356 *
357 * @access public
358 * @param integer the starting segment number
359 * @param array an array of default values
360 * @return array
361 */
362 function uri_to_assoc($n = 3, $default = array())
363 {
Barry Mienydd671972010-10-04 16:33:58 +0200364 return $this->_uri_to_assoc($n, $default, 'segment');
Derek Allard2067d1a2008-11-13 22:59:24 +0000365 }
366 /**
367 * Identical to above only it uses the re-routed segment array
368 *
369 */
370 function ruri_to_assoc($n = 3, $default = array())
371 {
Barry Mienydd671972010-10-04 16:33:58 +0200372 return $this->_uri_to_assoc($n, $default, 'rsegment');
Derek Allard2067d1a2008-11-13 22:59:24 +0000373 }
374
375 // --------------------------------------------------------------------
376
377 /**
378 * Generate a key value pair from the URI string or Re-routed URI string
379 *
380 * @access private
381 * @param integer the starting segment number
382 * @param array an array of default values
383 * @param string which array we should use
384 * @return array
385 */
386 function _uri_to_assoc($n = 3, $default = array(), $which = 'segment')
387 {
388 if ($which == 'segment')
389 {
390 $total_segments = 'total_segments';
391 $segment_array = 'segment_array';
392 }
393 else
394 {
395 $total_segments = 'total_rsegments';
396 $segment_array = 'rsegment_array';
397 }
398
399 if ( ! is_numeric($n))
400 {
401 return $default;
402 }
403
404 if (isset($this->keyval[$n]))
405 {
406 return $this->keyval[$n];
407 }
408
409 if ($this->$total_segments() < $n)
410 {
411 if (count($default) == 0)
412 {
413 return array();
414 }
415
416 $retval = array();
417 foreach ($default as $val)
418 {
419 $retval[$val] = FALSE;
420 }
421 return $retval;
422 }
423
424 $segments = array_slice($this->$segment_array(), ($n - 1));
425
426 $i = 0;
427 $lastval = '';
428 $retval = array();
429 foreach ($segments as $seg)
430 {
431 if ($i % 2)
432 {
433 $retval[$lastval] = $seg;
434 }
435 else
436 {
437 $retval[$seg] = FALSE;
438 $lastval = $seg;
439 }
440
441 $i++;
442 }
443
444 if (count($default) > 0)
445 {
446 foreach ($default as $val)
447 {
448 if ( ! array_key_exists($val, $retval))
449 {
450 $retval[$val] = FALSE;
451 }
452 }
453 }
454
455 // Cache the array for reuse
456 $this->keyval[$n] = $retval;
457 return $retval;
458 }
459
460 // --------------------------------------------------------------------
461
462 /**
463 * Generate a URI string from an associative array
464 *
465 *
466 * @access public
467 * @param array an associative array of key/values
468 * @return array
469 */
470 function assoc_to_uri($array)
471 {
472 $temp = array();
473 foreach ((array)$array as $key => $val)
474 {
475 $temp[] = $key;
476 $temp[] = $val;
477 }
478
479 return implode('/', $temp);
480 }
481
482 // --------------------------------------------------------------------
483
484 /**
485 * Fetch a URI Segment and add a trailing slash
486 *
487 * @access public
488 * @param integer
489 * @param string
490 * @return string
491 */
492 function slash_segment($n, $where = 'trailing')
493 {
494 return $this->_slash_segment($n, $where, 'segment');
495 }
496
497 // --------------------------------------------------------------------
498
499 /**
500 * Fetch a URI Segment and add a trailing slash
501 *
502 * @access public
503 * @param integer
504 * @param string
505 * @return string
506 */
507 function slash_rsegment($n, $where = 'trailing')
508 {
509 return $this->_slash_segment($n, $where, 'rsegment');
510 }
511
512 // --------------------------------------------------------------------
513
514 /**
515 * Fetch a URI Segment and add a trailing slash - helper function
516 *
517 * @access private
518 * @param integer
519 * @param string
520 * @param string
521 * @return string
522 */
523 function _slash_segment($n, $where = 'trailing', $which = 'segment')
524 {
Pascal Krieteebb6f4b2010-11-10 17:09:21 -0500525 $leading = '/';
526 $trailing = '/';
Phil Sturgeon48c718c2010-12-30 23:40:02 +0000527
Derek Allard2067d1a2008-11-13 22:59:24 +0000528 if ($where == 'trailing')
529 {
Derek Allard2067d1a2008-11-13 22:59:24 +0000530 $leading = '';
531 }
532 elseif ($where == 'leading')
533 {
Derek Allard2067d1a2008-11-13 22:59:24 +0000534 $trailing = '';
535 }
Phil Sturgeon48c718c2010-12-30 23:40:02 +0000536
Derek Allard2067d1a2008-11-13 22:59:24 +0000537 return $leading.$this->$which($n).$trailing;
538 }
539
540 // --------------------------------------------------------------------
541
542 /**
543 * Segment Array
544 *
545 * @access public
546 * @return array
547 */
548 function segment_array()
549 {
550 return $this->segments;
551 }
552
553 // --------------------------------------------------------------------
554
555 /**
556 * Routed Segment Array
557 *
558 * @access public
559 * @return array
560 */
561 function rsegment_array()
562 {
563 return $this->rsegments;
564 }
565
566 // --------------------------------------------------------------------
567
568 /**
569 * Total number of segments
570 *
571 * @access public
572 * @return integer
573 */
574 function total_segments()
575 {
576 return count($this->segments);
577 }
578
579 // --------------------------------------------------------------------
580
581 /**
582 * Total number of routed segments
583 *
584 * @access public
585 * @return integer
586 */
587 function total_rsegments()
588 {
589 return count($this->rsegments);
590 }
591
592 // --------------------------------------------------------------------
593
594 /**
595 * Fetch the entire URI string
596 *
597 * @access public
598 * @return string
599 */
600 function uri_string()
601 {
602 return $this->uri_string;
603 }
604
605
606 // --------------------------------------------------------------------
607
608 /**
609 * Fetch the entire Re-routed URI string
610 *
611 * @access public
612 * @return string
613 */
614 function ruri_string()
615 {
Eric Barnesffdc3922011-01-12 09:05:20 -0500616 return '/'.implode('/', $this->rsegment_array());
Derek Allard2067d1a2008-11-13 22:59:24 +0000617 }
618
619}
620// END URI Class
621
622/* End of file URI.php */
Derek Jonesc68dfbf2010-03-02 12:59:23 -0600623/* Location: ./system/core/URI.php */