blob: 9d28d89cd5682fee06d466e9139fbb7137f2b62d [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
Derek Jones7f3719f2010-01-05 13:35:37 +00009 * @copyright Copyright (c) 2008 - 2010, 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 Sturgeon48c718c2010-12-30 23:40:02 +000064 // Arguments exist, it must be a command line request
65 if ( ! empty($_SERVER['argv']))
66 {
67 $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;
75 return;
76 }
77
Derek Allard2067d1a2008-11-13 22:59:24 +000078 // 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();
117 return;
118 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000119
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
Dan Horrigan65d603e2010-12-15 08:38:30 -0500134 *
Dan Horriganfea45ad2011-01-19 00:54:12 -0500135 * This function will detect the URI automatically and fix the query string
136 * if necessary.
Dan Horrigan65d603e2010-12-15 08:38:30 -0500137 *
138 * @access private
139 * @return string
140 */
Dan Horriganfea45ad2011-01-19 00:54:12 -0500141 private function _detect_uri()
Dan Horrigan65d603e2010-12-15 08:38:30 -0500142 {
Dan Horriganfea45ad2011-01-19 00:54:12 -0500143 if ( ! isset($_SERVER['REQUEST_URI']))
Dan Horrigan65d603e2010-12-15 08:38:30 -0500144 {
Dan Horriganfea45ad2011-01-19 00:54:12 -0500145 return '';
Dan Horrigan65d603e2010-12-15 08:38:30 -0500146 }
Phil Sturgeonc8089152010-12-27 19:06:28 +0000147
Dan Horriganfea45ad2011-01-19 00:54:12 -0500148 $uri = $_SERVER['REQUEST_URI'];
149 if (strpos($uri, $_SERVER['SCRIPT_NAME']) === 0)
Dan Horrigan65d603e2010-12-15 08:38:30 -0500150 {
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'])));
Dan Horrigan65d603e2010-12-15 08:38:30 -0500156 }
Phil Sturgeon48c718c2010-12-30 23:40:02 +0000157
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)
Dan Horrigan65d603e2010-12-15 08:38:30 -0500161 {
Dan Horriganfea45ad2011-01-19 00:54:12 -0500162 $uri = substr($uri, 2);
Dan Horrigan65d603e2010-12-15 08:38:30 -0500163 }
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
Dan Horrigan65d603e2010-12-15 08:38:30 -0500172 {
Dan Horriganfea45ad2011-01-19 00:54:12 -0500173 $_SERVER['QUERY_STRING'] = '';
174 $_GET = array();
Dan Horrigan65d603e2010-12-15 08:38:30 -0500175 }
Dan Horriganfea45ad2011-01-19 00:54:12 -0500176 $uri = parse_url($uri, PHP_URL_PATH);
Dan Horrigan65d603e2010-12-15 08:38:30 -0500177
Dan Horriganfea45ad2011-01-19 00:54:12 -0500178 // Do some final cleaning of the URI and return it
179 return str_replace(array('//', '../'), '/', trim($uri, '/'));
Derek Allard2067d1a2008-11-13 22:59:24 +0000180 }
181
182 // --------------------------------------------------------------------
183
184 /**
Phil Sturgeon48c718c2010-12-30 23:40:02 +0000185 * Parse cli arguments
186 *
187 * Take each command line argument and assume it is a URI segment.
188 *
189 * @access private
190 * @return string
191 */
192 private function _parse_cli_args()
193 {
194 $args = array_slice($_SERVER['argv'], 1);
195
196 return $args ? '/' . implode('/', $args) : '';
197 }
198
199 // --------------------------------------------------------------------
200
201 /**
Derek Allard2067d1a2008-11-13 22:59:24 +0000202 * Filter segments for malicious characters
203 *
204 * @access private
205 * @param string
206 * @return string
207 */
208 function _filter_uri($str)
209 {
210 if ($str != '' && $this->config->item('permitted_uri_chars') != '' && $this->config->item('enable_query_strings') == FALSE)
211 {
Derek Jonesf0a9b332009-07-29 14:19:18 +0000212 // preg_quote() in PHP 5.3 escapes -, so the str_replace() and addition of - to preg_quote() is to maintain backwards
213 // compatibility as many are unaware of how characters in the permitted_uri_chars will be parsed as a regex pattern
214 if ( ! preg_match("|^[".str_replace(array('\\-', '\-'), '-', preg_quote($this->config->item('permitted_uri_chars'), '-'))."]+$|i", $str))
Derek Allard2067d1a2008-11-13 22:59:24 +0000215 {
Derek Jones817163a2009-07-11 17:05:58 +0000216 show_error('The URI you submitted has disallowed characters.', 400);
Derek Allard2067d1a2008-11-13 22:59:24 +0000217 }
218 }
219
220 // Convert programatic characters to entities
Barry Mienydd671972010-10-04 16:33:58 +0200221 $bad = array('$', '(', ')', '%28', '%29');
Derek Allard2067d1a2008-11-13 22:59:24 +0000222 $good = array('&#36;', '&#40;', '&#41;', '&#40;', '&#41;');
223
224 return str_replace($bad, $good, $str);
225 }
226
227 // --------------------------------------------------------------------
228
229 /**
230 * Remove the suffix from the URL if needed
231 *
232 * @access private
233 * @return void
234 */
235 function _remove_url_suffix()
236 {
237 if ($this->config->item('url_suffix') != "")
238 {
239 $this->uri_string = preg_replace("|".preg_quote($this->config->item('url_suffix'))."$|", "", $this->uri_string);
240 }
241 }
242
243 // --------------------------------------------------------------------
244
245 /**
246 * Explode the URI Segments. The individual segments will
247 * be stored in the $this->segments array.
248 *
249 * @access private
250 * @return void
251 */
252 function _explode_segments()
253 {
Phil Sturgeonc8089152010-12-27 19:06:28 +0000254 foreach (explode("/", preg_replace("|/*(.+?)/*$|", "\\1", $this->uri_string)) as $val)
Derek Allard2067d1a2008-11-13 22:59:24 +0000255 {
256 // Filter segments for security
257 $val = trim($this->_filter_uri($val));
258
259 if ($val != '')
260 {
261 $this->segments[] = $val;
262 }
263 }
264 }
265
266 // --------------------------------------------------------------------
267 /**
268 * Re-index Segments
269 *
270 * This function re-indexes the $this->segment array so that it
271 * starts at 1 rather than 0. Doing so makes it simpler to
272 * use functions like $this->uri->segment(n) since there is
273 * a 1:1 relationship between the segment array and the actual segments.
274 *
275 * @access private
276 * @return void
277 */
278 function _reindex_segments()
279 {
280 array_unshift($this->segments, NULL);
281 array_unshift($this->rsegments, NULL);
282 unset($this->segments[0]);
283 unset($this->rsegments[0]);
284 }
285
286 // --------------------------------------------------------------------
287
288 /**
289 * Fetch a URI Segment
290 *
291 * This function returns the URI segment based on the number provided.
292 *
293 * @access public
294 * @param integer
295 * @param bool
296 * @return string
297 */
298 function segment($n, $no_result = FALSE)
299 {
300 return ( ! isset($this->segments[$n])) ? $no_result : $this->segments[$n];
301 }
302
303 // --------------------------------------------------------------------
304
305 /**
306 * Fetch a URI "routed" Segment
307 *
308 * This function returns the re-routed URI segment (assuming routing rules are used)
309 * based on the number provided. If there is no routing this function returns the
310 * same result as $this->segment()
311 *
312 * @access public
313 * @param integer
314 * @param bool
315 * @return string
316 */
317 function rsegment($n, $no_result = FALSE)
318 {
319 return ( ! isset($this->rsegments[$n])) ? $no_result : $this->rsegments[$n];
320 }
321
322 // --------------------------------------------------------------------
323
324 /**
325 * Generate a key value pair from the URI string
326 *
327 * This function generates and associative array of URI data starting
328 * at the supplied segment. For example, if this is your URI:
329 *
330 * example.com/user/search/name/joe/location/UK/gender/male
331 *
332 * You can use this function to generate an array with this prototype:
333 *
334 * array (
335 * name => joe
336 * location => UK
337 * gender => male
338 * )
339 *
340 * @access public
341 * @param integer the starting segment number
342 * @param array an array of default values
343 * @return array
344 */
345 function uri_to_assoc($n = 3, $default = array())
346 {
Barry Mienydd671972010-10-04 16:33:58 +0200347 return $this->_uri_to_assoc($n, $default, 'segment');
Derek Allard2067d1a2008-11-13 22:59:24 +0000348 }
349 /**
350 * Identical to above only it uses the re-routed segment array
351 *
352 */
353 function ruri_to_assoc($n = 3, $default = array())
354 {
Barry Mienydd671972010-10-04 16:33:58 +0200355 return $this->_uri_to_assoc($n, $default, 'rsegment');
Derek Allard2067d1a2008-11-13 22:59:24 +0000356 }
357
358 // --------------------------------------------------------------------
359
360 /**
361 * Generate a key value pair from the URI string or Re-routed URI string
362 *
363 * @access private
364 * @param integer the starting segment number
365 * @param array an array of default values
366 * @param string which array we should use
367 * @return array
368 */
369 function _uri_to_assoc($n = 3, $default = array(), $which = 'segment')
370 {
371 if ($which == 'segment')
372 {
373 $total_segments = 'total_segments';
374 $segment_array = 'segment_array';
375 }
376 else
377 {
378 $total_segments = 'total_rsegments';
379 $segment_array = 'rsegment_array';
380 }
381
382 if ( ! is_numeric($n))
383 {
384 return $default;
385 }
386
387 if (isset($this->keyval[$n]))
388 {
389 return $this->keyval[$n];
390 }
391
392 if ($this->$total_segments() < $n)
393 {
394 if (count($default) == 0)
395 {
396 return array();
397 }
398
399 $retval = array();
400 foreach ($default as $val)
401 {
402 $retval[$val] = FALSE;
403 }
404 return $retval;
405 }
406
407 $segments = array_slice($this->$segment_array(), ($n - 1));
408
409 $i = 0;
410 $lastval = '';
411 $retval = array();
412 foreach ($segments as $seg)
413 {
414 if ($i % 2)
415 {
416 $retval[$lastval] = $seg;
417 }
418 else
419 {
420 $retval[$seg] = FALSE;
421 $lastval = $seg;
422 }
423
424 $i++;
425 }
426
427 if (count($default) > 0)
428 {
429 foreach ($default as $val)
430 {
431 if ( ! array_key_exists($val, $retval))
432 {
433 $retval[$val] = FALSE;
434 }
435 }
436 }
437
438 // Cache the array for reuse
439 $this->keyval[$n] = $retval;
440 return $retval;
441 }
442
443 // --------------------------------------------------------------------
444
445 /**
446 * Generate a URI string from an associative array
447 *
448 *
449 * @access public
450 * @param array an associative array of key/values
451 * @return array
452 */
453 function assoc_to_uri($array)
454 {
455 $temp = array();
456 foreach ((array)$array as $key => $val)
457 {
458 $temp[] = $key;
459 $temp[] = $val;
460 }
461
462 return implode('/', $temp);
463 }
464
465 // --------------------------------------------------------------------
466
467 /**
468 * Fetch a URI Segment and add a trailing slash
469 *
470 * @access public
471 * @param integer
472 * @param string
473 * @return string
474 */
475 function slash_segment($n, $where = 'trailing')
476 {
477 return $this->_slash_segment($n, $where, 'segment');
478 }
479
480 // --------------------------------------------------------------------
481
482 /**
483 * Fetch a URI Segment and add a trailing slash
484 *
485 * @access public
486 * @param integer
487 * @param string
488 * @return string
489 */
490 function slash_rsegment($n, $where = 'trailing')
491 {
492 return $this->_slash_segment($n, $where, 'rsegment');
493 }
494
495 // --------------------------------------------------------------------
496
497 /**
498 * Fetch a URI Segment and add a trailing slash - helper function
499 *
500 * @access private
501 * @param integer
502 * @param string
503 * @param string
504 * @return string
505 */
506 function _slash_segment($n, $where = 'trailing', $which = 'segment')
507 {
Pascal Krieteebb6f4b2010-11-10 17:09:21 -0500508 $leading = '/';
509 $trailing = '/';
Phil Sturgeon48c718c2010-12-30 23:40:02 +0000510
Derek Allard2067d1a2008-11-13 22:59:24 +0000511 if ($where == 'trailing')
512 {
Derek Allard2067d1a2008-11-13 22:59:24 +0000513 $leading = '';
514 }
515 elseif ($where == 'leading')
516 {
Derek Allard2067d1a2008-11-13 22:59:24 +0000517 $trailing = '';
518 }
Phil Sturgeon48c718c2010-12-30 23:40:02 +0000519
Derek Allard2067d1a2008-11-13 22:59:24 +0000520 return $leading.$this->$which($n).$trailing;
521 }
522
523 // --------------------------------------------------------------------
524
525 /**
526 * Segment Array
527 *
528 * @access public
529 * @return array
530 */
531 function segment_array()
532 {
533 return $this->segments;
534 }
535
536 // --------------------------------------------------------------------
537
538 /**
539 * Routed Segment Array
540 *
541 * @access public
542 * @return array
543 */
544 function rsegment_array()
545 {
546 return $this->rsegments;
547 }
548
549 // --------------------------------------------------------------------
550
551 /**
552 * Total number of segments
553 *
554 * @access public
555 * @return integer
556 */
557 function total_segments()
558 {
559 return count($this->segments);
560 }
561
562 // --------------------------------------------------------------------
563
564 /**
565 * Total number of routed segments
566 *
567 * @access public
568 * @return integer
569 */
570 function total_rsegments()
571 {
572 return count($this->rsegments);
573 }
574
575 // --------------------------------------------------------------------
576
577 /**
578 * Fetch the entire URI string
579 *
580 * @access public
581 * @return string
582 */
583 function uri_string()
584 {
585 return $this->uri_string;
586 }
587
588
589 // --------------------------------------------------------------------
590
591 /**
592 * Fetch the entire Re-routed URI string
593 *
594 * @access public
595 * @return string
596 */
597 function ruri_string()
598 {
Eric Barnesffdc3922011-01-12 09:05:20 -0500599 return '/'.implode('/', $this->rsegment_array());
Derek Allard2067d1a2008-11-13 22:59:24 +0000600 }
601
602}
603// END URI Class
604
605/* End of file URI.php */
Derek Jonesc68dfbf2010-03-02 12:59:23 -0600606/* Location: ./system/core/URI.php */