blob: 479a225fb73ba2e9ac2d84e09d68eee3136d25b4 [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 {
Dan Horrigan65d603e2010-12-15 08:38:30 -050064 // Let's try the REQUEST_URI first, this will work in most situations
65 if ($uri = $this->_get_request_uri())
Derek Allard2067d1a2008-11-13 22:59:24 +000066 {
Dan Horrigan65d603e2010-12-15 08:38:30 -050067 $this->uri_string = $this->_parse_request_uri($uri);
Derek Allard2067d1a2008-11-13 22:59:24 +000068 return;
69 }
70
Phil Sturgeon48c718c2010-12-30 23:40:02 +000071 // Arguments exist, it must be a command line request
72 if ( ! empty($_SERVER['argv']))
73 {
74 $this->uri_string = $this->_parse_cli_args();
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 Horrigan65d603e2010-12-15 08:38:30 -0500111 $this->uri_string = $this->_parse_request_uri($this->_get_request_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 Horrigan65d603e2010-12-15 08:38:30 -0500133 * Get REQUEST_URI
134 *
135 * Retrieves the REQUEST_URI, or equivelent for IIS.
136 *
137 * @access private
138 * @return string
139 */
140 function _get_request_uri()
141 {
142 $uri = FALSE;
143
144 // Let's check for standard servers first
145 if (isset($_SERVER['REQUEST_URI']))
146 {
147 $uri = $_SERVER['REQUEST_URI'];
Phil Sturgeonc8089152010-12-27 19:06:28 +0000148 if (strpos($uri, $_SERVER['SERVER_NAME']) !== FALSE)
Dan Horrigan65d603e2010-12-15 08:38:30 -0500149 {
Phil Sturgeonc8089152010-12-27 19:06:28 +0000150 $uri = preg_replace('/^\w+:\/\/[^\/]+/', '', $uri);
Dan Horrigan65d603e2010-12-15 08:38:30 -0500151 }
152 }
Phil Sturgeonc8089152010-12-27 19:06:28 +0000153
Dan Horrigan65d603e2010-12-15 08:38:30 -0500154 // Now lets check for IIS
155 elseif (isset($_SERVER['HTTP_X_REWRITE_URL']))
156 {
157 $uri = $_SERVER['HTTP_X_REWRITE_URL'];
158 }
Phil Sturgeon48c718c2010-12-30 23:40:02 +0000159
Dan Horrigan65d603e2010-12-15 08:38:30 -0500160 // Last ditch effort (for older CGI servers, like IIS 5)
161 elseif (isset($_SERVER['ORIG_PATH_INFO']))
162 {
163 $uri = $_SERVER['ORIG_PATH_INFO'];
164 if ( ! empty($_SERVER['QUERY_STRING']))
165 {
Phil Sturgeonc8089152010-12-27 19:06:28 +0000166 $uri .= '?' . $_SERVER['QUERY_STRING'];
Dan Horrigan65d603e2010-12-15 08:38:30 -0500167 }
168 }
169
170 return $uri;
171 }
172
173 // --------------------------------------------------------------------
174
175 /**
176 * Parse REQUEST_URI
Derek Allard2067d1a2008-11-13 22:59:24 +0000177 *
178 * Due to the way REQUEST_URI works it usually contains path info
179 * that makes it unusable as URI data. We'll trim off the unnecessary
180 * data, hopefully arriving at a valid URI that we can use.
181 *
182 * @access private
Dan Horrigan65d603e2010-12-15 08:38:30 -0500183 * @param string
Derek Allard2067d1a2008-11-13 22:59:24 +0000184 * @return string
185 */
Phil Sturgeon48c718c2010-12-30 23:40:02 +0000186 private function _parse_request_uri($uri)
Derek Allard2067d1a2008-11-13 22:59:24 +0000187 {
Dan Horrigan65d603e2010-12-15 08:38:30 -0500188 // Some server's require URL's like index.php?/whatever If that is the case,
189 // then we need to add that to our parsing.
Phil Sturgeonc8089152010-12-27 19:06:28 +0000190 $fc_path = ltrim(FCPATH . SELF, '/');
191 if (strpos($uri, SELF . '?') !== FALSE)
Derek Allard2067d1a2008-11-13 22:59:24 +0000192 {
193 $fc_path .= '?';
194 }
195
Dan Horrigan65d603e2010-12-15 08:38:30 -0500196 $parsed_uri = explode('/', ltrim($uri, '/'));
Derek Allard2067d1a2008-11-13 22:59:24 +0000197
198 $i = 0;
Phil Sturgeonc8089152010-12-27 19:06:28 +0000199 foreach (explode("/", $fc_path) as $segment)
Derek Allard2067d1a2008-11-13 22:59:24 +0000200 {
201 if (isset($parsed_uri[$i]) && $segment == $parsed_uri[$i])
202 {
203 $i++;
204 }
205 }
206
Dan Horrigan65d603e2010-12-15 08:38:30 -0500207 $uri = implode("/", array_slice($parsed_uri, $i));
Derek Allard2067d1a2008-11-13 22:59:24 +0000208
Dan Horrigan65d603e2010-12-15 08:38:30 -0500209 // Let's take off any query string and re-assign $_SERVER['QUERY_STRING'] and $_GET.
210 // This is only needed on some servers. However, we are forced to use it to accomodate
211 // them.
212 if (($qs_pos = strpos($uri, '?')) !== FALSE)
Derek Allard2067d1a2008-11-13 22:59:24 +0000213 {
Dan Horrigan65d603e2010-12-15 08:38:30 -0500214 $_SERVER['QUERY_STRING'] = substr($uri, $qs_pos + 1);
215 parse_str($_SERVER['QUERY_STRING'], $_GET);
216 $uri = substr($uri, 0, $qs_pos);
Derek Allard2067d1a2008-11-13 22:59:24 +0000217 }
218
Dan Horrigan65d603e2010-12-15 08:38:30 -0500219 // If it is just a / or index.php then just empty it.
Phil Sturgeonc8089152010-12-27 19:06:28 +0000220 if ($uri == '/' OR $uri == SELF)
Dan Horrigan65d603e2010-12-15 08:38:30 -0500221 {
222 $uri = '';
223 }
224
225 return $uri;
Derek Allard2067d1a2008-11-13 22:59:24 +0000226 }
227
228 // --------------------------------------------------------------------
229
230 /**
Phil Sturgeon48c718c2010-12-30 23:40:02 +0000231 * Parse cli arguments
232 *
233 * Take each command line argument and assume it is a URI segment.
234 *
235 * @access private
236 * @return string
237 */
238 private function _parse_cli_args()
239 {
240 $args = array_slice($_SERVER['argv'], 1);
241
242 return $args ? '/' . implode('/', $args) : '';
243 }
244
245 // --------------------------------------------------------------------
246
247 /**
Derek Allard2067d1a2008-11-13 22:59:24 +0000248 * Filter segments for malicious characters
249 *
250 * @access private
251 * @param string
252 * @return string
253 */
254 function _filter_uri($str)
255 {
256 if ($str != '' && $this->config->item('permitted_uri_chars') != '' && $this->config->item('enable_query_strings') == FALSE)
257 {
Derek Jonesf0a9b332009-07-29 14:19:18 +0000258 // preg_quote() in PHP 5.3 escapes -, so the str_replace() and addition of - to preg_quote() is to maintain backwards
259 // compatibility as many are unaware of how characters in the permitted_uri_chars will be parsed as a regex pattern
260 if ( ! preg_match("|^[".str_replace(array('\\-', '\-'), '-', preg_quote($this->config->item('permitted_uri_chars'), '-'))."]+$|i", $str))
Derek Allard2067d1a2008-11-13 22:59:24 +0000261 {
Derek Jones817163a2009-07-11 17:05:58 +0000262 show_error('The URI you submitted has disallowed characters.', 400);
Derek Allard2067d1a2008-11-13 22:59:24 +0000263 }
264 }
265
266 // Convert programatic characters to entities
Barry Mienydd671972010-10-04 16:33:58 +0200267 $bad = array('$', '(', ')', '%28', '%29');
Derek Allard2067d1a2008-11-13 22:59:24 +0000268 $good = array('&#36;', '&#40;', '&#41;', '&#40;', '&#41;');
269
270 return str_replace($bad, $good, $str);
271 }
272
273 // --------------------------------------------------------------------
274
275 /**
276 * Remove the suffix from the URL if needed
277 *
278 * @access private
279 * @return void
280 */
281 function _remove_url_suffix()
282 {
283 if ($this->config->item('url_suffix') != "")
284 {
285 $this->uri_string = preg_replace("|".preg_quote($this->config->item('url_suffix'))."$|", "", $this->uri_string);
286 }
287 }
288
289 // --------------------------------------------------------------------
290
291 /**
292 * Explode the URI Segments. The individual segments will
293 * be stored in the $this->segments array.
294 *
295 * @access private
296 * @return void
297 */
298 function _explode_segments()
299 {
Phil Sturgeonc8089152010-12-27 19:06:28 +0000300 foreach (explode("/", preg_replace("|/*(.+?)/*$|", "\\1", $this->uri_string)) as $val)
Derek Allard2067d1a2008-11-13 22:59:24 +0000301 {
302 // Filter segments for security
303 $val = trim($this->_filter_uri($val));
304
305 if ($val != '')
306 {
307 $this->segments[] = $val;
308 }
309 }
310 }
311
312 // --------------------------------------------------------------------
313 /**
314 * Re-index Segments
315 *
316 * This function re-indexes the $this->segment array so that it
317 * starts at 1 rather than 0. Doing so makes it simpler to
318 * use functions like $this->uri->segment(n) since there is
319 * a 1:1 relationship between the segment array and the actual segments.
320 *
321 * @access private
322 * @return void
323 */
324 function _reindex_segments()
325 {
326 array_unshift($this->segments, NULL);
327 array_unshift($this->rsegments, NULL);
328 unset($this->segments[0]);
329 unset($this->rsegments[0]);
330 }
331
332 // --------------------------------------------------------------------
333
334 /**
335 * Fetch a URI Segment
336 *
337 * This function returns the URI segment based on the number provided.
338 *
339 * @access public
340 * @param integer
341 * @param bool
342 * @return string
343 */
344 function segment($n, $no_result = FALSE)
345 {
346 return ( ! isset($this->segments[$n])) ? $no_result : $this->segments[$n];
347 }
348
349 // --------------------------------------------------------------------
350
351 /**
352 * Fetch a URI "routed" Segment
353 *
354 * This function returns the re-routed URI segment (assuming routing rules are used)
355 * based on the number provided. If there is no routing this function returns the
356 * same result as $this->segment()
357 *
358 * @access public
359 * @param integer
360 * @param bool
361 * @return string
362 */
363 function rsegment($n, $no_result = FALSE)
364 {
365 return ( ! isset($this->rsegments[$n])) ? $no_result : $this->rsegments[$n];
366 }
367
368 // --------------------------------------------------------------------
369
370 /**
371 * Generate a key value pair from the URI string
372 *
373 * This function generates and associative array of URI data starting
374 * at the supplied segment. For example, if this is your URI:
375 *
376 * example.com/user/search/name/joe/location/UK/gender/male
377 *
378 * You can use this function to generate an array with this prototype:
379 *
380 * array (
381 * name => joe
382 * location => UK
383 * gender => male
384 * )
385 *
386 * @access public
387 * @param integer the starting segment number
388 * @param array an array of default values
389 * @return array
390 */
391 function uri_to_assoc($n = 3, $default = array())
392 {
Barry Mienydd671972010-10-04 16:33:58 +0200393 return $this->_uri_to_assoc($n, $default, 'segment');
Derek Allard2067d1a2008-11-13 22:59:24 +0000394 }
395 /**
396 * Identical to above only it uses the re-routed segment array
397 *
398 */
399 function ruri_to_assoc($n = 3, $default = array())
400 {
Barry Mienydd671972010-10-04 16:33:58 +0200401 return $this->_uri_to_assoc($n, $default, 'rsegment');
Derek Allard2067d1a2008-11-13 22:59:24 +0000402 }
403
404 // --------------------------------------------------------------------
405
406 /**
407 * Generate a key value pair from the URI string or Re-routed URI string
408 *
409 * @access private
410 * @param integer the starting segment number
411 * @param array an array of default values
412 * @param string which array we should use
413 * @return array
414 */
415 function _uri_to_assoc($n = 3, $default = array(), $which = 'segment')
416 {
417 if ($which == 'segment')
418 {
419 $total_segments = 'total_segments';
420 $segment_array = 'segment_array';
421 }
422 else
423 {
424 $total_segments = 'total_rsegments';
425 $segment_array = 'rsegment_array';
426 }
427
428 if ( ! is_numeric($n))
429 {
430 return $default;
431 }
432
433 if (isset($this->keyval[$n]))
434 {
435 return $this->keyval[$n];
436 }
437
438 if ($this->$total_segments() < $n)
439 {
440 if (count($default) == 0)
441 {
442 return array();
443 }
444
445 $retval = array();
446 foreach ($default as $val)
447 {
448 $retval[$val] = FALSE;
449 }
450 return $retval;
451 }
452
453 $segments = array_slice($this->$segment_array(), ($n - 1));
454
455 $i = 0;
456 $lastval = '';
457 $retval = array();
458 foreach ($segments as $seg)
459 {
460 if ($i % 2)
461 {
462 $retval[$lastval] = $seg;
463 }
464 else
465 {
466 $retval[$seg] = FALSE;
467 $lastval = $seg;
468 }
469
470 $i++;
471 }
472
473 if (count($default) > 0)
474 {
475 foreach ($default as $val)
476 {
477 if ( ! array_key_exists($val, $retval))
478 {
479 $retval[$val] = FALSE;
480 }
481 }
482 }
483
484 // Cache the array for reuse
485 $this->keyval[$n] = $retval;
486 return $retval;
487 }
488
489 // --------------------------------------------------------------------
490
491 /**
492 * Generate a URI string from an associative array
493 *
494 *
495 * @access public
496 * @param array an associative array of key/values
497 * @return array
498 */
499 function assoc_to_uri($array)
500 {
501 $temp = array();
502 foreach ((array)$array as $key => $val)
503 {
504 $temp[] = $key;
505 $temp[] = $val;
506 }
507
508 return implode('/', $temp);
509 }
510
511 // --------------------------------------------------------------------
512
513 /**
514 * Fetch a URI Segment and add a trailing slash
515 *
516 * @access public
517 * @param integer
518 * @param string
519 * @return string
520 */
521 function slash_segment($n, $where = 'trailing')
522 {
523 return $this->_slash_segment($n, $where, 'segment');
524 }
525
526 // --------------------------------------------------------------------
527
528 /**
529 * Fetch a URI Segment and add a trailing slash
530 *
531 * @access public
532 * @param integer
533 * @param string
534 * @return string
535 */
536 function slash_rsegment($n, $where = 'trailing')
537 {
538 return $this->_slash_segment($n, $where, 'rsegment');
539 }
540
541 // --------------------------------------------------------------------
542
543 /**
544 * Fetch a URI Segment and add a trailing slash - helper function
545 *
546 * @access private
547 * @param integer
548 * @param string
549 * @param string
550 * @return string
551 */
552 function _slash_segment($n, $where = 'trailing', $which = 'segment')
553 {
Pascal Krieteebb6f4b2010-11-10 17:09:21 -0500554 $leading = '/';
555 $trailing = '/';
Phil Sturgeon48c718c2010-12-30 23:40:02 +0000556
Derek Allard2067d1a2008-11-13 22:59:24 +0000557 if ($where == 'trailing')
558 {
Derek Allard2067d1a2008-11-13 22:59:24 +0000559 $leading = '';
560 }
561 elseif ($where == 'leading')
562 {
Derek Allard2067d1a2008-11-13 22:59:24 +0000563 $trailing = '';
564 }
Phil Sturgeon48c718c2010-12-30 23:40:02 +0000565
Derek Allard2067d1a2008-11-13 22:59:24 +0000566 return $leading.$this->$which($n).$trailing;
567 }
568
569 // --------------------------------------------------------------------
570
571 /**
572 * Segment Array
573 *
574 * @access public
575 * @return array
576 */
577 function segment_array()
578 {
579 return $this->segments;
580 }
581
582 // --------------------------------------------------------------------
583
584 /**
585 * Routed Segment Array
586 *
587 * @access public
588 * @return array
589 */
590 function rsegment_array()
591 {
592 return $this->rsegments;
593 }
594
595 // --------------------------------------------------------------------
596
597 /**
598 * Total number of segments
599 *
600 * @access public
601 * @return integer
602 */
603 function total_segments()
604 {
605 return count($this->segments);
606 }
607
608 // --------------------------------------------------------------------
609
610 /**
611 * Total number of routed segments
612 *
613 * @access public
614 * @return integer
615 */
616 function total_rsegments()
617 {
618 return count($this->rsegments);
619 }
620
621 // --------------------------------------------------------------------
622
623 /**
624 * Fetch the entire URI string
625 *
626 * @access public
627 * @return string
628 */
629 function uri_string()
630 {
631 return $this->uri_string;
632 }
633
634
635 // --------------------------------------------------------------------
636
637 /**
638 * Fetch the entire Re-routed URI string
639 *
640 * @access public
641 * @return string
642 */
643 function ruri_string()
644 {
645 return '/'.implode('/', $this->rsegment_array()).'/';
646 }
647
648}
649// END URI Class
650
651/* End of file URI.php */
Derek Jonesc68dfbf2010-03-02 12:59:23 -0600652/* Location: ./system/core/URI.php */