blob: aa2d71e87153cf3db5cc97c7cfb249c07cdcf8a7 [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 *
5 * An open source application development framework for PHP 4.3.2 or newer
6 *
7 * @package CodeIgniter
8 * @author ExpressionEngine Dev Team
9 * @copyright Copyright (c) 2008, EllisLab, Inc.
10 * @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
31 var $keyval = array();
32 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 */
45 function CI_URI()
46 {
47 $this->config =& load_class('Config');
48 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 {
64 // If the URL has a question mark then it's simplest to just
65 // build the URI string from the zero index of the $_GET array.
66 // This avoids having to deal with $_SERVER variables, which
67 // can be unreliable in some environments
68 if (is_array($_GET) && count($_GET) == 1 && trim(key($_GET), '/') != '')
69 {
70 $this->uri_string = key($_GET);
71 return;
72 }
73
74 // Is there a PATH_INFO variable?
75 // Note: some servers seem to have trouble with getenv() so we'll test it two ways
76 $path = (isset($_SERVER['PATH_INFO'])) ? $_SERVER['PATH_INFO'] : @getenv('PATH_INFO');
77 if (trim($path, '/') != '' && $path != "/".SELF)
78 {
79 $this->uri_string = $path;
80 return;
81 }
82
83 // No PATH_INFO?... What about QUERY_STRING?
84 $path = (isset($_SERVER['QUERY_STRING'])) ? $_SERVER['QUERY_STRING'] : @getenv('QUERY_STRING');
85 if (trim($path, '/') != '')
86 {
87 $this->uri_string = $path;
88 return;
89 }
90
91 // No QUERY_STRING?... Maybe the ORIG_PATH_INFO variable exists?
92 $path = (isset($_SERVER['ORIG_PATH_INFO'])) ? $_SERVER['ORIG_PATH_INFO'] : @getenv('ORIG_PATH_INFO');
93 if (trim($path, '/') != '' && $path != "/".SELF)
94 {
95 // remove path and script information so we have good URI data
96 $this->uri_string = str_replace($_SERVER['SCRIPT_NAME'], '', $path);
97 return;
98 }
99
100 // We've exhausted all our options...
101 $this->uri_string = '';
102 }
103 else
104 {
105 $uri = strtoupper($this->config->item('uri_protocol'));
106
107 if ($uri == 'REQUEST_URI')
108 {
109 $this->uri_string = $this->_parse_request_uri();
110 return;
111 }
112
113 $this->uri_string = (isset($_SERVER[$uri])) ? $_SERVER[$uri] : @getenv($uri);
114 }
115
116 // If the URI contains only a slash we'll kill it
117 if ($this->uri_string == '/')
118 {
119 $this->uri_string = '';
120 }
121 }
122
123 // --------------------------------------------------------------------
124
125 /**
126 * Parse the REQUEST_URI
127 *
128 * Due to the way REQUEST_URI works it usually contains path info
129 * that makes it unusable as URI data. We'll trim off the unnecessary
130 * data, hopefully arriving at a valid URI that we can use.
131 *
132 * @access private
133 * @return string
134 */
135 function _parse_request_uri()
136 {
137 if ( ! isset($_SERVER['REQUEST_URI']) OR $_SERVER['REQUEST_URI'] == '')
138 {
139 return '';
140 }
141
142 $request_uri = preg_replace("|/(.*)|", "\\1", str_replace("\\", "/", $_SERVER['REQUEST_URI']));
143
144 if ($request_uri == '' OR $request_uri == SELF)
145 {
146 return '';
147 }
148
149 $fc_path = FCPATH;
150 if (strpos($request_uri, '?') !== FALSE)
151 {
152 $fc_path .= '?';
153 }
154
155 $parsed_uri = explode("/", $request_uri);
156
157 $i = 0;
158 foreach(explode("/", $fc_path) as $segment)
159 {
160 if (isset($parsed_uri[$i]) && $segment == $parsed_uri[$i])
161 {
162 $i++;
163 }
164 }
165
166 $parsed_uri = implode("/", array_slice($parsed_uri, $i));
167
168 if ($parsed_uri != '')
169 {
170 $parsed_uri = '/'.$parsed_uri;
171 }
172
173 return $parsed_uri;
174 }
175
176 // --------------------------------------------------------------------
177
178 /**
179 * Filter segments for malicious characters
180 *
181 * @access private
182 * @param string
183 * @return string
184 */
185 function _filter_uri($str)
186 {
187 if ($str != '' && $this->config->item('permitted_uri_chars') != '' && $this->config->item('enable_query_strings') == FALSE)
188 {
189 if ( ! preg_match("|^[".preg_quote($this->config->item('permitted_uri_chars'))."]+$|i", $str))
190 {
191 header('HTTP/1.1 400 Bad Request');
192 exit('The URI you submitted has disallowed characters.');
193 }
194 }
195
196 // Convert programatic characters to entities
197 $bad = array('$', '(', ')', '%28', '%29');
198 $good = array('&#36;', '&#40;', '&#41;', '&#40;', '&#41;');
199
200 return str_replace($bad, $good, $str);
201 }
202
203 // --------------------------------------------------------------------
204
205 /**
206 * Remove the suffix from the URL if needed
207 *
208 * @access private
209 * @return void
210 */
211 function _remove_url_suffix()
212 {
213 if ($this->config->item('url_suffix') != "")
214 {
215 $this->uri_string = preg_replace("|".preg_quote($this->config->item('url_suffix'))."$|", "", $this->uri_string);
216 }
217 }
218
219 // --------------------------------------------------------------------
220
221 /**
222 * Explode the URI Segments. The individual segments will
223 * be stored in the $this->segments array.
224 *
225 * @access private
226 * @return void
227 */
228 function _explode_segments()
229 {
230 foreach(explode("/", preg_replace("|/*(.+?)/*$|", "\\1", $this->uri_string)) as $val)
231 {
232 // Filter segments for security
233 $val = trim($this->_filter_uri($val));
234
235 if ($val != '')
236 {
237 $this->segments[] = $val;
238 }
239 }
240 }
241
242 // --------------------------------------------------------------------
243 /**
244 * Re-index Segments
245 *
246 * This function re-indexes the $this->segment array so that it
247 * starts at 1 rather than 0. Doing so makes it simpler to
248 * use functions like $this->uri->segment(n) since there is
249 * a 1:1 relationship between the segment array and the actual segments.
250 *
251 * @access private
252 * @return void
253 */
254 function _reindex_segments()
255 {
256 array_unshift($this->segments, NULL);
257 array_unshift($this->rsegments, NULL);
258 unset($this->segments[0]);
259 unset($this->rsegments[0]);
260 }
261
262 // --------------------------------------------------------------------
263
264 /**
265 * Fetch a URI Segment
266 *
267 * This function returns the URI segment based on the number provided.
268 *
269 * @access public
270 * @param integer
271 * @param bool
272 * @return string
273 */
274 function segment($n, $no_result = FALSE)
275 {
276 return ( ! isset($this->segments[$n])) ? $no_result : $this->segments[$n];
277 }
278
279 // --------------------------------------------------------------------
280
281 /**
282 * Fetch a URI "routed" Segment
283 *
284 * This function returns the re-routed URI segment (assuming routing rules are used)
285 * based on the number provided. If there is no routing this function returns the
286 * same result as $this->segment()
287 *
288 * @access public
289 * @param integer
290 * @param bool
291 * @return string
292 */
293 function rsegment($n, $no_result = FALSE)
294 {
295 return ( ! isset($this->rsegments[$n])) ? $no_result : $this->rsegments[$n];
296 }
297
298 // --------------------------------------------------------------------
299
300 /**
301 * Generate a key value pair from the URI string
302 *
303 * This function generates and associative array of URI data starting
304 * at the supplied segment. For example, if this is your URI:
305 *
306 * example.com/user/search/name/joe/location/UK/gender/male
307 *
308 * You can use this function to generate an array with this prototype:
309 *
310 * array (
311 * name => joe
312 * location => UK
313 * gender => male
314 * )
315 *
316 * @access public
317 * @param integer the starting segment number
318 * @param array an array of default values
319 * @return array
320 */
321 function uri_to_assoc($n = 3, $default = array())
322 {
323 return $this->_uri_to_assoc($n, $default, 'segment');
324 }
325 /**
326 * Identical to above only it uses the re-routed segment array
327 *
328 */
329 function ruri_to_assoc($n = 3, $default = array())
330 {
331 return $this->_uri_to_assoc($n, $default, 'rsegment');
332 }
333
334 // --------------------------------------------------------------------
335
336 /**
337 * Generate a key value pair from the URI string or Re-routed URI string
338 *
339 * @access private
340 * @param integer the starting segment number
341 * @param array an array of default values
342 * @param string which array we should use
343 * @return array
344 */
345 function _uri_to_assoc($n = 3, $default = array(), $which = 'segment')
346 {
347 if ($which == 'segment')
348 {
349 $total_segments = 'total_segments';
350 $segment_array = 'segment_array';
351 }
352 else
353 {
354 $total_segments = 'total_rsegments';
355 $segment_array = 'rsegment_array';
356 }
357
358 if ( ! is_numeric($n))
359 {
360 return $default;
361 }
362
363 if (isset($this->keyval[$n]))
364 {
365 return $this->keyval[$n];
366 }
367
368 if ($this->$total_segments() < $n)
369 {
370 if (count($default) == 0)
371 {
372 return array();
373 }
374
375 $retval = array();
376 foreach ($default as $val)
377 {
378 $retval[$val] = FALSE;
379 }
380 return $retval;
381 }
382
383 $segments = array_slice($this->$segment_array(), ($n - 1));
384
385 $i = 0;
386 $lastval = '';
387 $retval = array();
388 foreach ($segments as $seg)
389 {
390 if ($i % 2)
391 {
392 $retval[$lastval] = $seg;
393 }
394 else
395 {
396 $retval[$seg] = FALSE;
397 $lastval = $seg;
398 }
399
400 $i++;
401 }
402
403 if (count($default) > 0)
404 {
405 foreach ($default as $val)
406 {
407 if ( ! array_key_exists($val, $retval))
408 {
409 $retval[$val] = FALSE;
410 }
411 }
412 }
413
414 // Cache the array for reuse
415 $this->keyval[$n] = $retval;
416 return $retval;
417 }
418
419 // --------------------------------------------------------------------
420
421 /**
422 * Generate a URI string from an associative array
423 *
424 *
425 * @access public
426 * @param array an associative array of key/values
427 * @return array
428 */
429 function assoc_to_uri($array)
430 {
431 $temp = array();
432 foreach ((array)$array as $key => $val)
433 {
434 $temp[] = $key;
435 $temp[] = $val;
436 }
437
438 return implode('/', $temp);
439 }
440
441 // --------------------------------------------------------------------
442
443 /**
444 * Fetch a URI Segment and add a trailing slash
445 *
446 * @access public
447 * @param integer
448 * @param string
449 * @return string
450 */
451 function slash_segment($n, $where = 'trailing')
452 {
453 return $this->_slash_segment($n, $where, 'segment');
454 }
455
456 // --------------------------------------------------------------------
457
458 /**
459 * Fetch a URI Segment and add a trailing slash
460 *
461 * @access public
462 * @param integer
463 * @param string
464 * @return string
465 */
466 function slash_rsegment($n, $where = 'trailing')
467 {
468 return $this->_slash_segment($n, $where, 'rsegment');
469 }
470
471 // --------------------------------------------------------------------
472
473 /**
474 * Fetch a URI Segment and add a trailing slash - helper function
475 *
476 * @access private
477 * @param integer
478 * @param string
479 * @param string
480 * @return string
481 */
482 function _slash_segment($n, $where = 'trailing', $which = 'segment')
483 {
484 if ($where == 'trailing')
485 {
486 $trailing = '/';
487 $leading = '';
488 }
489 elseif ($where == 'leading')
490 {
491 $leading = '/';
492 $trailing = '';
493 }
494 else
495 {
496 $leading = '/';
497 $trailing = '/';
498 }
499 return $leading.$this->$which($n).$trailing;
500 }
501
502 // --------------------------------------------------------------------
503
504 /**
505 * Segment Array
506 *
507 * @access public
508 * @return array
509 */
510 function segment_array()
511 {
512 return $this->segments;
513 }
514
515 // --------------------------------------------------------------------
516
517 /**
518 * Routed Segment Array
519 *
520 * @access public
521 * @return array
522 */
523 function rsegment_array()
524 {
525 return $this->rsegments;
526 }
527
528 // --------------------------------------------------------------------
529
530 /**
531 * Total number of segments
532 *
533 * @access public
534 * @return integer
535 */
536 function total_segments()
537 {
538 return count($this->segments);
539 }
540
541 // --------------------------------------------------------------------
542
543 /**
544 * Total number of routed segments
545 *
546 * @access public
547 * @return integer
548 */
549 function total_rsegments()
550 {
551 return count($this->rsegments);
552 }
553
554 // --------------------------------------------------------------------
555
556 /**
557 * Fetch the entire URI string
558 *
559 * @access public
560 * @return string
561 */
562 function uri_string()
563 {
564 return $this->uri_string;
565 }
566
567
568 // --------------------------------------------------------------------
569
570 /**
571 * Fetch the entire Re-routed URI string
572 *
573 * @access public
574 * @return string
575 */
576 function ruri_string()
577 {
578 return '/'.implode('/', $this->rsegment_array()).'/';
579 }
580
581}
582// END URI Class
583
584/* End of file URI.php */
Derek Jonesa3ffbbb2008-05-11 18:18:29 +0000585/* Location: ./system/libraries/URI.php */