blob: 047e3c9bc998ccad851e3877de3e89c5664ce802 [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
71 // Is there a PATH_INFO variable?
72 // Note: some servers seem to have trouble with getenv() so we'll test it two ways
73 $path = (isset($_SERVER['PATH_INFO'])) ? $_SERVER['PATH_INFO'] : @getenv('PATH_INFO');
74 if (trim($path, '/') != '' && $path != "/".SELF)
75 {
76 $this->uri_string = $path;
77 return;
78 }
79
80 // No PATH_INFO?... What about QUERY_STRING?
81 $path = (isset($_SERVER['QUERY_STRING'])) ? $_SERVER['QUERY_STRING'] : @getenv('QUERY_STRING');
82 if (trim($path, '/') != '')
83 {
84 $this->uri_string = $path;
85 return;
86 }
87
Dan Horrigan65d603e2010-12-15 08:38:30 -050088 // As a last ditch effort lets try using the $_GET array
89 if (is_array($_GET) && count($_GET) == 1 && trim(key($_GET), '/') != '')
Derek Allard2067d1a2008-11-13 22:59:24 +000090 {
Dan Horrigan65d603e2010-12-15 08:38:30 -050091 $this->uri_string = key($_GET);
Derek Allard2067d1a2008-11-13 22:59:24 +000092 return;
93 }
94
95 // We've exhausted all our options...
96 $this->uri_string = '';
97 }
98 else
99 {
100 $uri = strtoupper($this->config->item('uri_protocol'));
101
102 if ($uri == 'REQUEST_URI')
103 {
Dan Horrigan65d603e2010-12-15 08:38:30 -0500104 $this->uri_string = $this->_parse_request_uri($this->_get_request_uri());
Derek Allard2067d1a2008-11-13 22:59:24 +0000105 return;
106 }
107
108 $this->uri_string = (isset($_SERVER[$uri])) ? $_SERVER[$uri] : @getenv($uri);
109 }
110
111 // If the URI contains only a slash we'll kill it
112 if ($this->uri_string == '/')
113 {
114 $this->uri_string = '';
115 }
116 }
117
118 // --------------------------------------------------------------------
119
120 /**
Dan Horrigan65d603e2010-12-15 08:38:30 -0500121 * Get REQUEST_URI
122 *
123 * Retrieves the REQUEST_URI, or equivelent for IIS.
124 *
125 * @access private
126 * @return string
127 */
128 function _get_request_uri()
129 {
130 $uri = FALSE;
131
132 // Let's check for standard servers first
133 if (isset($_SERVER['REQUEST_URI']))
134 {
135 $uri = $_SERVER['REQUEST_URI'];
Phil Sturgeonc8089152010-12-27 19:06:28 +0000136 if (strpos($uri, $_SERVER['SERVER_NAME']) !== FALSE)
Dan Horrigan65d603e2010-12-15 08:38:30 -0500137 {
Phil Sturgeonc8089152010-12-27 19:06:28 +0000138 $uri = preg_replace('/^\w+:\/\/[^\/]+/', '', $uri);
Dan Horrigan65d603e2010-12-15 08:38:30 -0500139 }
140 }
Phil Sturgeonc8089152010-12-27 19:06:28 +0000141
Dan Horrigan65d603e2010-12-15 08:38:30 -0500142 // Now lets check for IIS
143 elseif (isset($_SERVER['HTTP_X_REWRITE_URL']))
144 {
145 $uri = $_SERVER['HTTP_X_REWRITE_URL'];
146 }
Phil Sturgeonc8089152010-12-27 19:06:28 +0000147
Dan Horrigan65d603e2010-12-15 08:38:30 -0500148 // Last ditch effort (for older CGI servers, like IIS 5)
149 elseif (isset($_SERVER['ORIG_PATH_INFO']))
150 {
151 $uri = $_SERVER['ORIG_PATH_INFO'];
152 if ( ! empty($_SERVER['QUERY_STRING']))
153 {
Phil Sturgeonc8089152010-12-27 19:06:28 +0000154 $uri .= '?' . $_SERVER['QUERY_STRING'];
Dan Horrigan65d603e2010-12-15 08:38:30 -0500155 }
156 }
157
158 return $uri;
159 }
160
161 // --------------------------------------------------------------------
162
163 /**
164 * Parse REQUEST_URI
Derek Allard2067d1a2008-11-13 22:59:24 +0000165 *
166 * Due to the way REQUEST_URI works it usually contains path info
167 * that makes it unusable as URI data. We'll trim off the unnecessary
168 * data, hopefully arriving at a valid URI that we can use.
169 *
170 * @access private
Dan Horrigan65d603e2010-12-15 08:38:30 -0500171 * @param string
Derek Allard2067d1a2008-11-13 22:59:24 +0000172 * @return string
173 */
Dan Horrigan65d603e2010-12-15 08:38:30 -0500174 function _parse_request_uri($uri)
Derek Allard2067d1a2008-11-13 22:59:24 +0000175 {
Dan Horrigan65d603e2010-12-15 08:38:30 -0500176 // Some server's require URL's like index.php?/whatever If that is the case,
177 // then we need to add that to our parsing.
Phil Sturgeonc8089152010-12-27 19:06:28 +0000178 $fc_path = ltrim(FCPATH . SELF, '/');
179 if (strpos($uri, SELF . '?') !== FALSE)
Derek Allard2067d1a2008-11-13 22:59:24 +0000180 {
181 $fc_path .= '?';
182 }
183
Dan Horrigan65d603e2010-12-15 08:38:30 -0500184 $parsed_uri = explode('/', ltrim($uri, '/'));
Derek Allard2067d1a2008-11-13 22:59:24 +0000185
186 $i = 0;
Phil Sturgeonc8089152010-12-27 19:06:28 +0000187 foreach (explode("/", $fc_path) as $segment)
Derek Allard2067d1a2008-11-13 22:59:24 +0000188 {
189 if (isset($parsed_uri[$i]) && $segment == $parsed_uri[$i])
190 {
191 $i++;
192 }
193 }
194
Dan Horrigan65d603e2010-12-15 08:38:30 -0500195 $uri = implode("/", array_slice($parsed_uri, $i));
Derek Allard2067d1a2008-11-13 22:59:24 +0000196
Dan Horrigan65d603e2010-12-15 08:38:30 -0500197 // Let's take off any query string and re-assign $_SERVER['QUERY_STRING'] and $_GET.
198 // This is only needed on some servers. However, we are forced to use it to accomodate
199 // them.
200 if (($qs_pos = strpos($uri, '?')) !== FALSE)
Derek Allard2067d1a2008-11-13 22:59:24 +0000201 {
Dan Horrigan65d603e2010-12-15 08:38:30 -0500202 $_SERVER['QUERY_STRING'] = substr($uri, $qs_pos + 1);
203 parse_str($_SERVER['QUERY_STRING'], $_GET);
204 $uri = substr($uri, 0, $qs_pos);
Derek Allard2067d1a2008-11-13 22:59:24 +0000205 }
206
Dan Horrigan65d603e2010-12-15 08:38:30 -0500207 // If it is just a / or index.php then just empty it.
Phil Sturgeonc8089152010-12-27 19:06:28 +0000208 if ($uri == '/' OR $uri == SELF)
Dan Horrigan65d603e2010-12-15 08:38:30 -0500209 {
210 $uri = '';
211 }
212
213 return $uri;
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 = '/';
527
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 }
Pascal Krieteebb6f4b2010-11-10 17:09:21 -0500536
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 {
616 return '/'.implode('/', $this->rsegment_array()).'/';
617 }
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 */