80dc62e58eb52c6e4ab620af9544376ebe9bcf1a
[living-lab-site.git] / system / core / URI.php
1 <?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');
2 /**
3  * CodeIgniter
4  *
5  * An open source application development framework for PHP 5.1.6 or newer
6  *
7  * @package             CodeIgniter
8  * @author              ExpressionEngine Dev Team
9  * @copyright   Copyright (c) 2008 - 2011, 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  */
29 class 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 __construct()
46         {
47                 $this->config =& load_class('Config', 'core');
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                         // Is the request coming from the command line?
65                         if (defined('STDIN'))
66                         {
67                                 $this->_set_uri_string($this->_parse_cli_args());
68                                 return;
69                         }
70
71                         // Let's try the REQUEST_URI first, this will work in most situations
72                         if ($uri = $this->_detect_uri())
73                         {
74                                 $this->_set_uri_string($uri);
75                                 return;
76                         }
77
78                         // 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->_set_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->_set_uri_string($path);
92                                 return;
93                         }
94
95                         // As a last ditch effort lets try using the $_GET array
96                         if (is_array($_GET) && count($_GET) == 1 && trim(key($_GET), '/') != '')
97                         {
98                                 $this->_set_uri_string(key($_GET));
99                                 return;
100                         }
101
102                         // We've exhausted all our options...
103                         $this->uri_string = '';
104                         return;
105                 }
106
107                 $uri = strtoupper($this->config->item('uri_protocol'));
108
109                 if ($uri == 'REQUEST_URI')
110                 {
111                         $this->_set_uri_string($this->_detect_uri());
112                         return;
113                 }
114                 elseif ($uri == 'CLI')
115                 {
116                         $this->_set_uri_string($this->_parse_cli_args());
117                         return;
118                 }
119
120                 $path = (isset($_SERVER[$uri])) ? $_SERVER[$uri] : @getenv($uri);
121                 $this->_set_uri_string($path);
122         }
123         
124         // --------------------------------------------------------------------
125
126         /**
127          * Set the URI String
128          *
129          * @access      public
130          * @return      string
131          */
132         function _set_uri_string($str)
133         {
134                 // Filter out control characters
135                 $str = remove_invisible_characters($str, FALSE);
136                 
137                 // If the URI contains only a slash we'll kill it
138                 $this->uri_string = ($str == '/') ? '' : $str;
139         }
140
141         // --------------------------------------------------------------------
142
143         /**
144          * Detects the URI
145          *
146          * This function will detect the URI automatically and fix the query string
147          * if necessary.
148          *
149          * @access      private
150          * @return      string
151          */
152         private function _detect_uri()
153         {
154                 if ( ! isset($_SERVER['REQUEST_URI']))
155                 {
156                         return '';
157                 }
158
159                 $uri = $_SERVER['REQUEST_URI'];
160                 if (strpos($uri, $_SERVER['SCRIPT_NAME']) === 0)
161                 {
162                         $uri = substr($uri, strlen($_SERVER['SCRIPT_NAME']));
163                 }
164                 elseif (strpos($uri, dirname($_SERVER['SCRIPT_NAME'])) === 0)
165                 {
166                         $uri = substr($uri, strlen(dirname($_SERVER['SCRIPT_NAME'])));
167                 }
168
169                 // This section ensures that even on servers that require the URI to be in the query string (Nginx) a correct
170                 // URI is found, and also fixes the QUERY_STRING server var and $_GET array.
171                 if (strncmp($uri, '?/', 2) === 0)
172                 {
173                         $uri = substr($uri, 2);
174                 }
175                 $parts = preg_split('#\?#i', $uri, 2);
176                 $uri = $parts[0];
177                 if (isset($parts[1]))
178                 {
179                         $_SERVER['QUERY_STRING'] = $parts[1];
180                         parse_str($_SERVER['QUERY_STRING'], $_GET);
181                 }
182                 else
183                 {
184                         $_SERVER['QUERY_STRING'] = '';
185                         $_GET = array();
186                 }
187                 
188                 if ($uri == '/' || empty($uri))
189                 {
190                         return '/';
191                 }
192                                 
193                 $uri = parse_url($uri, PHP_URL_PATH);
194
195                 // Do some final cleaning of the URI and return it
196                 return str_replace(array('//', '../'), '/', trim($uri, '/'));
197         }
198
199         // --------------------------------------------------------------------
200
201         /**
202          * Parse cli arguments
203          *
204          * Take each command line argument and assume it is a URI segment.
205          *
206          * @access      private
207          * @return      string
208          */
209         private function _parse_cli_args()
210         {
211                 $args = array_slice($_SERVER['argv'], 1);
212
213                 return $args ? '/' . implode('/', $args) : '';
214         }
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                 {
229                         // 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))
232                         {
233                                 show_error('The URI you submitted has disallowed characters.', 400);
234                         }
235                 }
236
237                 // Convert programatic characters to entities
238                 $bad    = array('$',            '(',            ')',            '%28',          '%29');
239                 $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         {
271                 foreach (explode("/", preg_replace("|/*(.+?)/*$|", "\\1", $this->uri_string)) as $val)
272                 {
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         {
364                 return $this->_uri_to_assoc($n, $default, 'segment');
365         }
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         {
372                 return $this->_uri_to_assoc($n, $default, 'rsegment');
373         }
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         {
525                 $leading        = '/';
526                 $trailing       = '/';
527
528                 if ($where == 'trailing')
529                 {
530                         $leading        = '';
531                 }
532                 elseif ($where == 'leading')
533                 {
534                         $trailing       = '';
535                 }
536
537                 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 */
623 /* Location: ./system/core/URI.php */