blob: c056530a329a2bcbcf588cfc5adb48a3c795a357 [file] [log] [blame]
adminb0dd10f2006-08-25 17:25:49 +00001<?php if (!defined('BASEPATH')) exit('No direct script access allowed');
2/**
3 * Code Igniter
4 *
5 * An open source application development framework for PHP 4.3.2 or newer
6 *
7 * @package CodeIgniter
8 * @author Rick Ellis
9 * @copyright Copyright (c) 2006, pMachine, Inc.
10 * @license http://www.codeignitor.com/user_guide/license.html
11 * @link http://www.codeigniter.com
12 * @since Version 1.0
13 * @filesource
14 */
15
16// ------------------------------------------------------------------------
17
18/**
19 * Router Class
20 *
21 * Parses URIs and determines routing
22 *
23 * @package CodeIgniter
24 * @subpackage Libraries
25 * @author Rick Ellis
26 * @category Libraries
27 * @link http://www.codeigniter.com/user_guide/general/routing.html
28 */
29class CI_Router {
30
31 var $config;
32 var $uri_string = '';
33 var $segments = array();
admin99bccd62006-09-21 17:05:40 +000034 var $rsegments = array();
adminb0dd10f2006-08-25 17:25:49 +000035 var $routes = array();
admin33de9a12006-09-28 06:50:16 +000036 var $error_routes = array();
adminb0dd10f2006-08-25 17:25:49 +000037 var $class = '';
38 var $method = 'index';
admin45c872b2006-08-26 04:51:38 +000039 var $directory = '';
adminb0dd10f2006-08-25 17:25:49 +000040 var $uri_protocol = 'auto';
41 var $default_controller;
42 var $scaffolding_request = FALSE; // Must be set to FALSE
43
44 /**
45 * Constructor
46 *
47 * Runs the route mapping function.
48 */
49 function CI_Router()
50 {
admin33de9a12006-09-28 06:50:16 +000051 $this->config =& _load_class('Config');
adminb0dd10f2006-08-25 17:25:49 +000052 $this->_set_route_mapping();
53 log_message('debug', "Router Class Initialized");
54 }
55
56 // --------------------------------------------------------------------
57
58 /**
59 * Set the route mapping
60 *
61 * This function determies what should be served based on the URI request,
62 * as well as any "routes" that have been set in the routing config file.
63 *
64 * @access private
65 * @return void
66 */
67 function _set_route_mapping()
68 {
admin17a890d2006-09-27 20:42:42 +000069
70 // Are query strings enabled in the config file?
71 // If so, we're done since segment based URIs are not used with query strings.
adminb0dd10f2006-08-25 17:25:49 +000072 if ($this->config->item('enable_query_strings') === TRUE AND isset($_GET[$this->config->item('controller_trigger')]))
73 {
74 $this->set_class($_GET[$this->config->item('controller_trigger')]);
75
76 if (isset($_GET[$this->config->item('function_trigger')]))
77 {
78 $this->set_method($_GET[$this->config->item('function_trigger')]);
79 }
80
81 return;
82 }
admin592cdcb2006-09-22 18:45:42 +000083
admin17a890d2006-09-27 20:42:42 +000084 // Load the routes.php file.
admin99bccd62006-09-21 17:05:40 +000085 @include_once(APPPATH.'config/routes'.EXT);
adminb0dd10f2006-08-25 17:25:49 +000086 $this->routes = ( ! isset($route) OR ! is_array($route)) ? array() : $route;
87 unset($route);
88
admin17a890d2006-09-27 20:42:42 +000089 // Set the default controller so we can display it in the event
90 // the URI doesn't correlated to a valid controller.
admin33de9a12006-09-28 06:50:16 +000091 $this->default_controller = ( ! isset($this->routes['default_controller']) OR $this->routes['default_controller'] == '') ? FALSE : strtolower($this->routes['default_controller']);
92
admin17a890d2006-09-27 20:42:42 +000093 // Fetch the complete URI string
94 $this->uri_string = $this->_get_uri_string();
95
96 // If the URI contains only a slash we'll kill it
97 if ($this->uri_string == '/')
98 {
99 $this->uri_string = '';
100 }
101
admin592cdcb2006-09-22 18:45:42 +0000102 // Is there a URI string? If not, the default controller specified in the "routes" file will be shown.
admin17a890d2006-09-27 20:42:42 +0000103 if ($this->uri_string == '')
adminb0dd10f2006-08-25 17:25:49 +0000104 {
105 if ($this->default_controller === FALSE)
106 {
107 show_error("Unable to determine what should be displayed. A default route has not been specified in the routing file.");
108 }
109
110 $this->set_class($this->default_controller);
111 $this->set_method('index');
112
113 log_message('debug', "No URI present. Default controller set.");
114 return;
115 }
admin45c872b2006-08-26 04:51:38 +0000116 unset($this->routes['default_controller']);
adminb0dd10f2006-08-25 17:25:49 +0000117
118 // Do we need to remove the suffix specified in the config file?
119 if ($this->config->item('url_suffix') != "")
120 {
121 $this->uri_string = preg_replace("|".preg_quote($this->config->item('url_suffix'))."$|", "", $this->uri_string);
122 }
123
admin17a890d2006-09-27 20:42:42 +0000124
adminb0dd10f2006-08-25 17:25:49 +0000125 // Explode the URI Segments. The individual segments will
admin45c872b2006-08-26 04:51:38 +0000126 // be stored in the $this->segments array.
admin45c872b2006-08-26 04:51:38 +0000127 foreach(explode("/", preg_replace("|/*(.+?)/*$|", "\\1", $this->uri_string)) as $val)
128 {
129 // Filter segments for security
130 $val = trim($this->_filter_uri($val));
131
132 if ($val != '')
admine07fbb32006-08-26 17:11:01 +0000133 $this->segments[] = $val;
admin45c872b2006-08-26 04:51:38 +0000134 }
adminb0dd10f2006-08-25 17:25:49 +0000135
admine07fbb32006-08-26 17:11:01 +0000136 // Parse any custom routing that may exist
137 $this->_parse_routes();
admin45c872b2006-08-26 04:51:38 +0000138
admine07fbb32006-08-26 17:11:01 +0000139 // Re-index the segment array so that it starts with 1 rather than 0
admin99bccd62006-09-21 17:05:40 +0000140 $this->_reindex_segments();
adminb0dd10f2006-08-25 17:25:49 +0000141 }
adminb0dd10f2006-08-25 17:25:49 +0000142
143 // --------------------------------------------------------------------
144
145 /**
146 * Compile Segments
147 *
148 * This function takes an array of URI segments as
149 * input, and puts it into the $this->segments array.
150 * It also sets the current class/method
151 *
152 * @access private
153 * @param array
154 * @param bool
155 * @return void
156 */
admin45c872b2006-08-26 04:51:38 +0000157 function _compile_segments($segments = array())
158 {
159 $segments = $this->_validate_segments($segments);
adminb0dd10f2006-08-25 17:25:49 +0000160
admin45c872b2006-08-26 04:51:38 +0000161 if (count($segments) == 0)
162 {
163 return;
164 }
165
admin83b05a82006-09-25 21:06:46 +0000166 $this->set_class($segments[0]);
adminb0dd10f2006-08-25 17:25:49 +0000167
admin83b05a82006-09-25 21:06:46 +0000168 if (isset($segments[1]))
adminb0dd10f2006-08-25 17:25:49 +0000169 {
170 // A scaffolding request. No funny business with the URL
admin83b05a82006-09-25 21:06:46 +0000171 if ($this->routes['scaffolding_trigger'] == $segments[1] AND $segments[1] != '_ci_scaffolding')
adminb0dd10f2006-08-25 17:25:49 +0000172 {
173 $this->scaffolding_request = TRUE;
174 unset($this->routes['scaffolding_trigger']);
175 }
176 else
177 {
178 // A standard method request
admin83b05a82006-09-25 21:06:46 +0000179 $this->set_method($segments[1]);
adminb0dd10f2006-08-25 17:25:49 +0000180 }
181 }
admin99bccd62006-09-21 17:05:40 +0000182
183 // Update our "routed" segment array to contain the segments.
184 // Note: If there is no custom routing, this array will be
185 // identical to $this->segments
186 $this->rsegments = $segments;
adminb0dd10f2006-08-25 17:25:49 +0000187 }
adminb0dd10f2006-08-25 17:25:49 +0000188
189 // --------------------------------------------------------------------
190
191 /**
admin45c872b2006-08-26 04:51:38 +0000192 * Validates the supplied segments. Attempts to determine the path to
193 * the controller.
194 *
195 * @access private
196 * @param array
197 * @return array
198 */
199 function _validate_segments($segments)
200 {
admine07fbb32006-08-26 17:11:01 +0000201 // Does the requested controller exist in the root folder?
admin83b05a82006-09-25 21:06:46 +0000202 if (file_exists(APPPATH.'controllers/'.$segments[0].EXT))
admin45c872b2006-08-26 04:51:38 +0000203 {
admine07fbb32006-08-26 17:11:01 +0000204 return $segments;
admin45c872b2006-08-26 04:51:38 +0000205 }
admin1cf89aa2006-09-03 18:24:39 +0000206
admine07fbb32006-08-26 17:11:01 +0000207 // Is the controller in a sub-folder?
admin83b05a82006-09-25 21:06:46 +0000208 if (is_dir(APPPATH.'controllers/'.$segments[0]))
admin1cf89aa2006-09-03 18:24:39 +0000209 {
admine07fbb32006-08-26 17:11:01 +0000210 // Set the directory and remove it from the segment array
admin83b05a82006-09-25 21:06:46 +0000211 $this->set_directory($segments[0]);
admine07fbb32006-08-26 17:11:01 +0000212 $segments = array_slice($segments, 1);
213
admin1cf89aa2006-09-03 18:24:39 +0000214 if (count($segments) > 0)
215 {
216 // Does the requested controller exist in the sub-folder?
admin83b05a82006-09-25 21:06:46 +0000217 if ( ! file_exists(APPPATH.'controllers/'.$this->fetch_directory().$segments[0].EXT))
admin1cf89aa2006-09-03 18:24:39 +0000218 {
219 show_404();
220 }
221 }
222 else
admine07fbb32006-08-26 17:11:01 +0000223 {
224 $this->set_class($this->default_controller);
225 $this->set_method('index');
admin27818492006-09-05 03:31:28 +0000226
227 // Does the default controller exist in the sub-folder?
228 if ( ! file_exists(APPPATH.'controllers/'.$this->fetch_directory().$this->default_controller.EXT))
229 {
230 $this->directory = '';
231 return array();
232 }
233
admine07fbb32006-08-26 17:11:01 +0000234 }
235
236 return $segments;
237 }
238
239 // Can't find the requested controller...
240 show_404();
admin45c872b2006-08-26 04:51:38 +0000241 }
admin99bccd62006-09-21 17:05:40 +0000242
243 // --------------------------------------------------------------------
244 /**
245 * Re-index Segments
246 *
247 * This function re-indexes the $this->segment array so that it
248 * starts at 1 rather then 0. Doing so makes it simpler to
249 * use functions like $this->uri->segment(n) since there is
250 * a 1:1 relationship between the segment array and the actual segments.
251 *
252 * @access private
253 * @return void
254 */
255 function _reindex_segments()
256 {
257 // Is the routed segment array different then the main segment array?
258 $diff = (count(array_diff($this->rsegments, $this->segments)) == 0) ? FALSE : TRUE;
259
260 $i = 1;
261 foreach ($this->segments as $val)
262 {
263 $this->segments[$i++] = $val;
264 }
admin83b05a82006-09-25 21:06:46 +0000265 unset($this->segments[0]);
admin99bccd62006-09-21 17:05:40 +0000266
267 if ($diff == FALSE)
268 {
269 $this->rsegments = $this->segments;
270 }
271 else
272 {
273 $i = 1;
274 foreach ($this->rsegments as $val)
275 {
276 $this->rsegments[$i++] = $val;
277 }
admin83b05a82006-09-25 21:06:46 +0000278 unset($this->rsegments[0]);
admin99bccd62006-09-21 17:05:40 +0000279 }
280 }
admin45c872b2006-08-26 04:51:38 +0000281
282 // --------------------------------------------------------------------
283
284 /**
admin592cdcb2006-09-22 18:45:42 +0000285 * Get the URI String
286 *
287 * @access private
288 * @return string
289 */
290 function _get_uri_string()
291 {
292 if (strtoupper($this->config->item('uri_protocol')) == 'AUTO')
293 {
294 $path_info = getenv('PATH_INFO');
295 if ($path_info != '' AND $path_info != "/".SELF)
296 {
297 return $path_info;
298 }
299 else
300 {
301 $req_uri = $this->_parse_request_uri();
302
303 if ($req_uri != "")
304 {
305 return $req_uri;
306 }
307 else
308 {
309 $path_info = getenv('ORIG_PATH_INFO');
310 if ($path_info != '' AND $path_info != "/".SELF)
311 {
312 return $path_info;
313 }
314 else
315 {
316 return getenv('QUERY_STRING');
317 }
318 }
319 }
320 }
321 else
322 {
323 $uri = strtoupper($this->config->item('uri_protocol'));
324
325 if ($uri == 'REQUEST_URI')
326 {
327 return $this->_parse_request_uri();
328 }
329
330 return getenv($uri);
331 }
332 }
admin592cdcb2006-09-22 18:45:42 +0000333
334 // --------------------------------------------------------------------
335
336 /**
337 * Parse the REQUEST_URI
338 *
339 * Due to the way REQUEST_URI works it usually contains path info
340 * that makes it unusable as URI data. We'll trim off the unnecessary
341 * data, hopefully arriving at a valid URI that we can use.
342 *
343 * @access private
344 * @return string
345 */
346 function _parse_request_uri()
347 {
admindbd8aec2006-09-22 19:20:09 +0000348 if (($request_uri = getenv('REQUEST_URI')) == '')
349 {
350 return '';
351 }
352
admin592cdcb2006-09-22 18:45:42 +0000353 $fc_path = FCPATH;
354
355 if (strpos($request_uri, '?') !== FALSE)
356 {
357 $fc_path .= '?';
358 }
359
360 $parsed_uri = explode("/", preg_replace("|/(.*)|", "\\1", str_replace("\\", "/", $request_uri)));
361
362 $i = 0;
363 foreach(explode("/", $fc_path) as $segment)
364 {
365 if ($segment == $parsed_uri[$i])
366 {
367 $i++;
368 }
369 }
370
371 $parsed_uri = implode("/", array_slice($parsed_uri, $i));
372
373 if ($parsed_uri != '')
374 {
375 $parsed_uri = '/'.$parsed_uri;
376 }
377
378 return $parsed_uri;
379 }
admin592cdcb2006-09-22 18:45:42 +0000380
381 // --------------------------------------------------------------------
382
383 /**
adminb0dd10f2006-08-25 17:25:49 +0000384 * Filter segments for malicious characters
385 *
386 * @access private
387 * @param string
388 * @return string
389 */
390 function _filter_uri($str)
391 {
admin1082bdd2006-08-27 19:32:02 +0000392 if ($this->config->item('permitted_uri_chars') != '')
393 {
394 if ( ! preg_match("|^[".preg_quote($this->config->item('permitted_uri_chars'))."]+$|i", $str))
395 {
396 exit('The URI you submitted has disallowed characters: '.$str);
397 }
398 }
399 return $str;
adminb0dd10f2006-08-25 17:25:49 +0000400 }
adminb0dd10f2006-08-25 17:25:49 +0000401
402 // --------------------------------------------------------------------
403
404 /**
adminb0dd10f2006-08-25 17:25:49 +0000405 * Parse Routes
406 *
407 * This function matches any routes that may exist in
408 * the config/routes.php file against the URI to
409 * determine if the class/method need to be remapped.
410 *
411 * @access private
412 * @return void
413 */
414 function _parse_routes()
415 {
admine07fbb32006-08-26 17:11:01 +0000416 // Do we even have any custom routing to deal with?
417 if (count($this->routes) == 0)
418 {
419 $this->_compile_segments($this->segments);
420 return;
421 }
422
adminb0dd10f2006-08-25 17:25:49 +0000423 // Turn the segment array into a URI string
424 $uri = implode('/', $this->segments);
425 $num = count($this->segments);
426
427 // Is there a literal match? If so we're done
428 if (isset($this->routes[$uri]))
429 {
admin45c872b2006-08-26 04:51:38 +0000430 $this->_compile_segments(explode('/', $this->routes[$uri]));
adminb0dd10f2006-08-25 17:25:49 +0000431 return;
432 }
admine07fbb32006-08-26 17:11:01 +0000433
adminb0dd10f2006-08-25 17:25:49 +0000434 // Loop through the route array looking for wildcards
admind4e95072006-08-26 01:15:06 +0000435 foreach (array_slice($this->routes, 1) as $key => $val)
adminb071bb52006-08-26 19:28:37 +0000436 {
admind4e95072006-08-26 01:15:06 +0000437 // Convert wildcards to RegEx
438 $key = str_replace(':any', '.+', str_replace(':num', '[0-9]+', $key));
439
admin45c872b2006-08-26 04:51:38 +0000440 // Does the RegEx match?
admin71430b42006-09-15 20:29:25 +0000441 if (preg_match('#^'.$key.'$#', $uri))
admind4e95072006-08-26 01:15:06 +0000442 {
admin45c872b2006-08-26 04:51:38 +0000443 // Do we have a back-reference?
admind4e95072006-08-26 01:15:06 +0000444 if (strpos($val, '$') !== FALSE AND strpos($key, '(') !== FALSE)
445 {
admin71430b42006-09-15 20:29:25 +0000446 $val = preg_replace('#^'.$key.'$#', $val, $uri);
admind4e95072006-08-26 01:15:06 +0000447 }
448
admin45c872b2006-08-26 04:51:38 +0000449 $this->_compile_segments(explode('/', $val));
450 return;
adminb0dd10f2006-08-25 17:25:49 +0000451 }
admine07fbb32006-08-26 17:11:01 +0000452 }
453
454 // If we got this far it means we didn't encounter a
455 // matching route so we'll set the site default route
456 $this->_compile_segments($this->segments);
adminb0dd10f2006-08-25 17:25:49 +0000457 }
admin45c872b2006-08-26 04:51:38 +0000458
459 // --------------------------------------------------------------------
460
461 /**
462 * Set the class name
463 *
464 * @access public
465 * @param string
466 * @return void
467 */
468 function set_class($class)
469 {
470 $this->class = $class;
471 }
admin45c872b2006-08-26 04:51:38 +0000472
473 // --------------------------------------------------------------------
474
475 /**
476 * Fetch the current class
477 *
478 * @access public
479 * @return string
480 */
481 function fetch_class()
482 {
483 return $this->class;
484 }
admin45c872b2006-08-26 04:51:38 +0000485
486 // --------------------------------------------------------------------
487
488 /**
489 * Set the method name
490 *
491 * @access public
492 * @param string
493 * @return void
494 */
495 function set_method($method)
496 {
497 $this->method = $method;
498 }
admin45c872b2006-08-26 04:51:38 +0000499
500 // --------------------------------------------------------------------
501
502 /**
503 * Fetch the current method
504 *
505 * @access public
506 * @return string
507 */
508 function fetch_method()
509 {
510 return $this->method;
511 }
admin45c872b2006-08-26 04:51:38 +0000512
513 // --------------------------------------------------------------------
514
515 /**
516 * Set the directory name
517 *
518 * @access public
519 * @param string
520 * @return void
521 */
522 function set_directory($dir)
523 {
524 $this->directory = $dir.'/';
525 }
admin45c872b2006-08-26 04:51:38 +0000526
527 // --------------------------------------------------------------------
528
529 /**
530 * Fetch the sub-directory (if any) that contains the requested controller class
531 *
532 * @access public
533 * @return string
534 */
535 function fetch_directory()
536 {
537 return $this->directory;
538 }
admin45c872b2006-08-26 04:51:38 +0000539
adminb0dd10f2006-08-25 17:25:49 +0000540}
541// END Router Class
542?>