blob: 2219f5739b5225fd44910de894f80fe050ab0416 [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();
34 var $routes = array();
35 var $class = '';
36 var $method = 'index';
admin45c872b2006-08-26 04:51:38 +000037 var $directory = '';
adminb0dd10f2006-08-25 17:25:49 +000038 var $uri_protocol = 'auto';
39 var $default_controller;
40 var $scaffolding_request = FALSE; // Must be set to FALSE
41
42 /**
43 * Constructor
44 *
45 * Runs the route mapping function.
46 */
47 function CI_Router()
48 {
49 $this->config =& _load_class('CI_Config');
50 $this->_set_route_mapping();
51 log_message('debug', "Router Class Initialized");
52 }
53
54 // --------------------------------------------------------------------
55
56 /**
57 * Set the route mapping
58 *
59 * This function determies what should be served based on the URI request,
60 * as well as any "routes" that have been set in the routing config file.
61 *
62 * @access private
63 * @return void
64 */
65 function _set_route_mapping()
66 {
67 // Are query strings enabled? If so we're done...
68 if ($this->config->item('enable_query_strings') === TRUE AND isset($_GET[$this->config->item('controller_trigger')]))
69 {
70 $this->set_class($_GET[$this->config->item('controller_trigger')]);
71
72 if (isset($_GET[$this->config->item('function_trigger')]))
73 {
74 $this->set_method($_GET[$this->config->item('function_trigger')]);
75 }
76
77 return;
78 }
79
80 // Load the routes.php file
81 include_once(APPPATH.'config/routes'.EXT);
82 $this->routes = ( ! isset($route) OR ! is_array($route)) ? array() : $route;
83 unset($route);
84
85 // Set the default controller
86 $this->default_controller = ( ! isset($this->routes['default_controller']) OR $this->routes['default_controller'] == '') ? FALSE : strtolower($this->routes['default_controller']);
87
88 // Fetch the URI string Depending on the server,
89 // the URI will be available in one of two globals
adminb071bb52006-08-26 19:28:37 +000090 if ($this->config->item('uri_protocol') == 'auto')
adminb0dd10f2006-08-25 17:25:49 +000091 {
adminb071bb52006-08-26 19:28:37 +000092 $path_info = getenv('PATH_INFO');
93 if ($path_info != '' AND $path_info != "/".SELF)
94 {
95 $this->uri_string = $path_info;
96 }
97 else
98 {
99 $path_info = getenv('ORIG_PATH_INFO');
100 if ($path_info != '' AND $path_info != "/".SELF)
101 {
102 $this->uri_string = $path_info;
103 }
104 else
105 {
106 $this->uri_string = getenv('QUERY_STRING');
107 }
108 }
adminb0dd10f2006-08-25 17:25:49 +0000109 }
adminb071bb52006-08-26 19:28:37 +0000110 else
111 {
112 $this->uri_string = getenv(strtoupper($this->config->item('uri_protocol')));
113 }
114
adminb0dd10f2006-08-25 17:25:49 +0000115
116 // Is there a URI string? If not, the default controller specified
117 // by the admin in the "routes" file will be shown.
118 if ($this->uri_string == '')
119 {
120 if ($this->default_controller === FALSE)
121 {
122 show_error("Unable to determine what should be displayed. A default route has not been specified in the routing file.");
123 }
124
125 $this->set_class($this->default_controller);
126 $this->set_method('index');
127
128 log_message('debug', "No URI present. Default controller set.");
129 return;
130 }
admin45c872b2006-08-26 04:51:38 +0000131 unset($this->routes['default_controller']);
adminb0dd10f2006-08-25 17:25:49 +0000132
133 // Do we need to remove the suffix specified in the config file?
134 if ($this->config->item('url_suffix') != "")
135 {
136 $this->uri_string = preg_replace("|".preg_quote($this->config->item('url_suffix'))."$|", "", $this->uri_string);
137 }
138
139 // Explode the URI Segments. The individual segments will
admin45c872b2006-08-26 04:51:38 +0000140 // be stored in the $this->segments array.
admin45c872b2006-08-26 04:51:38 +0000141 foreach(explode("/", preg_replace("|/*(.+?)/*$|", "\\1", $this->uri_string)) as $val)
142 {
143 // Filter segments for security
144 $val = trim($this->_filter_uri($val));
145
146 if ($val != '')
admine07fbb32006-08-26 17:11:01 +0000147 $this->segments[] = $val;
admin45c872b2006-08-26 04:51:38 +0000148 }
adminb0dd10f2006-08-25 17:25:49 +0000149
admine07fbb32006-08-26 17:11:01 +0000150 // Parse any custom routing that may exist
151 $this->_parse_routes();
admin45c872b2006-08-26 04:51:38 +0000152
admine07fbb32006-08-26 17:11:01 +0000153 // Re-index the segment array so that it starts with 1 rather than 0
154 $i = 1;
155 foreach ($this->segments as $val)
adminb0dd10f2006-08-25 17:25:49 +0000156 {
admine07fbb32006-08-26 17:11:01 +0000157 $this->segments[$i++] = $val;
adminb0dd10f2006-08-25 17:25:49 +0000158 }
admine07fbb32006-08-26 17:11:01 +0000159 unset($this->segments['0']);
adminb0dd10f2006-08-25 17:25:49 +0000160 }
161 // END _set_route_mapping()
162
163 // --------------------------------------------------------------------
164
165 /**
166 * Compile Segments
167 *
168 * This function takes an array of URI segments as
169 * input, and puts it into the $this->segments array.
170 * It also sets the current class/method
171 *
172 * @access private
173 * @param array
174 * @param bool
175 * @return void
176 */
admin45c872b2006-08-26 04:51:38 +0000177 function _compile_segments($segments = array())
178 {
179 $segments = $this->_validate_segments($segments);
adminb0dd10f2006-08-25 17:25:49 +0000180
admin45c872b2006-08-26 04:51:38 +0000181 if (count($segments) == 0)
182 {
183 return;
184 }
185
admine07fbb32006-08-26 17:11:01 +0000186 $this->set_class($segments['0']);
adminb0dd10f2006-08-25 17:25:49 +0000187
admine07fbb32006-08-26 17:11:01 +0000188 if (isset($segments['1']))
adminb0dd10f2006-08-25 17:25:49 +0000189 {
190 // A scaffolding request. No funny business with the URL
admine07fbb32006-08-26 17:11:01 +0000191 if ($this->routes['scaffolding_trigger'] == $segments['1'] AND $segments['1'] != '_ci_scaffolding')
adminb0dd10f2006-08-25 17:25:49 +0000192 {
193 $this->scaffolding_request = TRUE;
194 unset($this->routes['scaffolding_trigger']);
195 }
196 else
197 {
198 // A standard method request
admine07fbb32006-08-26 17:11:01 +0000199 $this->set_method($segments['1']);
adminb0dd10f2006-08-25 17:25:49 +0000200 }
201 }
adminb0dd10f2006-08-25 17:25:49 +0000202 }
203 // END _compile_segments()
204
205 // --------------------------------------------------------------------
206
207 /**
admin45c872b2006-08-26 04:51:38 +0000208 * Validates the supplied segments. Attempts to determine the path to
209 * the controller.
210 *
211 * @access private
212 * @param array
213 * @return array
214 */
215 function _validate_segments($segments)
216 {
admine07fbb32006-08-26 17:11:01 +0000217 // Does the requested controller exist in the root folder?
218 if (file_exists(APPPATH.'controllers/'.$segments['0'].EXT))
admin45c872b2006-08-26 04:51:38 +0000219 {
admine07fbb32006-08-26 17:11:01 +0000220 return $segments;
admin45c872b2006-08-26 04:51:38 +0000221 }
222
admine07fbb32006-08-26 17:11:01 +0000223 // Is the controller in a sub-folder?
224 if (is_dir(APPPATH.'controllers/'.$segments['0']))
225 {
226 // Set the directory and remove it from the segment array
227 $this->set_directory($segments['0']);
228 $segments = array_slice($segments, 1);
229
230 if (count($segments) == 0)
231 {
232 $this->set_class($this->default_controller);
233 $this->set_method('index');
234 $this->directory = '';
235 return array();
236 }
237
238 return $segments;
239 }
240
241 // Can't find the requested controller...
242 show_404();
admin45c872b2006-08-26 04:51:38 +0000243 }
244 // END _validate_segments()
245
246 // --------------------------------------------------------------------
247
248 /**
adminb0dd10f2006-08-25 17:25:49 +0000249 * Filter segments for malicious characters
250 *
251 * @access private
252 * @param string
253 * @return string
254 */
255 function _filter_uri($str)
256 {
admin1082bdd2006-08-27 19:32:02 +0000257 if ($this->config->item('permitted_uri_chars') != '')
258 {
259 if ( ! preg_match("|^[".preg_quote($this->config->item('permitted_uri_chars'))."]+$|i", $str))
260 {
261 exit('The URI you submitted has disallowed characters: '.$str);
262 }
263 }
264 return $str;
adminb0dd10f2006-08-25 17:25:49 +0000265 }
266 // END _filter_uri()
267
268 // --------------------------------------------------------------------
269
270 /**
adminb0dd10f2006-08-25 17:25:49 +0000271 * Parse Routes
272 *
273 * This function matches any routes that may exist in
274 * the config/routes.php file against the URI to
275 * determine if the class/method need to be remapped.
276 *
277 * @access private
278 * @return void
279 */
280 function _parse_routes()
281 {
admine07fbb32006-08-26 17:11:01 +0000282 // Do we even have any custom routing to deal with?
283 if (count($this->routes) == 0)
284 {
285 $this->_compile_segments($this->segments);
286 return;
287 }
288
adminb0dd10f2006-08-25 17:25:49 +0000289 // Turn the segment array into a URI string
290 $uri = implode('/', $this->segments);
291 $num = count($this->segments);
292
293 // Is there a literal match? If so we're done
294 if (isset($this->routes[$uri]))
295 {
admin45c872b2006-08-26 04:51:38 +0000296 $this->_compile_segments(explode('/', $this->routes[$uri]));
adminb0dd10f2006-08-25 17:25:49 +0000297 return;
298 }
admine07fbb32006-08-26 17:11:01 +0000299
adminb0dd10f2006-08-25 17:25:49 +0000300 // Loop through the route array looking for wildcards
admind4e95072006-08-26 01:15:06 +0000301 foreach (array_slice($this->routes, 1) as $key => $val)
adminb071bb52006-08-26 19:28:37 +0000302 {
admind4e95072006-08-26 01:15:06 +0000303 // Convert wildcards to RegEx
304 $key = str_replace(':any', '.+', str_replace(':num', '[0-9]+', $key));
305
admin45c872b2006-08-26 04:51:38 +0000306 // Does the RegEx match?
admind4e95072006-08-26 01:15:06 +0000307 if (preg_match('|^'.$key.'$|', $uri))
308 {
admin45c872b2006-08-26 04:51:38 +0000309 // Do we have a back-reference?
admind4e95072006-08-26 01:15:06 +0000310 if (strpos($val, '$') !== FALSE AND strpos($key, '(') !== FALSE)
311 {
312 $val = preg_replace('|^'.$key.'$|', $val, $uri);
313 }
314
admin45c872b2006-08-26 04:51:38 +0000315 $this->_compile_segments(explode('/', $val));
316 return;
adminb0dd10f2006-08-25 17:25:49 +0000317 }
admine07fbb32006-08-26 17:11:01 +0000318 }
319
320 // If we got this far it means we didn't encounter a
321 // matching route so we'll set the site default route
322 $this->_compile_segments($this->segments);
adminb0dd10f2006-08-25 17:25:49 +0000323 }
324 // END set_method()
admin45c872b2006-08-26 04:51:38 +0000325
326 // --------------------------------------------------------------------
327
328 /**
329 * Set the class name
330 *
331 * @access public
332 * @param string
333 * @return void
334 */
335 function set_class($class)
336 {
337 $this->class = $class;
338 }
339 // END set_class()
340
341 // --------------------------------------------------------------------
342
343 /**
344 * Fetch the current class
345 *
346 * @access public
347 * @return string
348 */
349 function fetch_class()
350 {
351 return $this->class;
352 }
353 // END fetch_class()
354
355 // --------------------------------------------------------------------
356
357 /**
358 * Set the method name
359 *
360 * @access public
361 * @param string
362 * @return void
363 */
364 function set_method($method)
365 {
366 $this->method = $method;
367 }
368 // END set_method()
369
370 // --------------------------------------------------------------------
371
372 /**
373 * Fetch the current method
374 *
375 * @access public
376 * @return string
377 */
378 function fetch_method()
379 {
380 return $this->method;
381 }
382 // END fetch_method()
383
384 // --------------------------------------------------------------------
385
386 /**
387 * Set the directory name
388 *
389 * @access public
390 * @param string
391 * @return void
392 */
393 function set_directory($dir)
394 {
395 $this->directory = $dir.'/';
396 }
397 // END set_directory()
398
399 // --------------------------------------------------------------------
400
401 /**
402 * Fetch the sub-directory (if any) that contains the requested controller class
403 *
404 * @access public
405 * @return string
406 */
407 function fetch_directory()
408 {
409 return $this->directory;
410 }
411 // END fetch_directory()
412
adminb0dd10f2006-08-25 17:25:49 +0000413}
414// END Router Class
415?>