blob: f6487d3f90cc1aac54eee202ddf3ee58578390b0 [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'];
136 if (strpos($uri, $_SERVER['HTTP_HOST']) !== FALSE)
137 {
138 $uri = preg_replace('/^\w+:\/\/[^\/]+/','',$uri);
139 }
140 }
141 // Now lets check for IIS
142 elseif (isset($_SERVER['HTTP_X_REWRITE_URL']))
143 {
144 $uri = $_SERVER['HTTP_X_REWRITE_URL'];
145 }
146 // Last ditch effort (for older CGI servers, like IIS 5)
147 elseif (isset($_SERVER['ORIG_PATH_INFO']))
148 {
149 $uri = $_SERVER['ORIG_PATH_INFO'];
150 if ( ! empty($_SERVER['QUERY_STRING']))
151 {
152 $uri .= '?'.$_SERVER['QUERY_STRING'];
153 }
154 }
155
156 return $uri;
157 }
158
159 // --------------------------------------------------------------------
160
161 /**
162 * Parse REQUEST_URI
Derek Allard2067d1a2008-11-13 22:59:24 +0000163 *
164 * Due to the way REQUEST_URI works it usually contains path info
165 * that makes it unusable as URI data. We'll trim off the unnecessary
166 * data, hopefully arriving at a valid URI that we can use.
167 *
168 * @access private
Dan Horrigan65d603e2010-12-15 08:38:30 -0500169 * @param string
Derek Allard2067d1a2008-11-13 22:59:24 +0000170 * @return string
171 */
Dan Horrigan65d603e2010-12-15 08:38:30 -0500172 function _parse_request_uri($uri)
Derek Allard2067d1a2008-11-13 22:59:24 +0000173 {
Dan Horrigan65d603e2010-12-15 08:38:30 -0500174 // Some server's require URL's like index.php?/whatever If that is the case,
175 // then we need to add that to our parsing.
176 $fc_path = ltrim(FCPATH.SELF, '/');
177 if (strpos($uri, SELF.'?') !== FALSE)
Derek Allard2067d1a2008-11-13 22:59:24 +0000178 {
179 $fc_path .= '?';
180 }
181
Dan Horrigan65d603e2010-12-15 08:38:30 -0500182 $parsed_uri = explode('/', ltrim($uri, '/'));
Derek Allard2067d1a2008-11-13 22:59:24 +0000183
184 $i = 0;
185 foreach(explode("/", $fc_path) as $segment)
186 {
187 if (isset($parsed_uri[$i]) && $segment == $parsed_uri[$i])
188 {
189 $i++;
190 }
191 }
192
Dan Horrigan65d603e2010-12-15 08:38:30 -0500193 $uri = implode("/", array_slice($parsed_uri, $i));
Derek Allard2067d1a2008-11-13 22:59:24 +0000194
Dan Horrigan65d603e2010-12-15 08:38:30 -0500195 // Let's take off any query string and re-assign $_SERVER['QUERY_STRING'] and $_GET.
196 // This is only needed on some servers. However, we are forced to use it to accomodate
197 // them.
198 if (($qs_pos = strpos($uri, '?')) !== FALSE)
Derek Allard2067d1a2008-11-13 22:59:24 +0000199 {
Dan Horrigan65d603e2010-12-15 08:38:30 -0500200 $_SERVER['QUERY_STRING'] = substr($uri, $qs_pos + 1);
201 parse_str($_SERVER['QUERY_STRING'], $_GET);
202 $uri = substr($uri, 0, $qs_pos);
Derek Allard2067d1a2008-11-13 22:59:24 +0000203 }
204
Dan Horrigan65d603e2010-12-15 08:38:30 -0500205 // If it is just a / or index.php then just empty it.
206 if ($uri == '/' || $uri == SELF)
207 {
208 $uri = '';
209 }
210
211 return $uri;
Derek Allard2067d1a2008-11-13 22:59:24 +0000212 }
213
214 // --------------------------------------------------------------------
215
216 /**
217 * Filter segments for malicious characters
218 *
219 * @access private
220 * @param string
221 * @return string
222 */
223 function _filter_uri($str)
224 {
225 if ($str != '' && $this->config->item('permitted_uri_chars') != '' && $this->config->item('enable_query_strings') == FALSE)
226 {
Derek Jonesf0a9b332009-07-29 14:19:18 +0000227 // preg_quote() in PHP 5.3 escapes -, so the str_replace() and addition of - to preg_quote() is to maintain backwards
228 // compatibility as many are unaware of how characters in the permitted_uri_chars will be parsed as a regex pattern
229 if ( ! preg_match("|^[".str_replace(array('\\-', '\-'), '-', preg_quote($this->config->item('permitted_uri_chars'), '-'))."]+$|i", $str))
Derek Allard2067d1a2008-11-13 22:59:24 +0000230 {
Derek Jones817163a2009-07-11 17:05:58 +0000231 show_error('The URI you submitted has disallowed characters.', 400);
Derek Allard2067d1a2008-11-13 22:59:24 +0000232 }
233 }
234
235 // Convert programatic characters to entities
Barry Mienydd671972010-10-04 16:33:58 +0200236 $bad = array('$', '(', ')', '%28', '%29');
Derek Allard2067d1a2008-11-13 22:59:24 +0000237 $good = array('&#36;', '&#40;', '&#41;', '&#40;', '&#41;');
238
239 return str_replace($bad, $good, $str);
240 }
241
242 // --------------------------------------------------------------------
243
244 /**
245 * Remove the suffix from the URL if needed
246 *
247 * @access private
248 * @return void
249 */
250 function _remove_url_suffix()
251 {
252 if ($this->config->item('url_suffix') != "")
253 {
254 $this->uri_string = preg_replace("|".preg_quote($this->config->item('url_suffix'))."$|", "", $this->uri_string);
255 }
256 }
257
258 // --------------------------------------------------------------------
259
260 /**
261 * Explode the URI Segments. The individual segments will
262 * be stored in the $this->segments array.
263 *
264 * @access private
265 * @return void
266 */
267 function _explode_segments()
268 {
269 foreach(explode("/", preg_replace("|/*(.+?)/*$|", "\\1", $this->uri_string)) as $val)
270 {
271 // Filter segments for security
272 $val = trim($this->_filter_uri($val));
273
274 if ($val != '')
275 {
276 $this->segments[] = $val;
277 }
278 }
279 }
280
281 // --------------------------------------------------------------------
282 /**
283 * Re-index Segments
284 *
285 * This function re-indexes the $this->segment array so that it
286 * starts at 1 rather than 0. Doing so makes it simpler to
287 * use functions like $this->uri->segment(n) since there is
288 * a 1:1 relationship between the segment array and the actual segments.
289 *
290 * @access private
291 * @return void
292 */
293 function _reindex_segments()
294 {
295 array_unshift($this->segments, NULL);
296 array_unshift($this->rsegments, NULL);
297 unset($this->segments[0]);
298 unset($this->rsegments[0]);
299 }
300
301 // --------------------------------------------------------------------
302
303 /**
304 * Fetch a URI Segment
305 *
306 * This function returns the URI segment based on the number provided.
307 *
308 * @access public
309 * @param integer
310 * @param bool
311 * @return string
312 */
313 function segment($n, $no_result = FALSE)
314 {
315 return ( ! isset($this->segments[$n])) ? $no_result : $this->segments[$n];
316 }
317
318 // --------------------------------------------------------------------
319
320 /**
321 * Fetch a URI "routed" Segment
322 *
323 * This function returns the re-routed URI segment (assuming routing rules are used)
324 * based on the number provided. If there is no routing this function returns the
325 * same result as $this->segment()
326 *
327 * @access public
328 * @param integer
329 * @param bool
330 * @return string
331 */
332 function rsegment($n, $no_result = FALSE)
333 {
334 return ( ! isset($this->rsegments[$n])) ? $no_result : $this->rsegments[$n];
335 }
336
337 // --------------------------------------------------------------------
338
339 /**
340 * Generate a key value pair from the URI string
341 *
342 * This function generates and associative array of URI data starting
343 * at the supplied segment. For example, if this is your URI:
344 *
345 * example.com/user/search/name/joe/location/UK/gender/male
346 *
347 * You can use this function to generate an array with this prototype:
348 *
349 * array (
350 * name => joe
351 * location => UK
352 * gender => male
353 * )
354 *
355 * @access public
356 * @param integer the starting segment number
357 * @param array an array of default values
358 * @return array
359 */
360 function uri_to_assoc($n = 3, $default = array())
361 {
Barry Mienydd671972010-10-04 16:33:58 +0200362 return $this->_uri_to_assoc($n, $default, 'segment');
Derek Allard2067d1a2008-11-13 22:59:24 +0000363 }
364 /**
365 * Identical to above only it uses the re-routed segment array
366 *
367 */
368 function ruri_to_assoc($n = 3, $default = array())
369 {
Barry Mienydd671972010-10-04 16:33:58 +0200370 return $this->_uri_to_assoc($n, $default, 'rsegment');
Derek Allard2067d1a2008-11-13 22:59:24 +0000371 }
372
373 // --------------------------------------------------------------------
374
375 /**
376 * Generate a key value pair from the URI string or Re-routed URI string
377 *
378 * @access private
379 * @param integer the starting segment number
380 * @param array an array of default values
381 * @param string which array we should use
382 * @return array
383 */
384 function _uri_to_assoc($n = 3, $default = array(), $which = 'segment')
385 {
386 if ($which == 'segment')
387 {
388 $total_segments = 'total_segments';
389 $segment_array = 'segment_array';
390 }
391 else
392 {
393 $total_segments = 'total_rsegments';
394 $segment_array = 'rsegment_array';
395 }
396
397 if ( ! is_numeric($n))
398 {
399 return $default;
400 }
401
402 if (isset($this->keyval[$n]))
403 {
404 return $this->keyval[$n];
405 }
406
407 if ($this->$total_segments() < $n)
408 {
409 if (count($default) == 0)
410 {
411 return array();
412 }
413
414 $retval = array();
415 foreach ($default as $val)
416 {
417 $retval[$val] = FALSE;
418 }
419 return $retval;
420 }
421
422 $segments = array_slice($this->$segment_array(), ($n - 1));
423
424 $i = 0;
425 $lastval = '';
426 $retval = array();
427 foreach ($segments as $seg)
428 {
429 if ($i % 2)
430 {
431 $retval[$lastval] = $seg;
432 }
433 else
434 {
435 $retval[$seg] = FALSE;
436 $lastval = $seg;
437 }
438
439 $i++;
440 }
441
442 if (count($default) > 0)
443 {
444 foreach ($default as $val)
445 {
446 if ( ! array_key_exists($val, $retval))
447 {
448 $retval[$val] = FALSE;
449 }
450 }
451 }
452
453 // Cache the array for reuse
454 $this->keyval[$n] = $retval;
455 return $retval;
456 }
457
458 // --------------------------------------------------------------------
459
460 /**
461 * Generate a URI string from an associative array
462 *
463 *
464 * @access public
465 * @param array an associative array of key/values
466 * @return array
467 */
468 function assoc_to_uri($array)
469 {
470 $temp = array();
471 foreach ((array)$array as $key => $val)
472 {
473 $temp[] = $key;
474 $temp[] = $val;
475 }
476
477 return implode('/', $temp);
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_segment($n, $where = 'trailing')
491 {
492 return $this->_slash_segment($n, $where, 'segment');
493 }
494
495 // --------------------------------------------------------------------
496
497 /**
498 * Fetch a URI Segment and add a trailing slash
499 *
500 * @access public
501 * @param integer
502 * @param string
503 * @return string
504 */
505 function slash_rsegment($n, $where = 'trailing')
506 {
507 return $this->_slash_segment($n, $where, 'rsegment');
508 }
509
510 // --------------------------------------------------------------------
511
512 /**
513 * Fetch a URI Segment and add a trailing slash - helper function
514 *
515 * @access private
516 * @param integer
517 * @param string
518 * @param string
519 * @return string
520 */
521 function _slash_segment($n, $where = 'trailing', $which = 'segment')
522 {
Pascal Krieteebb6f4b2010-11-10 17:09:21 -0500523 $leading = '/';
524 $trailing = '/';
525
Derek Allard2067d1a2008-11-13 22:59:24 +0000526 if ($where == 'trailing')
527 {
Derek Allard2067d1a2008-11-13 22:59:24 +0000528 $leading = '';
529 }
530 elseif ($where == 'leading')
531 {
Derek Allard2067d1a2008-11-13 22:59:24 +0000532 $trailing = '';
533 }
Pascal Krieteebb6f4b2010-11-10 17:09:21 -0500534
Derek Allard2067d1a2008-11-13 22:59:24 +0000535 return $leading.$this->$which($n).$trailing;
536 }
537
538 // --------------------------------------------------------------------
539
540 /**
541 * Segment Array
542 *
543 * @access public
544 * @return array
545 */
546 function segment_array()
547 {
548 return $this->segments;
549 }
550
551 // --------------------------------------------------------------------
552
553 /**
554 * Routed Segment Array
555 *
556 * @access public
557 * @return array
558 */
559 function rsegment_array()
560 {
561 return $this->rsegments;
562 }
563
564 // --------------------------------------------------------------------
565
566 /**
567 * Total number of segments
568 *
569 * @access public
570 * @return integer
571 */
572 function total_segments()
573 {
574 return count($this->segments);
575 }
576
577 // --------------------------------------------------------------------
578
579 /**
580 * Total number of routed segments
581 *
582 * @access public
583 * @return integer
584 */
585 function total_rsegments()
586 {
587 return count($this->rsegments);
588 }
589
590 // --------------------------------------------------------------------
591
592 /**
593 * Fetch the entire URI string
594 *
595 * @access public
596 * @return string
597 */
598 function uri_string()
599 {
600 return $this->uri_string;
601 }
602
603
604 // --------------------------------------------------------------------
605
606 /**
607 * Fetch the entire Re-routed URI string
608 *
609 * @access public
610 * @return string
611 */
612 function ruri_string()
613 {
614 return '/'.implode('/', $this->rsegment_array()).'/';
615 }
616
617}
618// END URI Class
619
620/* End of file URI.php */
Derek Jonesc68dfbf2010-03-02 12:59:23 -0600621/* Location: ./system/core/URI.php */