Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

executable file 629 lines (552 sloc) 15.615 kb
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
/**
* CodeIgniter
*
* An open source application development framework for PHP 5.2.4 or newer
*
* NOTICE OF LICENSE
*
* Licensed under the Open Software License version 3.0
*
* This source file is subject to the Open Software License (OSL 3.0) that is
* bundled with this package in the files license.txt / license.rst. It is
* also available through the world wide web at this URL:
* http://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to obtain it
* through the world wide web, please send an email to
* licensing@ellislab.com so we can send you a copy immediately.
*
* @package CodeIgniter
* @author EllisLab Dev Team
* @copyright Copyright (c) 2008 - 2012, EllisLab, Inc. (http://ellislab.com/)
* @license http://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* @link http://codeigniter.com
* @since Version 1.0
* @filesource
*/
/**
* Router Class
*
* Parses URIs and determines routing
*
* @package CodeIgniter
* @subpackage Libraries
* @category Libraries
* @author EllisLab Dev Team
* @link http://codeigniter.com/user_guide/general/routing.html
*/
class CI_Router {
/**
* CodeIgniter core
*
* @var object
*/
protected $CI;
/**
* List of routes
*
* @var array
*/
public $routes = array();
/**
* Stack of route data
*
* @var array
*/
protected $route_stack;
/**
* Default controller (and method if specific)
*
* @var string
*/
protected $default_controller;
/**
* Route stack index constants
*/
const SEG_PATH = 0;
const SEG_SUBDIR = 1;
const SEG_CLASS = 2;
const SEG_METHOD = 3;
const SEG_ARGS = 4;
/**
* Constructor
*
* Runs the route mapping function.
*
* @return void
*/
public function __construct()
{
$this->CI =& get_instance();
$this->route_stack = array('', '', '', '');
log_message('debug', 'Router Class Initialized');
}
// --------------------------------------------------------------------
/**
* Set the route mapping
*
* This function determines what should be served based on the URI request,
* as well as any "routes" that have been set in the routing config file.
*
* @return void
*/
public function _set_routing()
{
// Load the routes.php file.
$route = $this->CI->config->get('routes.php', 'route');
// Set route remapping
$this->routes = is_array($route) ? $route : array();
unset($route);
// Set the default controller so we can display it in the event
// the URI doesn't correlate to a valid controller.
$this->default_controller = empty($this->routes['default_controller']) ? FALSE :
strtolower($this->routes['default_controller']);
// Are query strings enabled in the config file? Normally CI doesn't utilize query strings
// since URI segments are more search-engine friendly, but they can optionally be used.
// If this feature is enabled, we will gather the directory/class/method a little differently
$ctl_trigger = $this->CI->config->item('controller_trigger');
if ($this->CI->config->item('enable_query_strings') === TRUE && isset($_GET[$ctl_trigger]))
{
$segments = array();
// Add directory segment if provided
$dir_trigger = $this->CI->config->item('directory_trigger');
if (isset($_GET[$dir_trigger]))
{
$segments[] = trim($this->CI->uri->_filter_uri($_GET[$dir_trigger]));
}
// Add controller segment - this was qualified above
$class = trim($this->CI->uri->_filter_uri($_GET[$ctl_trigger]));
$segments[] = $class;
// Add function segment if provided
$fun_trigger = $this->CI->config->item('function_trigger');
if (isset($_GET[$fun_trigger]))
{
$segments[] = trim($this->CI->uri->_filter_uri($_GET[$fun_trigger]));
}
}
else
{
// Fetch the complete URI string, remove the suffix, and explode
$this->CI->uri->_fetch_uri_string();
$this->CI->uri->_remove_url_suffix();
$this->CI->uri->_explode_segments();
$segments = $this->CI->uri->segments;
}
// Set the route stack
$this->_set_request($segments);
}
// --------------------------------------------------------------------
/**
* Set the Route
*
* This function takes an array of URI segments as
* input, and sets the current class/method
*
* @param array Route segments
* @return void
*/
protected function _set_request(array $segments)
{
// Determine if segments point to a valid route
$route = $this->validate_route($segments);
if ($route === FALSE)
{
// Invalid request - show a 404
$page = isset($segments[0]) ? $segments[0] : '';
show_404($page);
}
// Set route stack and clean directory and class
$this->route_stack = $route;
$this->set_directory($route[self::SEG_SUBDIR]);
$this->set_class($route[self::SEG_CLASS]);
// Update our "routed" segment array to contain the segments without the path or directory.
// Note: If there is no custom routing, this array will be
// identical to $this->CI->uri->segments
$this->CI->uri->rsegments = array_slice($route, self::SEG_CLASS);
// Re-index the segment array so that it starts with 1 rather than 0
$this->CI->uri->_reindex_segments();
}
// --------------------------------------------------------------------
/**
* Validates the supplied segments.
*
* This function attempts to determine the path to the controller.
* On success, a complete array of at least 4 segments is returned:
* array(
* 0 => $path, // Package path where Controller found
* 1 => $subdir, // Subdirectory, which may be ''
* 2 => $class, // Validated Controller class
* 3 => $method, // Method, which may be 'index'
* ... // Any remaining segments
* );
*
* @access public
* @param array route segments
* @return mixed FALSE if route doesn't exist, otherwise array of 4+ segments
*/
public function validate_route($route)
{
// If we don't have any segments, the default will have to do
if (empty($route))
{
$route = $this->_default_segments();
if (empty($route))
{
// No default - fail
return FALSE;
}
}
// Explode route if not already segmented
if ( ! is_array($route))
{
$route = explode('/', $route);
}
// Parse any custom routing that may exist
$route = $this->_parse_routes($route);
// Search paths for controller
foreach ($this->CI->load->get_package_paths() as $path)
{
// Does the requested controller exist in the base folder?
if (file_exists($path.'controllers/'.$route[0].'.php'))
{
// Found it - append method if missing
if ( ! isset($route[1]))
{
$route[] = 'index';
}
// Prepend path and empty directory and return
return array_merge(array($path, ''), $route);
}
// Is the controller in a sub-folder?
if (is_dir($path.'controllers/'.$route[0]))
{
// Found a sub-folder - is there a controller name?
if (isset($route[1]))
{
// Yes - get class and method
$class = $route[1];
$method = isset($route[2]) ? $route[2] : 'index';
}
else
{
// Get default controller segments
$default = $this->_default_segments();
if (empty($default))
{
// No default controller to apply - carry on
unset($default);
continue;
}
// Get class and method
$class = array_shift($default);
$method = array_shift($default);
}
// Does the requested controller exist in the sub-folder?
if (file_exists($path.'controllers/'.$route[0].'/'.$class.'.php'))
{
// Found it - assemble segments
if ( ! isset($route[1]))
{
$route[] = $class;
}
if ( ! isset($route[2]))
{
$route[] = $method;
}
if (isset($default) && count($default) > 0)
{
$route = array_merge($route, $default);
}
// Prepend path and return
array_unshift($route, $path);
return $route;
}
}
}
// Search for controller in modules hierarchy
// If the module path is APPPATH/modules/, "/foo/bar/baz" may map to:
// APPPATH/modules/controllers/foo.php
// APPPATH/modules/foo/controllers/bar.php
// APPPATH/modules/foo/bar/controllers/baz.php
// APPPATH/modules/foo/bar/baz/controllers/[default].php
$seg_ct = count($route);
foreach ($this->CI->load->get_module_paths() as $path)
{
// Does the requested controller exist in the base folder?
if (file_exists($path.'controllers/'.$route[0].'.php'))
{
// Found it - append method if missing
if ($seg_ct < 2)
{
$route[] = 'index';
}
// Prepend path and empty directory and return
return array_merge(array($path, ''), $route);
}
// Is there a module sub-folder?
$sub = '';
for ($seg = 0; $seg < $seg_ct; ++$seg)
{
// Add segment to subdirectory path and check for controllers
$sub .= $route[$seg].'/';
if (is_dir($path.$sub.'controllers/'))
{
// Found a module - is there a controller name?
if ($seg_ct > $seg + 1)
{
// Yes - get class and method
$class = $route[$seg + 1];
$method = $seg_ct > $seg + 2 ? $route[$seg + 2] : 'index';
}
else
{
// Get default controller segments
$default = $this->_default_segments();
if (empty($default))
{
// No default controller to apply - carry on
unset($default);
continue;
}
// Get class and method
$class = array_shift($default);
$method = array_shift($default);
}
// Does the requested controller exist in the module?
if (file_exists($path.$sub.'controllers/'.$class.'.php'))
{
// Found it - assemble segments
if ($seg_ct <= $seg + 1)
{
$route[] = $class;
}
if ($seg_ct <= $seg + 2)
{
$route[] = $method;
}
if (isset($default) && count($default) > 0)
{
$route = array_merge($route, $default);
}
// Return path, empty (post-)subdirectory, and remainder of route
$route = array_merge(array($path.$sub, ''), array_slice($route, $seg + 1));
return $route;
}
}
}
}
// If we got here, no valid route was found
return FALSE;
}
// --------------------------------------------------------------------
/**
* Parse Routes
*
* This function matches any routes that may exist in
* the config/routes.php file against the URI to
* determine if the class/method need to be remapped.
*
* @param array Route segments
* @return array Remapped segments
*/
protected function _parse_routes(array $segments)
{
// Turn the segment array into a URI string
$uri = implode('/', $segments);
// Is there a literal match? If so we're done
if (isset($this->routes[$uri]))
{
return explode('/', $this->routes[$uri]);
}
// Loop through the route array looking for wild-cards
foreach ($this->routes as $key => $val)
{
// Convert wild-cards to RegEx
$key = str_replace(array(':any', ':num'), array('.+', '[0-9]+'), $key);
// Does the RegEx match?
if (preg_match('#^'.$key.'$#', $uri))
{
// Do we have a back-reference?
if (strpos($val, '$') !== FALSE && strpos($key, '(') !== FALSE)
{
$val = preg_replace('#^'.$key.'$#', $val, $uri);
}
return explode('/', $val);
}
}
// If we got this far it means we didn't encounter a
// matching route so we'll return the segments unchanged
return $segments;
}
// --------------------------------------------------------------------
/**
* Set the class name
*
* @param string
* @return void
*/
public function set_class($class)
{
$this->route_stack[self::SEG_CLASS] = str_replace(array('/', '.'), '', $class);
}
// --------------------------------------------------------------------
/**
* Fetch the current class
*
* @return string
*/
public function fetch_class()
{
return $this->route_stack[self::SEG_CLASS];
}
// --------------------------------------------------------------------
/**
* Set the method name
*
* @param string
* @return void
*/
public function set_method($method)
{
$this->route_stack[self::SEG_METHOD] = $method;
}
// --------------------------------------------------------------------
/**
* Fetch the current method
*
* @return string
*/
public function fetch_method()
{
$method = $this->route_stack[self::SEG_METHOD];
return ($method === $this->fetch_class()) ? 'index' : $method;
}
// --------------------------------------------------------------------
/**
* Set the directory name
*
* @param string
* @return void
*/
public function set_directory($dir)
{
$this->route_stack[self::SEG_SUBDIR] = $dir == '' ? '' : str_replace(array('/', '.'), '', $dir).'/';
}
// --------------------------------------------------------------------
/**
* Fetch the sub-directory (if any) that contains the requested controller class
*
* @return string
*/
public function fetch_directory()
{
return $this->route_stack[self::SEG_SUBDIR];
}
// --------------------------------------------------------------------
/**
* Set the package path
*
* @param string
* @return void
*/
public function set_path($path)
{
$this->route_stack[self::SEG_PATH] = $path;
}
// --------------------------------------------------------------------
/**
* Fetch the current package path
*
* @return string
*/
public function fetch_path()
{
return $this->route_stack[self::SEG_PATH];
}
// --------------------------------------------------------------------
/**
* Fetch the current route stack
*
* @return array
*/
public function fetch_route()
{
return $this->route_stack;
}
// --------------------------------------------------------------------
/**
* Get error route
*
* Identifies the 404 or error override route, if defined, and validates it.
*
* @param string Override route name
* @return mixed FALSE if route doesn't exist, otherwise array of 4+ segments
*/
public function get_error_route($route)
{
// See if override is defined
if (empty($this->routes[$route])) {
// No override to apply
return FALSE;
}
// Return validated override path
return $this->validate_route($this->routes[$route]);
}
// --------------------------------------------------------------------
/**
* Set the controller overrides
*
* @param array
* @return void
*/
public function _set_overrides($routing)
{
if ( ! is_array($routing))
{
return;
}
if (isset($routing['path']))
{
$this->set_path($routing['path']);
}
if (isset($routing['directory']))
{
$this->set_directory($routing['directory']);
}
if ( ! empty($routing['controller']))
{
$this->set_class($routing['controller']);
}
if (isset($routing['function']))
{
$routing['function'] = ($routing['function'] == '') ? 'index' : $routing['function'];
$this->set_method($routing['function']);
}
}
// --------------------------------------------------------------------
/**
* Get segments of default controller
*
* @access protected
* @return array array of segments
*/
protected function _default_segments()
{
// Check for default controller
if (empty($this->default_controller))
{
// Return empty array
return array();
}
// Break out default controller
$default = explode('/', $this->default_controller);
if ( ! isset($default[1]))
{
// Add default method
$default[] = 'index';
}
return $default;
}
}
/* End of file Router.php */
/* Location: ./system/core/Router.php */
Jump to Line
Something went wrong with that request. Please try again.