blob: c43cde0053605244916eccf9cf2c2d59f4ad4033 [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 {
Phil Sturgeon48c718c2010-12-30 23:40:02 +000067 $this->uri_string = $this->_parse_cli_args();
68 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 {
74 $this->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 {
83 $this->uri_string = $path;
84 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 {
91 $this->uri_string = $path;
92 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 {
Dan Horrigan65d603e2010-12-15 08:38:30 -050098 $this->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 = '';
104 }
105 else
106 {
107 $uri = strtoupper($this->config->item('uri_protocol'));
108
109 if ($uri == 'REQUEST_URI')
110 {
Dan Horriganfea45ad2011-01-19 00:54:12 -0500111 $this->uri_string = $this->_detect_uri();
Derek Allard2067d1a2008-11-13 22:59:24 +0000112 return;
113 }
Phil Sturgeon48c718c2010-12-30 23:40:02 +0000114 elseif ($uri == 'CLI')
115 {
116 $this->uri_string = $this->_parse_cli_args();
Derek Allard2067d1a2008-11-13 22:59:24 +0000117 return;
118 }
119
120 $this->uri_string = (isset($_SERVER[$uri])) ? $_SERVER[$uri] : @getenv($uri);
121 }
122
123 // If the URI contains only a slash we'll kill it
124 if ($this->uri_string == '/')
125 {
126 $this->uri_string = '';
127 }
128 }
129
130 // --------------------------------------------------------------------
131
132 /**
Dan Horriganfea45ad2011-01-19 00:54:12 -0500133 * Detects the URI
Derek Allard2067d1a2008-11-13 22:59:24 +0000134 *
Dan Horriganfea45ad2011-01-19 00:54:12 -0500135 * This function will detect the URI automatically and fix the query string
136 * if necessary.
Derek Allard2067d1a2008-11-13 22:59:24 +0000137 *
138 * @access private
139 * @return string
140 */
Dan Horriganfea45ad2011-01-19 00:54:12 -0500141 private function _detect_uri()
Derek Allard2067d1a2008-11-13 22:59:24 +0000142 {
Dan Horriganfea45ad2011-01-19 00:54:12 -0500143 if ( ! isset($_SERVER['REQUEST_URI']))
Derek Allard2067d1a2008-11-13 22:59:24 +0000144 {
145 return '';
146 }
147
Dan Horriganfea45ad2011-01-19 00:54:12 -0500148 $uri = $_SERVER['REQUEST_URI'];
149 if (strpos($uri, $_SERVER['SCRIPT_NAME']) === 0)
Derek Allard2067d1a2008-11-13 22:59:24 +0000150 {
Dan Horriganfea45ad2011-01-19 00:54:12 -0500151 $uri = substr($uri, strlen($_SERVER['SCRIPT_NAME']));
152 }
153 elseif (strpos($uri, dirname($_SERVER['SCRIPT_NAME'])) === 0)
154 {
155 $uri = substr($uri, strlen(dirname($_SERVER['SCRIPT_NAME'])));
Derek Allard2067d1a2008-11-13 22:59:24 +0000156 }
157
Dan Horriganfea45ad2011-01-19 00:54:12 -0500158 // This section ensures that even on servers that require the URI to be in the query string (Nginx) a correct
159 // URI is found, and also fixes the QUERY_STRING server var and $_GET array.
160 if (strncmp($uri, '?/', 2) === 0)
Derek Allard2067d1a2008-11-13 22:59:24 +0000161 {
Dan Horriganfea45ad2011-01-19 00:54:12 -0500162 $uri = substr($uri, 2);
Derek Allard2067d1a2008-11-13 22:59:24 +0000163 }
Dan Horriganfea45ad2011-01-19 00:54:12 -0500164 $parts = preg_split('#\?#i', $uri, 2);
165 $uri = $parts[0];
166 if (isset($parts[1]))
Derek Allard2067d1a2008-11-13 22:59:24 +0000167 {
Dan Horriganfea45ad2011-01-19 00:54:12 -0500168 $_SERVER['QUERY_STRING'] = $parts[1];
Dan Horrigan65d603e2010-12-15 08:38:30 -0500169 parse_str($_SERVER['QUERY_STRING'], $_GET);
Derek Allard2067d1a2008-11-13 22:59:24 +0000170 }
Dan Horriganfea45ad2011-01-19 00:54:12 -0500171 else
Derek Allard2067d1a2008-11-13 22:59:24 +0000172 {
Dan Horriganfea45ad2011-01-19 00:54:12 -0500173 $_SERVER['QUERY_STRING'] = '';
174 $_GET = array();
Derek Allard2067d1a2008-11-13 22:59:24 +0000175 }
ericbarnes@ericbarnes.locale58199b2011-02-02 22:40:36 -0500176
177 if ($uri == '/' || empty($uri))
178 {
179 return '/';
180 }
181
Dan Horriganfea45ad2011-01-19 00:54:12 -0500182 $uri = parse_url($uri, PHP_URL_PATH);
Derek Allard2067d1a2008-11-13 22:59:24 +0000183
Dan Horriganfea45ad2011-01-19 00:54:12 -0500184 // Do some final cleaning of the URI and return it
185 return str_replace(array('//', '../'), '/', trim($uri, '/'));
Derek Allard2067d1a2008-11-13 22:59:24 +0000186 }
187
188 // --------------------------------------------------------------------
189
190 /**
Phil Sturgeon48c718c2010-12-30 23:40:02 +0000191 * Parse cli arguments
192 *
193 * Take each command line argument and assume it is a URI segment.
194 *
195 * @access private
196 * @return string
197 */
198 private function _parse_cli_args()
199 {
200 $args = array_slice($_SERVER['argv'], 1);
201
202 return $args ? '/' . implode('/', $args) : '';
Derek Allard2067d1a2008-11-13 22:59:24 +0000203 }
204
205 // --------------------------------------------------------------------
206
207 /**
208 * Filter segments for malicious characters
209 *
210 * @access private
211 * @param string
212 * @return string
213 */
214 function _filter_uri($str)
215 {
216 if ($str != '' && $this->config->item('permitted_uri_chars') != '' && $this->config->item('enable_query_strings') == FALSE)
217 {
Derek Jonesf0a9b332009-07-29 14:19:18 +0000218 // preg_quote() in PHP 5.3 escapes -, so the str_replace() and addition of - to preg_quote() is to maintain backwards
219 // compatibility as many are unaware of how characters in the permitted_uri_chars will be parsed as a regex pattern
220 if ( ! preg_match("|^[".str_replace(array('\\-', '\-'), '-', preg_quote($this->config->item('permitted_uri_chars'), '-'))."]+$|i", $str))
Derek Allard2067d1a2008-11-13 22:59:24 +0000221 {
Derek Jones817163a2009-07-11 17:05:58 +0000222 show_error('The URI you submitted has disallowed characters.', 400);
Derek Allard2067d1a2008-11-13 22:59:24 +0000223 }
224 }
225
226 // Convert programatic characters to entities
Barry Mienydd671972010-10-04 16:33:58 +0200227 $bad = array('$', '(', ')', '%28', '%29');
Derek Allard2067d1a2008-11-13 22:59:24 +0000228 $good = array('&#36;', '&#40;', '&#41;', '&#40;', '&#41;');
229
230 return str_replace($bad, $good, $str);
231 }
232
233 // --------------------------------------------------------------------
234
235 /**
236 * Remove the suffix from the URL if needed
237 *
238 * @access private
239 * @return void
240 */
241 function _remove_url_suffix()
242 {
243 if ($this->config->item('url_suffix') != "")
244 {
245 $this->uri_string = preg_replace("|".preg_quote($this->config->item('url_suffix'))."$|", "", $this->uri_string);
246 }
247 }
248
249 // --------------------------------------------------------------------
250
251 /**
252 * Explode the URI Segments. The individual segments will
253 * be stored in the $this->segments array.
254 *
255 * @access private
256 * @return void
257 */
258 function _explode_segments()
259 {
Phil Sturgeonc8089152010-12-27 19:06:28 +0000260 foreach (explode("/", preg_replace("|/*(.+?)/*$|", "\\1", $this->uri_string)) as $val)
Derek Allard2067d1a2008-11-13 22:59:24 +0000261 {
262 // Filter segments for security
263 $val = trim($this->_filter_uri($val));
264
265 if ($val != '')
266 {
267 $this->segments[] = $val;
268 }
269 }
270 }
271
272 // --------------------------------------------------------------------
273 /**
274 * Re-index Segments
275 *
276 * This function re-indexes the $this->segment array so that it
277 * starts at 1 rather than 0. Doing so makes it simpler to
278 * use functions like $this->uri->segment(n) since there is
279 * a 1:1 relationship between the segment array and the actual segments.
280 *
281 * @access private
282 * @return void
283 */
284 function _reindex_segments()
285 {
286 array_unshift($this->segments, NULL);
287 array_unshift($this->rsegments, NULL);
288 unset($this->segments[0]);
289 unset($this->rsegments[0]);
290 }
291
292 // --------------------------------------------------------------------
293
294 /**
295 * Fetch a URI Segment
296 *
297 * This function returns the URI segment based on the number provided.
298 *
299 * @access public
300 * @param integer
301 * @param bool
302 * @return string
303 */
304 function segment($n, $no_result = FALSE)
305 {
306 return ( ! isset($this->segments[$n])) ? $no_result : $this->segments[$n];
307 }
308
309 // --------------------------------------------------------------------
310
311 /**
312 * Fetch a URI "routed" Segment
313 *
314 * This function returns the re-routed URI segment (assuming routing rules are used)
315 * based on the number provided. If there is no routing this function returns the
316 * same result as $this->segment()
317 *
318 * @access public
319 * @param integer
320 * @param bool
321 * @return string
322 */
323 function rsegment($n, $no_result = FALSE)
324 {
325 return ( ! isset($this->rsegments[$n])) ? $no_result : $this->rsegments[$n];
326 }
327
328 // --------------------------------------------------------------------
329
330 /**
331 * Generate a key value pair from the URI string
332 *
333 * This function generates and associative array of URI data starting
334 * at the supplied segment. For example, if this is your URI:
335 *
336 * example.com/user/search/name/joe/location/UK/gender/male
337 *
338 * You can use this function to generate an array with this prototype:
339 *
340 * array (
341 * name => joe
342 * location => UK
343 * gender => male
344 * )
345 *
346 * @access public
347 * @param integer the starting segment number
348 * @param array an array of default values
349 * @return array
350 */
351 function uri_to_assoc($n = 3, $default = array())
352 {
Barry Mienydd671972010-10-04 16:33:58 +0200353 return $this->_uri_to_assoc($n, $default, 'segment');
Derek Allard2067d1a2008-11-13 22:59:24 +0000354 }
355 /**
356 * Identical to above only it uses the re-routed segment array
357 *
358 */
359 function ruri_to_assoc($n = 3, $default = array())
360 {
Barry Mienydd671972010-10-04 16:33:58 +0200361 return $this->_uri_to_assoc($n, $default, 'rsegment');
Derek Allard2067d1a2008-11-13 22:59:24 +0000362 }
363
364 // --------------------------------------------------------------------
365
366 /**
367 * Generate a key value pair from the URI string or Re-routed URI string
368 *
369 * @access private
370 * @param integer the starting segment number
371 * @param array an array of default values
372 * @param string which array we should use
373 * @return array
374 */
375 function _uri_to_assoc($n = 3, $default = array(), $which = 'segment')
376 {
377 if ($which == 'segment')
378 {
379 $total_segments = 'total_segments';
380 $segment_array = 'segment_array';
381 }
382 else
383 {
384 $total_segments = 'total_rsegments';
385 $segment_array = 'rsegment_array';
386 }
387
388 if ( ! is_numeric($n))
389 {
390 return $default;
391 }
392
393 if (isset($this->keyval[$n]))
394 {
395 return $this->keyval[$n];
396 }
397
398 if ($this->$total_segments() < $n)
399 {
400 if (count($default) == 0)
401 {
402 return array();
403 }
404
405 $retval = array();
406 foreach ($default as $val)
407 {
408 $retval[$val] = FALSE;
409 }
410 return $retval;
411 }
412
413 $segments = array_slice($this->$segment_array(), ($n - 1));
414
415 $i = 0;
416 $lastval = '';
417 $retval = array();
418 foreach ($segments as $seg)
419 {
420 if ($i % 2)
421 {
422 $retval[$lastval] = $seg;
423 }
424 else
425 {
426 $retval[$seg] = FALSE;
427 $lastval = $seg;
428 }
429
430 $i++;
431 }
432
433 if (count($default) > 0)
434 {
435 foreach ($default as $val)
436 {
437 if ( ! array_key_exists($val, $retval))
438 {
439 $retval[$val] = FALSE;
440 }
441 }
442 }
443
444 // Cache the array for reuse
445 $this->keyval[$n] = $retval;
446 return $retval;
447 }
448
449 // --------------------------------------------------------------------
450
451 /**
452 * Generate a URI string from an associative array
453 *
454 *
455 * @access public
456 * @param array an associative array of key/values
457 * @return array
458 */
459 function assoc_to_uri($array)
460 {
461 $temp = array();
462 foreach ((array)$array as $key => $val)
463 {
464 $temp[] = $key;
465 $temp[] = $val;
466 }
467
468 return implode('/', $temp);
469 }
470
471 // --------------------------------------------------------------------
472
473 /**
474 * Fetch a URI Segment and add a trailing slash
475 *
476 * @access public
477 * @param integer
478 * @param string
479 * @return string
480 */
481 function slash_segment($n, $where = 'trailing')
482 {
483 return $this->_slash_segment($n, $where, 'segment');
484 }
485
486 // --------------------------------------------------------------------
487
488 /**
489 * Fetch a URI Segment and add a trailing slash
490 *
491 * @access public
492 * @param integer
493 * @param string
494 * @return string
495 */
496 function slash_rsegment($n, $where = 'trailing')
497 {
498 return $this->_slash_segment($n, $where, 'rsegment');
499 }
500
501 // --------------------------------------------------------------------
502
503 /**
504 * Fetch a URI Segment and add a trailing slash - helper function
505 *
506 * @access private
507 * @param integer
508 * @param string
509 * @param string
510 * @return string
511 */
512 function _slash_segment($n, $where = 'trailing', $which = 'segment')
513 {
Pascal Krieteebb6f4b2010-11-10 17:09:21 -0500514 $leading = '/';
515 $trailing = '/';
Phil Sturgeon48c718c2010-12-30 23:40:02 +0000516
Derek Allard2067d1a2008-11-13 22:59:24 +0000517 if ($where == 'trailing')
518 {
Derek Allard2067d1a2008-11-13 22:59:24 +0000519 $leading = '';
520 }
521 elseif ($where == 'leading')
522 {
Derek Allard2067d1a2008-11-13 22:59:24 +0000523 $trailing = '';
524 }
Phil Sturgeon48c718c2010-12-30 23:40:02 +0000525
Derek Allard2067d1a2008-11-13 22:59:24 +0000526 return $leading.$this->$which($n).$trailing;
527 }
528
529 // --------------------------------------------------------------------
530
531 /**
532 * Segment Array
533 *
534 * @access public
535 * @return array
536 */
537 function segment_array()
538 {
539 return $this->segments;
540 }
541
542 // --------------------------------------------------------------------
543
544 /**
545 * Routed Segment Array
546 *
547 * @access public
548 * @return array
549 */
550 function rsegment_array()
551 {
552 return $this->rsegments;
553 }
554
555 // --------------------------------------------------------------------
556
557 /**
558 * Total number of segments
559 *
560 * @access public
561 * @return integer
562 */
563 function total_segments()
564 {
565 return count($this->segments);
566 }
567
568 // --------------------------------------------------------------------
569
570 /**
571 * Total number of routed segments
572 *
573 * @access public
574 * @return integer
575 */
576 function total_rsegments()
577 {
578 return count($this->rsegments);
579 }
580
581 // --------------------------------------------------------------------
582
583 /**
584 * Fetch the entire URI string
585 *
586 * @access public
587 * @return string
588 */
589 function uri_string()
590 {
591 return $this->uri_string;
592 }
593
594
595 // --------------------------------------------------------------------
596
597 /**
598 * Fetch the entire Re-routed URI string
599 *
600 * @access public
601 * @return string
602 */
603 function ruri_string()
604 {
Eric Barnesffdc3922011-01-12 09:05:20 -0500605 return '/'.implode('/', $this->rsegment_array());
Derek Allard2067d1a2008-11-13 22:59:24 +0000606 }
607
608}
609// END URI Class
610
611/* End of file URI.php */
Derek Jonesc68dfbf2010-03-02 12:59:23 -0600612/* Location: ./system/core/URI.php */