blob: 51c2191af87e57bf1263e100e74cb16b8abce84a [file] [log] [blame]
Derek Jones37f4b9c2011-07-01 17:56:50 -05001<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
Derek Allard2067d1a2008-11-13 22:59:24 +00002/**
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
David Behler07b53422011-08-15 00:25:06 +020031 /**
32 * List of cached uri segments
33 *
34 * @var array
35 * @access public
36 */
Greg Akera9263282010-11-10 15:26:43 -060037 var $keyval = array();
David Behler07b53422011-08-15 00:25:06 +020038 /**
39 * Current uri string
40 *
41 * @var string
42 * @access public
43 */
Derek Allard2067d1a2008-11-13 22:59:24 +000044 var $uri_string;
David Behler07b53422011-08-15 00:25:06 +020045 /**
46 * List of uri segments
47 *
48 * @var array
49 * @access public
50 */
Derek Allard2067d1a2008-11-13 22:59:24 +000051 var $segments = array();
David Behler07b53422011-08-15 00:25:06 +020052 /**
53 * Re-indexed list of uri segments
54 * Starts at 1 instead of 0
55 *
56 * @var array
57 * @access public
58 */
Derek Allard2067d1a2008-11-13 22:59:24 +000059 var $rsegments = array();
60
61 /**
62 * Constructor
63 *
Derek Jones37f4b9c2011-07-01 17:56:50 -050064 * Simply globalizes the $RTR object. The front
Derek Allard2067d1a2008-11-13 22:59:24 +000065 * loads the Router class early on so it's not available
66 * normally as other classes are.
67 *
68 * @access public
69 */
Greg Akera9263282010-11-10 15:26:43 -060070 function __construct()
Derek Allard2067d1a2008-11-13 22:59:24 +000071 {
Derek Jones7576a3b2010-03-02 14:00:36 -060072 $this->config =& load_class('Config', 'core');
Derek Allard2067d1a2008-11-13 22:59:24 +000073 log_message('debug', "URI Class Initialized");
74 }
75
76
77 // --------------------------------------------------------------------
78
79 /**
80 * Get the URI String
81 *
82 * @access private
83 * @return string
84 */
85 function _fetch_uri_string()
86 {
87 if (strtoupper($this->config->item('uri_protocol')) == 'AUTO')
88 {
Phil Sturgeondda07e92011-01-31 23:26:25 +000089 // Is the request coming from the command line?
Stephen2e00c242011-08-28 10:25:40 +020090 if ($this->_is_cli_request())
Derek Allard2067d1a2008-11-13 22:59:24 +000091 {
Pascal Kriete73598e32011-04-05 15:01:05 -040092 $this->_set_uri_string($this->_parse_cli_args());
Phil Sturgeon48c718c2010-12-30 23:40:02 +000093 return;
94 }
95
Dan Horriganfea45ad2011-01-19 00:54:12 -050096 // Let's try the REQUEST_URI first, this will work in most situations
97 if ($uri = $this->_detect_uri())
98 {
Pascal Kriete73598e32011-04-05 15:01:05 -040099 $this->_set_uri_string($uri);
Derek Allard2067d1a2008-11-13 22:59:24 +0000100 return;
101 }
102
103 // Is there a PATH_INFO variable?
104 // Note: some servers seem to have trouble with getenv() so we'll test it two ways
105 $path = (isset($_SERVER['PATH_INFO'])) ? $_SERVER['PATH_INFO'] : @getenv('PATH_INFO');
106 if (trim($path, '/') != '' && $path != "/".SELF)
107 {
Pascal Kriete73598e32011-04-05 15:01:05 -0400108 $this->_set_uri_string($path);
Derek Allard2067d1a2008-11-13 22:59:24 +0000109 return;
110 }
111
112 // No PATH_INFO?... What about QUERY_STRING?
Derek Jones37f4b9c2011-07-01 17:56:50 -0500113 $path = (isset($_SERVER['QUERY_STRING'])) ? $_SERVER['QUERY_STRING'] : @getenv('QUERY_STRING');
Derek Allard2067d1a2008-11-13 22:59:24 +0000114 if (trim($path, '/') != '')
115 {
Pascal Kriete73598e32011-04-05 15:01:05 -0400116 $this->_set_uri_string($path);
Derek Allard2067d1a2008-11-13 22:59:24 +0000117 return;
118 }
119
Dan Horrigan65d603e2010-12-15 08:38:30 -0500120 // As a last ditch effort lets try using the $_GET array
121 if (is_array($_GET) && count($_GET) == 1 && trim(key($_GET), '/') != '')
Derek Allard2067d1a2008-11-13 22:59:24 +0000122 {
Pascal Kriete73598e32011-04-05 15:01:05 -0400123 $this->_set_uri_string(key($_GET));
Derek Allard2067d1a2008-11-13 22:59:24 +0000124 return;
125 }
126
127 // We've exhausted all our options...
128 $this->uri_string = '';
Pascal Kriete73598e32011-04-05 15:01:05 -0400129 return;
Derek Allard2067d1a2008-11-13 22:59:24 +0000130 }
Pascal Kriete73598e32011-04-05 15:01:05 -0400131
132 $uri = strtoupper($this->config->item('uri_protocol'));
133
134 if ($uri == 'REQUEST_URI')
Derek Allard2067d1a2008-11-13 22:59:24 +0000135 {
Pascal Kriete73598e32011-04-05 15:01:05 -0400136 $this->_set_uri_string($this->_detect_uri());
137 return;
138 }
139 elseif ($uri == 'CLI')
140 {
141 $this->_set_uri_string($this->_parse_cli_args());
142 return;
Derek Allard2067d1a2008-11-13 22:59:24 +0000143 }
144
Pascal Kriete73598e32011-04-05 15:01:05 -0400145 $path = (isset($_SERVER[$uri])) ? $_SERVER[$uri] : @getenv($uri);
146 $this->_set_uri_string($path);
147 }
Eric Barnes26eebdd2011-04-17 23:45:41 -0400148
Pascal Kriete73598e32011-04-05 15:01:05 -0400149 // --------------------------------------------------------------------
150
151 /**
152 * Set the URI String
153 *
154 * @access public
David Behler07b53422011-08-15 00:25:06 +0200155 * @param string
Pascal Kriete73598e32011-04-05 15:01:05 -0400156 * @return string
157 */
158 function _set_uri_string($str)
159 {
160 // Filter out control characters
161 $str = remove_invisible_characters($str, FALSE);
Eric Barnes26eebdd2011-04-17 23:45:41 -0400162
Derek Allard2067d1a2008-11-13 22:59:24 +0000163 // If the URI contains only a slash we'll kill it
Pascal Kriete73598e32011-04-05 15:01:05 -0400164 $this->uri_string = ($str == '/') ? '' : $str;
Derek Allard2067d1a2008-11-13 22:59:24 +0000165 }
166
167 // --------------------------------------------------------------------
168
169 /**
Dan Horriganfea45ad2011-01-19 00:54:12 -0500170 * Detects the URI
Derek Allard2067d1a2008-11-13 22:59:24 +0000171 *
Dan Horriganfea45ad2011-01-19 00:54:12 -0500172 * This function will detect the URI automatically and fix the query string
173 * if necessary.
Derek Allard2067d1a2008-11-13 22:59:24 +0000174 *
175 * @access private
176 * @return string
177 */
Dan Horriganfea45ad2011-01-19 00:54:12 -0500178 private function _detect_uri()
Derek Allard2067d1a2008-11-13 22:59:24 +0000179 {
Eric Barnes26eebdd2011-04-17 23:45:41 -0400180 if ( ! isset($_SERVER['REQUEST_URI']) OR ! isset($_SERVER['SCRIPT_NAME']))
Derek Allard2067d1a2008-11-13 22:59:24 +0000181 {
182 return '';
183 }
184
Dan Horriganfea45ad2011-01-19 00:54:12 -0500185 $uri = $_SERVER['REQUEST_URI'];
186 if (strpos($uri, $_SERVER['SCRIPT_NAME']) === 0)
Derek Allard2067d1a2008-11-13 22:59:24 +0000187 {
Dan Horriganfea45ad2011-01-19 00:54:12 -0500188 $uri = substr($uri, strlen($_SERVER['SCRIPT_NAME']));
189 }
190 elseif (strpos($uri, dirname($_SERVER['SCRIPT_NAME'])) === 0)
191 {
192 $uri = substr($uri, strlen(dirname($_SERVER['SCRIPT_NAME'])));
Derek Allard2067d1a2008-11-13 22:59:24 +0000193 }
194
Dan Horriganfea45ad2011-01-19 00:54:12 -0500195 // This section ensures that even on servers that require the URI to be in the query string (Nginx) a correct
196 // URI is found, and also fixes the QUERY_STRING server var and $_GET array.
197 if (strncmp($uri, '?/', 2) === 0)
Derek Allard2067d1a2008-11-13 22:59:24 +0000198 {
Dan Horriganfea45ad2011-01-19 00:54:12 -0500199 $uri = substr($uri, 2);
Derek Allard2067d1a2008-11-13 22:59:24 +0000200 }
Dan Horriganfea45ad2011-01-19 00:54:12 -0500201 $parts = preg_split('#\?#i', $uri, 2);
202 $uri = $parts[0];
203 if (isset($parts[1]))
Derek Allard2067d1a2008-11-13 22:59:24 +0000204 {
Dan Horriganfea45ad2011-01-19 00:54:12 -0500205 $_SERVER['QUERY_STRING'] = $parts[1];
Dan Horrigan65d603e2010-12-15 08:38:30 -0500206 parse_str($_SERVER['QUERY_STRING'], $_GET);
Derek Allard2067d1a2008-11-13 22:59:24 +0000207 }
Dan Horriganfea45ad2011-01-19 00:54:12 -0500208 else
Derek Allard2067d1a2008-11-13 22:59:24 +0000209 {
Dan Horriganfea45ad2011-01-19 00:54:12 -0500210 $_SERVER['QUERY_STRING'] = '';
211 $_GET = array();
Derek Allard2067d1a2008-11-13 22:59:24 +0000212 }
Eric Barnes26eebdd2011-04-17 23:45:41 -0400213
ericbarnes@ericbarnes.locale58199b2011-02-02 22:40:36 -0500214 if ($uri == '/' || empty($uri))
215 {
216 return '/';
217 }
Eric Barnes26eebdd2011-04-17 23:45:41 -0400218
Dan Horriganfea45ad2011-01-19 00:54:12 -0500219 $uri = parse_url($uri, PHP_URL_PATH);
Derek Allard2067d1a2008-11-13 22:59:24 +0000220
Dan Horriganfea45ad2011-01-19 00:54:12 -0500221 // Do some final cleaning of the URI and return it
222 return str_replace(array('//', '../'), '/', trim($uri, '/'));
Derek Allard2067d1a2008-11-13 22:59:24 +0000223 }
224
225 // --------------------------------------------------------------------
Stephen2e00c242011-08-28 10:25:40 +0200226
227 /**
228 * Is cli Request?
229 *
230 * Duplicate of function from the Input class to test to see if a request was made from the command line
231 *
232 * @return boolean
233 */
234 protected function _is_cli_request()
235 {
236 return (php_sapi_name() == 'cli') OR defined('STDIN');
237 }
238
239
240 // --------------------------------------------------------------------
Derek Allard2067d1a2008-11-13 22:59:24 +0000241
242 /**
Phil Sturgeon48c718c2010-12-30 23:40:02 +0000243 * Parse cli arguments
244 *
245 * Take each command line argument and assume it is a URI segment.
246 *
247 * @access private
248 * @return string
249 */
250 private function _parse_cli_args()
251 {
252 $args = array_slice($_SERVER['argv'], 1);
253
254 return $args ? '/' . implode('/', $args) : '';
Derek Allard2067d1a2008-11-13 22:59:24 +0000255 }
256
257 // --------------------------------------------------------------------
258
259 /**
260 * Filter segments for malicious characters
261 *
262 * @access private
263 * @param string
264 * @return string
265 */
266 function _filter_uri($str)
267 {
268 if ($str != '' && $this->config->item('permitted_uri_chars') != '' && $this->config->item('enable_query_strings') == FALSE)
269 {
Derek Jonesf0a9b332009-07-29 14:19:18 +0000270 // preg_quote() in PHP 5.3 escapes -, so the str_replace() and addition of - to preg_quote() is to maintain backwards
271 // compatibility as many are unaware of how characters in the permitted_uri_chars will be parsed as a regex pattern
272 if ( ! preg_match("|^[".str_replace(array('\\-', '\-'), '-', preg_quote($this->config->item('permitted_uri_chars'), '-'))."]+$|i", $str))
Derek Allard2067d1a2008-11-13 22:59:24 +0000273 {
Derek Jones817163a2009-07-11 17:05:58 +0000274 show_error('The URI you submitted has disallowed characters.', 400);
Derek Allard2067d1a2008-11-13 22:59:24 +0000275 }
276 }
277
278 // Convert programatic characters to entities
Barry Mienydd671972010-10-04 16:33:58 +0200279 $bad = array('$', '(', ')', '%28', '%29');
Derek Allard2067d1a2008-11-13 22:59:24 +0000280 $good = array('&#36;', '&#40;', '&#41;', '&#40;', '&#41;');
281
282 return str_replace($bad, $good, $str);
283 }
284
285 // --------------------------------------------------------------------
286
287 /**
288 * Remove the suffix from the URL if needed
289 *
290 * @access private
291 * @return void
292 */
293 function _remove_url_suffix()
294 {
Derek Jones37f4b9c2011-07-01 17:56:50 -0500295 if ($this->config->item('url_suffix') != "")
Derek Allard2067d1a2008-11-13 22:59:24 +0000296 {
297 $this->uri_string = preg_replace("|".preg_quote($this->config->item('url_suffix'))."$|", "", $this->uri_string);
298 }
299 }
300
301 // --------------------------------------------------------------------
302
303 /**
304 * Explode the URI Segments. The individual segments will
305 * be stored in the $this->segments array.
306 *
307 * @access private
308 * @return void
309 */
310 function _explode_segments()
311 {
Phil Sturgeonc8089152010-12-27 19:06:28 +0000312 foreach (explode("/", preg_replace("|/*(.+?)/*$|", "\\1", $this->uri_string)) as $val)
Derek Allard2067d1a2008-11-13 22:59:24 +0000313 {
314 // Filter segments for security
315 $val = trim($this->_filter_uri($val));
316
317 if ($val != '')
318 {
319 $this->segments[] = $val;
320 }
321 }
322 }
323
324 // --------------------------------------------------------------------
325 /**
326 * Re-index Segments
327 *
328 * This function re-indexes the $this->segment array so that it
Derek Jones37f4b9c2011-07-01 17:56:50 -0500329 * starts at 1 rather than 0. Doing so makes it simpler to
Derek Allard2067d1a2008-11-13 22:59:24 +0000330 * use functions like $this->uri->segment(n) since there is
331 * a 1:1 relationship between the segment array and the actual segments.
332 *
333 * @access private
334 * @return void
335 */
336 function _reindex_segments()
337 {
338 array_unshift($this->segments, NULL);
339 array_unshift($this->rsegments, NULL);
340 unset($this->segments[0]);
341 unset($this->rsegments[0]);
342 }
343
344 // --------------------------------------------------------------------
345
346 /**
347 * Fetch a URI Segment
348 *
349 * This function returns the URI segment based on the number provided.
350 *
351 * @access public
352 * @param integer
353 * @param bool
354 * @return string
355 */
356 function segment($n, $no_result = FALSE)
357 {
358 return ( ! isset($this->segments[$n])) ? $no_result : $this->segments[$n];
359 }
360
361 // --------------------------------------------------------------------
362
363 /**
364 * Fetch a URI "routed" Segment
365 *
366 * This function returns the re-routed URI segment (assuming routing rules are used)
Derek Jones37f4b9c2011-07-01 17:56:50 -0500367 * based on the number provided. If there is no routing this function returns the
Derek Allard2067d1a2008-11-13 22:59:24 +0000368 * same result as $this->segment()
369 *
370 * @access public
371 * @param integer
372 * @param bool
373 * @return string
374 */
375 function rsegment($n, $no_result = FALSE)
376 {
377 return ( ! isset($this->rsegments[$n])) ? $no_result : $this->rsegments[$n];
378 }
379
380 // --------------------------------------------------------------------
381
382 /**
383 * Generate a key value pair from the URI string
384 *
385 * This function generates and associative array of URI data starting
386 * at the supplied segment. For example, if this is your URI:
387 *
388 * example.com/user/search/name/joe/location/UK/gender/male
389 *
390 * You can use this function to generate an array with this prototype:
391 *
392 * array (
393 * name => joe
394 * location => UK
395 * gender => male
396 * )
397 *
398 * @access public
399 * @param integer the starting segment number
400 * @param array an array of default values
401 * @return array
402 */
403 function uri_to_assoc($n = 3, $default = array())
404 {
Barry Mienydd671972010-10-04 16:33:58 +0200405 return $this->_uri_to_assoc($n, $default, 'segment');
Derek Allard2067d1a2008-11-13 22:59:24 +0000406 }
407 /**
408 * Identical to above only it uses the re-routed segment array
409 *
David Behler07b53422011-08-15 00:25:06 +0200410 * @access public
411 * @param integer the starting segment number
412 * @param array an array of default values
413 * @return array
414 *
Derek Allard2067d1a2008-11-13 22:59:24 +0000415 */
416 function ruri_to_assoc($n = 3, $default = array())
417 {
Barry Mienydd671972010-10-04 16:33:58 +0200418 return $this->_uri_to_assoc($n, $default, 'rsegment');
Derek Allard2067d1a2008-11-13 22:59:24 +0000419 }
420
421 // --------------------------------------------------------------------
422
423 /**
424 * Generate a key value pair from the URI string or Re-routed URI string
425 *
426 * @access private
427 * @param integer the starting segment number
428 * @param array an array of default values
429 * @param string which array we should use
430 * @return array
431 */
432 function _uri_to_assoc($n = 3, $default = array(), $which = 'segment')
433 {
434 if ($which == 'segment')
435 {
436 $total_segments = 'total_segments';
437 $segment_array = 'segment_array';
438 }
439 else
440 {
441 $total_segments = 'total_rsegments';
442 $segment_array = 'rsegment_array';
443 }
444
445 if ( ! is_numeric($n))
446 {
447 return $default;
448 }
449
450 if (isset($this->keyval[$n]))
451 {
452 return $this->keyval[$n];
453 }
454
455 if ($this->$total_segments() < $n)
456 {
457 if (count($default) == 0)
458 {
459 return array();
460 }
461
462 $retval = array();
463 foreach ($default as $val)
464 {
465 $retval[$val] = FALSE;
466 }
467 return $retval;
468 }
469
470 $segments = array_slice($this->$segment_array(), ($n - 1));
471
472 $i = 0;
473 $lastval = '';
Derek Jones37f4b9c2011-07-01 17:56:50 -0500474 $retval = array();
Derek Allard2067d1a2008-11-13 22:59:24 +0000475 foreach ($segments as $seg)
476 {
477 if ($i % 2)
478 {
479 $retval[$lastval] = $seg;
480 }
481 else
482 {
483 $retval[$seg] = FALSE;
484 $lastval = $seg;
485 }
486
487 $i++;
488 }
489
490 if (count($default) > 0)
491 {
492 foreach ($default as $val)
493 {
494 if ( ! array_key_exists($val, $retval))
495 {
496 $retval[$val] = FALSE;
497 }
498 }
499 }
500
501 // Cache the array for reuse
502 $this->keyval[$n] = $retval;
503 return $retval;
504 }
505
506 // --------------------------------------------------------------------
507
508 /**
509 * Generate a URI string from an associative array
510 *
511 *
512 * @access public
513 * @param array an associative array of key/values
514 * @return array
515 */
516 function assoc_to_uri($array)
517 {
518 $temp = array();
519 foreach ((array)$array as $key => $val)
520 {
521 $temp[] = $key;
522 $temp[] = $val;
523 }
524
525 return implode('/', $temp);
526 }
527
528 // --------------------------------------------------------------------
529
530 /**
531 * Fetch a URI Segment and add a trailing slash
532 *
533 * @access public
534 * @param integer
535 * @param string
536 * @return string
537 */
538 function slash_segment($n, $where = 'trailing')
539 {
540 return $this->_slash_segment($n, $where, 'segment');
541 }
542
543 // --------------------------------------------------------------------
544
545 /**
546 * Fetch a URI Segment and add a trailing slash
547 *
548 * @access public
549 * @param integer
550 * @param string
551 * @return string
552 */
553 function slash_rsegment($n, $where = 'trailing')
554 {
555 return $this->_slash_segment($n, $where, 'rsegment');
556 }
557
558 // --------------------------------------------------------------------
559
560 /**
561 * Fetch a URI Segment and add a trailing slash - helper function
562 *
563 * @access private
564 * @param integer
565 * @param string
566 * @param string
567 * @return string
568 */
569 function _slash_segment($n, $where = 'trailing', $which = 'segment')
570 {
Pascal Krieteebb6f4b2010-11-10 17:09:21 -0500571 $leading = '/';
572 $trailing = '/';
Phil Sturgeon48c718c2010-12-30 23:40:02 +0000573
Derek Allard2067d1a2008-11-13 22:59:24 +0000574 if ($where == 'trailing')
575 {
Derek Allard2067d1a2008-11-13 22:59:24 +0000576 $leading = '';
577 }
578 elseif ($where == 'leading')
579 {
Derek Allard2067d1a2008-11-13 22:59:24 +0000580 $trailing = '';
581 }
Phil Sturgeon48c718c2010-12-30 23:40:02 +0000582
Derek Allard2067d1a2008-11-13 22:59:24 +0000583 return $leading.$this->$which($n).$trailing;
584 }
585
586 // --------------------------------------------------------------------
587
588 /**
589 * Segment Array
590 *
591 * @access public
592 * @return array
593 */
594 function segment_array()
595 {
596 return $this->segments;
597 }
598
599 // --------------------------------------------------------------------
600
601 /**
602 * Routed Segment Array
603 *
604 * @access public
605 * @return array
606 */
607 function rsegment_array()
608 {
609 return $this->rsegments;
610 }
611
612 // --------------------------------------------------------------------
613
614 /**
615 * Total number of segments
616 *
617 * @access public
618 * @return integer
619 */
620 function total_segments()
621 {
622 return count($this->segments);
623 }
624
625 // --------------------------------------------------------------------
626
627 /**
628 * Total number of routed segments
629 *
630 * @access public
631 * @return integer
632 */
633 function total_rsegments()
634 {
635 return count($this->rsegments);
636 }
637
638 // --------------------------------------------------------------------
639
640 /**
641 * Fetch the entire URI string
642 *
643 * @access public
644 * @return string
645 */
646 function uri_string()
647 {
648 return $this->uri_string;
649 }
650
651
652 // --------------------------------------------------------------------
653
654 /**
655 * Fetch the entire Re-routed URI string
656 *
657 * @access public
658 * @return string
659 */
660 function ruri_string()
661 {
Eric Barnesffdc3922011-01-12 09:05:20 -0500662 return '/'.implode('/', $this->rsegment_array());
Derek Allard2067d1a2008-11-13 22:59:24 +0000663 }
664
665}
666// END URI Class
667
668/* End of file URI.php */
Derek Jonesc68dfbf2010-03-02 12:59:23 -0600669/* Location: ./system/core/URI.php */