Subversion-Projekte lars-tiefland.ci

Revision

Revision 68 | Revision 2242 | Zur aktuellen Revision | Details | Vergleich mit vorheriger | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
68 lars 1
<?php
2
/**
3
 * CodeIgniter
4
 *
5
 * An open source application development framework for PHP
6
 *
7
 * This content is released under the MIT License (MIT)
8
 *
2049 lars 9
 * Copyright (c) 2014 - 2017, British Columbia Institute of Technology
68 lars 10
 *
11
 * Permission is hereby granted, free of charge, to any person obtaining a copy
12
 * of this software and associated documentation files (the "Software"), to deal
13
 * in the Software without restriction, including without limitation the rights
14
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15
 * copies of the Software, and to permit persons to whom the Software is
16
 * furnished to do so, subject to the following conditions:
17
 *
18
 * The above copyright notice and this permission notice shall be included in
19
 * all copies or substantial portions of the Software.
20
 *
21
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27
 * THE SOFTWARE.
28
 *
29
 * @package	CodeIgniter
30
 * @author	EllisLab Dev Team
31
 * @copyright	Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
2049 lars 32
 * @copyright	Copyright (c) 2014 - 2017, British Columbia Institute of Technology (http://bcit.ca/)
68 lars 33
 * @license	http://opensource.org/licenses/MIT	MIT License
34
 * @link	https://codeigniter.com
35
 * @since	Version 1.0.0
36
 * @filesource
37
 */
38
defined('BASEPATH') OR exit('No direct script access allowed');
39
 
40
/**
41
 * Router Class
42
 *
43
 * Parses URIs and determines routing
44
 *
45
 * @package		CodeIgniter
46
 * @subpackage	Libraries
47
 * @category	Libraries
48
 * @author		EllisLab Dev Team
49
 * @link		https://codeigniter.com/user_guide/general/routing.html
50
 */
51
class CI_Router {
52
 
53
	/**
54
	 * CI_Config class object
55
	 *
56
	 * @var	object
57
	 */
58
	public $config;
59
 
60
	/**
61
	 * List of routes
62
	 *
63
	 * @var	array
64
	 */
65
	public $routes =	array();
66
 
67
	/**
68
	 * Current class name
69
	 *
70
	 * @var	string
71
	 */
72
	public $class =		'';
73
 
74
	/**
75
	 * Current method name
76
	 *
77
	 * @var	string
78
	 */
79
	public $method =	'index';
80
 
81
	/**
82
	 * Sub-directory that contains the requested controller class
83
	 *
84
	 * @var	string
85
	 */
86
	public $directory;
87
 
88
	/**
89
	 * Default controller (and method if specific)
90
	 *
91
	 * @var	string
92
	 */
93
	public $default_controller;
94
 
95
	/**
96
	 * Translate URI dashes
97
	 *
98
	 * Determines whether dashes in controller & method segments
99
	 * should be automatically replaced by underscores.
100
	 *
101
	 * @var	bool
102
	 */
103
	public $translate_uri_dashes = FALSE;
104
 
105
	/**
106
	 * Enable query strings flag
107
	 *
108
	 * Determines whether to use GET parameters or segment URIs
109
	 *
110
	 * @var	bool
111
	 */
112
	public $enable_query_strings = FALSE;
113
 
114
	// --------------------------------------------------------------------
115
 
116
	/**
117
	 * Class constructor
118
	 *
119
	 * Runs the route mapping function.
120
	 *
121
	 * @param	array	$routing
122
	 * @return	void
123
	 */
124
	public function __construct($routing = NULL)
125
	{
126
		$this->config =& load_class('Config', 'core');
127
		$this->uri =& load_class('URI', 'core');
128
 
129
		$this->enable_query_strings = ( ! is_cli() && $this->config->item('enable_query_strings') === TRUE);
130
 
131
		// If a directory override is configured, it has to be set before any dynamic routing logic
132
		is_array($routing) && isset($routing['directory']) && $this->set_directory($routing['directory']);
133
		$this->_set_routing();
134
 
135
		// Set any routing overrides that may exist in the main index file
136
		if (is_array($routing))
137
		{
138
			empty($routing['controller']) OR $this->set_class($routing['controller']);
139
			empty($routing['function'])   OR $this->set_method($routing['function']);
140
		}
141
 
142
		log_message('info', 'Router Class Initialized');
143
	}
144
 
145
	// --------------------------------------------------------------------
146
 
147
	/**
148
	 * Set route mapping
149
	 *
150
	 * Determines what should be served based on the URI request,
151
	 * as well as any "routes" that have been set in the routing config file.
152
	 *
153
	 * @return	void
154
	 */
155
	protected function _set_routing()
156
	{
157
		// Load the routes.php file. It would be great if we could
158
		// skip this for enable_query_strings = TRUE, but then
159
		// default_controller would be empty ...
160
		if (file_exists(APPPATH.'config/routes.php'))
161
		{
162
			include(APPPATH.'config/routes.php');
163
		}
164
 
165
		if (file_exists(APPPATH.'config/'.ENVIRONMENT.'/routes.php'))
166
		{
167
			include(APPPATH.'config/'.ENVIRONMENT.'/routes.php');
168
		}
169
 
170
		// Validate & get reserved routes
171
		if (isset($route) && is_array($route))
172
		{
173
			isset($route['default_controller']) && $this->default_controller = $route['default_controller'];
174
			isset($route['translate_uri_dashes']) && $this->translate_uri_dashes = $route['translate_uri_dashes'];
175
			unset($route['default_controller'], $route['translate_uri_dashes']);
176
			$this->routes = $route;
177
		}
178
 
179
		// Are query strings enabled in the config file? Normally CI doesn't utilize query strings
180
		// since URI segments are more search-engine friendly, but they can optionally be used.
181
		// If this feature is enabled, we will gather the directory/class/method a little differently
182
		if ($this->enable_query_strings)
183
		{
184
			// If the directory is set at this time, it means an override exists, so skip the checks
185
			if ( ! isset($this->directory))
186
			{
187
				$_d = $this->config->item('directory_trigger');
188
				$_d = isset($_GET[$_d]) ? trim($_GET[$_d], " \t\n\r\0\x0B/") : '';
189
 
190
				if ($_d !== '')
191
				{
192
					$this->uri->filter_uri($_d);
193
					$this->set_directory($_d);
194
				}
195
			}
196
 
197
			$_c = trim($this->config->item('controller_trigger'));
198
			if ( ! empty($_GET[$_c]))
199
			{
200
				$this->uri->filter_uri($_GET[$_c]);
201
				$this->set_class($_GET[$_c]);
202
 
203
				$_f = trim($this->config->item('function_trigger'));
204
				if ( ! empty($_GET[$_f]))
205
				{
206
					$this->uri->filter_uri($_GET[$_f]);
207
					$this->set_method($_GET[$_f]);
208
				}
209
 
210
				$this->uri->rsegments = array(
211
					1 => $this->class,
212
					2 => $this->method
213
				);
214
			}
215
			else
216
			{
217
				$this->_set_default_controller();
218
			}
219
 
220
			// Routing rules don't apply to query strings and we don't need to detect
221
			// directories, so we're done here
222
			return;
223
		}
224
 
225
		// Is there anything to parse?
226
		if ($this->uri->uri_string !== '')
227
		{
228
			$this->_parse_routes();
229
		}
230
		else
231
		{
232
			$this->_set_default_controller();
233
		}
234
	}
235
 
236
	// --------------------------------------------------------------------
237
 
238
	/**
239
	 * Set request route
240
	 *
241
	 * Takes an array of URI segments as input and sets the class/method
242
	 * to be called.
243
	 *
244
	 * @used-by	CI_Router::_parse_routes()
245
	 * @param	array	$segments	URI segments
246
	 * @return	void
247
	 */
248
	protected function _set_request($segments = array())
249
	{
250
		$segments = $this->_validate_request($segments);
251
		// If we don't have any segments left - try the default controller;
252
		// WARNING: Directories get shifted out of the segments array!
253
		if (empty($segments))
254
		{
255
			$this->_set_default_controller();
256
			return;
257
		}
258
 
259
		if ($this->translate_uri_dashes === TRUE)
260
		{
261
			$segments[0] = str_replace('-', '_', $segments[0]);
262
			if (isset($segments[1]))
263
			{
264
				$segments[1] = str_replace('-', '_', $segments[1]);
265
			}
266
		}
267
 
268
		$this->set_class($segments[0]);
269
		if (isset($segments[1]))
270
		{
271
			$this->set_method($segments[1]);
272
		}
273
		else
274
		{
275
			$segments[1] = 'index';
276
		}
277
 
278
		array_unshift($segments, NULL);
279
		unset($segments[0]);
280
		$this->uri->rsegments = $segments;
281
	}
282
 
283
	// --------------------------------------------------------------------
284
 
285
	/**
286
	 * Set default controller
287
	 *
288
	 * @return	void
289
	 */
290
	protected function _set_default_controller()
291
	{
292
		if (empty($this->default_controller))
293
		{
294
			show_error('Unable to determine what should be displayed. A default route has not been specified in the routing file.');
295
		}
296
 
297
		// Is the method being specified?
298
		if (sscanf($this->default_controller, '%[^/]/%s', $class, $method) !== 2)
299
		{
300
			$method = 'index';
301
		}
302
 
303
		if ( ! file_exists(APPPATH.'controllers/'.$this->directory.ucfirst($class).'.php'))
304
		{
305
			// This will trigger 404 later
306
			return;
307
		}
308
 
309
		$this->set_class($class);
310
		$this->set_method($method);
311
 
312
		// Assign routed segments, index starting from 1
313
		$this->uri->rsegments = array(
314
			1 => $class,
315
			2 => $method
316
		);
317
 
318
		log_message('debug', 'No URI present. Default controller set.');
319
	}
320
 
321
	// --------------------------------------------------------------------
322
 
323
	/**
324
	 * Validate request
325
	 *
326
	 * Attempts validate the URI request and determine the controller path.
327
	 *
328
	 * @used-by	CI_Router::_set_request()
329
	 * @param	array	$segments	URI segments
330
	 * @return	mixed	URI segments
331
	 */
332
	protected function _validate_request($segments)
333
	{
334
		$c = count($segments);
335
		$directory_override = isset($this->directory);
336
 
337
		// Loop through our segments and return as soon as a controller
338
		// is found or when such a directory doesn't exist
339
		while ($c-- > 0)
340
		{
341
			$test = $this->directory
342
				.ucfirst($this->translate_uri_dashes === TRUE ? str_replace('-', '_', $segments[0]) : $segments[0]);
343
 
344
			if ( ! file_exists(APPPATH.'controllers/'.$test.'.php')
345
				&& $directory_override === FALSE
346
				&& is_dir(APPPATH.'controllers/'.$this->directory.$segments[0])
347
			)
348
			{
349
				$this->set_directory(array_shift($segments), TRUE);
350
				continue;
351
			}
352
 
353
			return $segments;
354
		}
355
 
356
		// This means that all segments were actually directories
357
		return $segments;
358
	}
359
 
360
	// --------------------------------------------------------------------
361
 
362
	/**
363
	 * Parse Routes
364
	 *
365
	 * Matches any routes that may exist in the config/routes.php file
366
	 * against the URI to determine if the class/method need to be remapped.
367
	 *
368
	 * @return	void
369
	 */
370
	protected function _parse_routes()
371
	{
372
		// Turn the segment array into a URI string
373
		$uri = implode('/', $this->uri->segments);
374
 
375
		// Get HTTP verb
376
		$http_verb = isset($_SERVER['REQUEST_METHOD']) ? strtolower($_SERVER['REQUEST_METHOD']) : 'cli';
377
 
378
		// Loop through the route array looking for wildcards
379
		foreach ($this->routes as $key => $val)
380
		{
381
			// Check if route format is using HTTP verbs
382
			if (is_array($val))
383
			{
384
				$val = array_change_key_case($val, CASE_LOWER);
385
				if (isset($val[$http_verb]))
386
				{
387
					$val = $val[$http_verb];
388
				}
389
				else
390
				{
391
					continue;
392
				}
393
			}
394
 
395
			// Convert wildcards to RegEx
396
			$key = str_replace(array(':any', ':num'), array('[^/]+', '[0-9]+'), $key);
397
 
398
			// Does the RegEx match?
399
			if (preg_match('#^'.$key.'$#', $uri, $matches))
400
			{
401
				// Are we using callbacks to process back-references?
402
				if ( ! is_string($val) && is_callable($val))
403
				{
404
					// Remove the original string from the matches array.
405
					array_shift($matches);
406
 
407
					// Execute the callback using the values in matches as its parameters.
408
					$val = call_user_func_array($val, $matches);
409
				}
410
				// Are we using the default routing method for back-references?
411
				elseif (strpos($val, '$') !== FALSE && strpos($key, '(') !== FALSE)
412
				{
413
					$val = preg_replace('#^'.$key.'$#', $val, $uri);
414
				}
415
 
416
				$this->_set_request(explode('/', $val));
417
				return;
418
			}
419
		}
420
 
421
		// If we got this far it means we didn't encounter a
422
		// matching route so we'll set the site default route
423
		$this->_set_request(array_values($this->uri->segments));
424
	}
425
 
426
	// --------------------------------------------------------------------
427
 
428
	/**
429
	 * Set class name
430
	 *
431
	 * @param	string	$class	Class name
432
	 * @return	void
433
	 */
434
	public function set_class($class)
435
	{
436
		$this->class = str_replace(array('/', '.'), '', $class);
437
	}
438
 
439
	// --------------------------------------------------------------------
440
 
441
	/**
442
	 * Fetch the current class
443
	 *
444
	 * @deprecated	3.0.0	Read the 'class' property instead
445
	 * @return	string
446
	 */
447
	public function fetch_class()
448
	{
449
		return $this->class;
450
	}
451
 
452
	// --------------------------------------------------------------------
453
 
454
	/**
455
	 * Set method name
456
	 *
457
	 * @param	string	$method	Method name
458
	 * @return	void
459
	 */
460
	public function set_method($method)
461
	{
462
		$this->method = $method;
463
	}
464
 
465
	// --------------------------------------------------------------------
466
 
467
	/**
468
	 * Fetch the current method
469
	 *
470
	 * @deprecated	3.0.0	Read the 'method' property instead
471
	 * @return	string
472
	 */
473
	public function fetch_method()
474
	{
475
		return $this->method;
476
	}
477
 
478
	// --------------------------------------------------------------------
479
 
480
	/**
481
	 * Set directory name
482
	 *
483
	 * @param	string	$dir	Directory name
484
	 * @param	bool	$append	Whether we're appending rather than setting the full value
485
	 * @return	void
486
	 */
487
	public function set_directory($dir, $append = FALSE)
488
	{
489
		if ($append !== TRUE OR empty($this->directory))
490
		{
491
			$this->directory = str_replace('.', '', trim($dir, '/')).'/';
492
		}
493
		else
494
		{
495
			$this->directory .= str_replace('.', '', trim($dir, '/')).'/';
496
		}
497
	}
498
 
499
	// --------------------------------------------------------------------
500
 
501
	/**
502
	 * Fetch directory
503
	 *
504
	 * Feches the sub-directory (if any) that contains the requested
505
	 * controller class.
506
	 *
507
	 * @deprecated	3.0.0	Read the 'directory' property instead
508
	 * @return	string
509
	 */
510
	public function fetch_directory()
511
	{
512
		return $this->directory;
513
	}
514
 
515
}