Skip to content
Browse files

Refactored show_404 to always apply 404_override

  • Loading branch information...
1 parent 7559d7e commit 374c2d25b1cf4acccf120695b49ef32b0435501b @dchill42 committed Aug 10, 2011
Showing with 176 additions and 143 deletions.
  1. +39 −91 system/core/CodeIgniter.php
  2. +28 −4 system/core/Exceptions.php
  3. +6 −12 system/core/Loader.php
  4. +103 −36 system/core/Router.php
View
130 system/core/CodeIgniter.php
@@ -233,6 +233,7 @@ public static function &get_instance()
include($file);
}
+ // Instantiate CodeIgniter
function &get_instance()
{
return CodeIgniter::get_instance();
@@ -268,6 +269,18 @@ function &get_instance()
/*
* ------------------------------------------------------
+ * Instantiate the output class
+ * ------------------------------------------------------
+ *
+ * Note: By instantiating Output before Router, we ensure
+ * it is available to support 404 overrides in case of a
+ * call to show_404().
+ *
+ */
+ $CI->load_core('Output');
+
+/*
+ * ------------------------------------------------------
* Instantiate the URI class
* ------------------------------------------------------
*/
@@ -289,13 +302,6 @@ function &get_instance()
/*
* ------------------------------------------------------
- * Instantiate the output class
- * ------------------------------------------------------
- */
- $CI->load_core('Output');
-
-/*
- * ------------------------------------------------------
* Is there a valid cache file? If so, we're done...
* ------------------------------------------------------
*/
@@ -336,49 +342,40 @@ function &get_instance()
$CI->load->ci_autoloader();
+ // Set a mark point for benchmarking
+ $BM->mark('loading_time:_base_classes_end');
+
/*
* ------------------------------------------------------
- * Load the local controller
+ * Is there a "pre_controller" hook?
* ------------------------------------------------------
*/
+ $EXT->_call_hook('pre_controller');
- // Load the Controller base class
- require BASEPATH.'core/Controller.php';
+/*
+ * ------------------------------------------------------
+ * Load the local controller
+ * ------------------------------------------------------
+ */
- // Load the Controller subclass, if found
- $file = 'core/'.$CI->config->item('subclass_prefix').'Controller.php';
- $packages = $CI->load->get_package_paths();
- foreach ($packages as $path)
- {
- if (file_exists($path.$file))
- {
- require $path.$file;
- break;
- }
- }
+ // Mark a start point so we can benchmark the controller
+ $BM->mark('controller_execution_time_( '.$class.' / '.$method.' )_start');
- // Load the local application controller
- // Note: The Router class automatically validates the controller path using the router->_validate_request().
- // If this include fails it means that the default controller in the Routes.php file is not resolving to something valid.
- $file = 'controllers/'.$CI->router->fetch_directory().$CI->router->fetch_class().'.php';
- $found = FALSE;
- foreach ($packages as $path)
- {
- if (file_exists($path.$file))
- {
- include($path.$file);
- $found = TRUE;
- break;
- }
- }
+ // Get the routed directory, class, and method
+ $subdir = $CI->router->fetch_directory();
+ $class = $CI->router->fetch_class();
+ $method = $CI->router->fetch_method();
- if ( ! $found)
- {
- show_error('Unable to load your default controller. Please make sure the controller specified in your Routes.php file is valid.');
- }
+ // Instantiate the controller object
+ $CI->load->controller($subdir.$class);
+ $CI->routed =& $CI->$class;
- // Set a mark point for benchmarking
- $BM->mark('loading_time:_base_classes_end');
+/*
+ * ------------------------------------------------------
+ * Is there a "post_controller_constructor" hook?
+ * ------------------------------------------------------
+ */
+ $EXT->_call_hook('post_controller_constructor');
/*
* ------------------------------------------------------
@@ -389,8 +386,6 @@ function &get_instance()
* loader class can be called via the URI, nor can
* controller functions that begin with an underscore
*/
- $class = $CI->router->fetch_class();
- $method = $CI->router->fetch_method();
if ( ! class_exists($class)
OR strncmp($method, '_', 1) == 0
@@ -402,32 +397,6 @@ function &get_instance()
/*
* ------------------------------------------------------
- * Is there a "pre_controller" hook?
- * ------------------------------------------------------
- */
- $EXT->_call_hook('pre_controller');
-
-/*
- * ------------------------------------------------------
- * Instantiate the requested controller
- * ------------------------------------------------------
- */
- // Mark a start point so we can benchmark the controller
- $BM->mark('controller_execution_time_( '.$class.' / '.$method.' )_start');
-
- $CI->routed = new $class();
- $name = strtolower($class);
- $CI->$name = $CI->routed;
-
-/*
- * ------------------------------------------------------
- * Is there a "post_controller_constructor" hook?
- * ------------------------------------------------------
- */
- $EXT->_call_hook('post_controller_constructor');
-
-/*
- * ------------------------------------------------------
* Call the requested method
* ------------------------------------------------------
*/
@@ -442,28 +411,7 @@ function &get_instance()
// methods, so we'll use this workaround for consistent behavior
if ( ! in_array(strtolower($method), array_map('strtolower', get_class_methods($CI->routed))))
{
- // Check and see if we are using a 404 override and use it.
- if ( ! empty($CI->router->routes['404_override']))
- {
- $x = explode('/', $CI->router->routes['404_override']);
- $class = $x[0];
- $method = (isset($x[1]) ? $x[1] : 'index');
- if ( ! class_exists($class))
- {
- if ( ! file_exists(APPPATH.'controllers/'.$class.'.php'))
- {
- show_404($class.'/'.$method);
- }
-
- include_once(APPPATH.'controllers/'.$class.'.php');
- unset($CI->routed);
- $CI->routed = new $class();
- }
- }
- else
- {
- show_404($class.'/'.$method);
- }
+ show_404($class.'/'.$method);
}
// Call the requested method.
View
32 system/core/Exceptions.php
@@ -83,22 +83,46 @@ function log_exception($severity, $message, $filepath, $line)
/**
* 404 Page Not Found Handler
*
+ * Calls the 404 override method if configured, or displays a generic 404 error.
+ * The 404 override method will get the requested page as its first argument,
+ * followed by any trailing segments of 404_override. So, if "foo/bar" triggered
+ * a 404, and 404_override was "my404/method/one/two", the effect would be to call:
+ * my404->method("foo/bar", "one", "two");
+ *
* @access private
* @param string
* @return string
*/
function show_404($page = '', $log_error = TRUE)
{
- $heading = "404 Page Not Found";
- $message = "The page you requested was not found.";
+ $heading = '404 Page Not Found';
+ $message = 'The page you requested was not found.';
// By default we log this, but allow a dev to skip it
if ($log_error)
{
log_message('error', '404 Page Not Found --> '.$page);
}
- echo $this->show_error($heading, $message, 'error_404', 404);
+ // Check Router for a 404 override
+ $CI =& get_instance();
+ $segments = $CI->router->override_404();
+ if ($segments === FALSE)
+ {
+ // Just display the generic 404
+ echo $this->show_error($heading, $message, 'error_404', 404);
+ }
+ else
+ {
+ // Pull off 404 class and method, and prepend requested page
+ $class = array_shift($segments);
+ $method = array_shift($segments);
+ array_unshift($segments, $page);
+
+ // Call 404 method and display output
+ call_user_func_array(array(&$CI->$class, $method), $segments);
+ $CI->output->_display();
+ }
exit;
}
@@ -175,4 +199,4 @@ function show_php_error($severity, $message, $filepath, $line)
// END Exceptions Class
/* End of file Exceptions.php */
-/* Location: ./system/core/Exceptions.php */
+/* Location: ./system/core/Exceptions.php */
View
18 system/core/Loader.php
@@ -217,6 +217,12 @@ public function controller($controller, $name = '')
show_error('The controller name you are loading is the name of a resource that is already being used: '.$name);
}
+ // Load base class(es) if not already done
+ if ( ! class_exists('CI_Controller'))
+ {
+ load_class('Controller', 'core');
+ }
+
// Search MVC paths for controller
$controller = strtolower($controller);
$file = 'controllers/'.$path.$controller.'.php';
@@ -318,19 +324,7 @@ public function model($model, $name = '', $db_conn = FALSE)
// Load base class(es) if not already done
if ( ! class_exists('CI_Model'))
{
- // Load core base class
load_class('Model', 'core');
-
- // Search for model subclass
- $file = 'core/'.$CI->config->item('subclass_prefix').'Model.php';
- foreach ($this->_ci_mvc_paths as $mod_path => $view_cascade)
- {
- if (file_exists($mod_path.$file))
- {
- require $mod_path.$file;
- break;
- }
- }
}
// Search MVC paths for model
View
139 system/core/Router.php
@@ -222,6 +222,33 @@ function _set_request($segments = array())
*/
function _validate_request($segments)
{
+ // Determine if segments point to a valid route
+ $route = $this->_route_exists($segments);
+ if ($route === FALSE)
+ {
+ // Invalid request - show a 404
+ // $segments[0] is safe, since _route_exists returns an empty
+ // $segments instead of FALSE
+ show_404($segments[0]);
+ }
+
+ return $route;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Determines if a route exists
+ *
+ * This functionality is shared between _validate_request(), which calls show_404()
+ * on failure, and override_404(), which returns control to show_404() on failure.
+ *
+ * @access private
+ * @param array route segments
+ * @return mixed FALSE if route doesn't exist, otherwise array of segments
+ */
+ function _route_exists($segments)
+ {
if (count($segments) == 0)
{
return $segments;
@@ -233,68 +260,53 @@ function _validate_request($segments)
// Does the requested controller exist in the base folder?
if (file_exists($path.'controllers/'.$segments[0].'.php'))
{
+ // Found it - clear directory and return segments
+ $this->set_directory('');
return $segments;
}
-
+
// Is the controller in a sub-folder?
if (is_dir($path.'controllers/'.$segments[0]))
{
- // Set the directory and remove it from the segment array
- $this->set_directory($segments[0]);
- $segments = array_slice($segments, 1);
-
- if (count($segments) > 0)
+ // Found a sub-folder - is there a controller name?
+ if (count($segments) > 1)
{
// Does the requested controller exist in the sub-folder?
- if ( ! file_exists($path.'controllers/'.$this->fetch_directory().$segments[0].'.php'))
+ if (file_exists($path.'controllers/'.$segments[0].$segments[1].'.php'))
{
- show_404($this->fetch_directory().$segments[0]);
+ // Found it - set directory and return remaining segments
+ $this->set_directory($segments[0]);
+ return array_slice($segments, 1);
}
}
else
{
- // Is the method being specified in the route?
+ // Try the default controller - does it specify a method?
if (strpos($this->default_controller, '/') !== FALSE)
{
- $x = explode('/', $this->default_controller);
-
- $this->set_class($x[0]);
- $this->set_method($x[1]);
+ list($class, $method) = explode('/', $this->default_controller, 2);
}
else
{
- $this->set_class($this->default_controller);
- $this->set_method('index');
+ $class = $this->default_controller;
+ $method = 'index';
}
// Does the default controller exist in the sub-folder?
- if ( ! file_exists($path.'controllers/'.$this->fetch_directory().$this->default_controller.'.php'))
+ if (file_exists($path.'controllers/'.$segments[0].$class.'.php'))
{
- $this->directory = '';
+ // Yes - set directory, default controller class, and method and return empty segments
+ $this->set_directory($segments[0]);
+ $this->set_class($class);
+ $this->set_method($method);
return array();
}
}
-
- return $segments;
}
}
-
- // If we've gotten this far it means that the URI does not correlate to a valid
- // controller class. We will now see if there is an override
- if ( ! empty($this->routes['404_override']))
- {
- $x = explode('/', $this->routes['404_override']);
-
- $this->set_class($x[0]);
- $this->set_method(isset($x[1]) ? $x[1] : 'index');
-
- return $x;
- }
-
-
- // Nothing else to do at this point but show a 404
- show_404($segments[0]);
+ // If we got here, no valid route was found
+ return FALSE;
}
// --------------------------------------------------------------------
@@ -433,6 +445,62 @@ function fetch_directory()
// --------------------------------------------------------------------
/**
+ * Get 404 override
+ *
+ * Identifies the 404 override route, if defined, and validates it.
+ * On success, the explicit class, method, and any remaining URL segments
+ * are returned as an array.
+ *
+ * @access public
+ * @return mixed array of segments on success, otherwise FALSE
+ */
+ function override_404()
+ {
+ // See if 404_override is defined
+ if (empty($this->routes['404_override']))
+ {
+ // No override to apply
+ return FALSE;
+ }
+
+ // Validate override path
+ $segments = $this->_route_exists(explode('/', $this->routes['404_override']));
+ if (empty($segments))
+ {
+ // Override not found
+ return FALSE;
+ }
+
+ // _route_exists set the directory accordingly - get the class and method
+ $subdir = $this->directory;
+ $class = $segments[0];
+ if (isset($segments[1]))
+ {
+ // Method provided
+ $method = $segments[1];
+ }
+ else
+ {
+ // Use default method and add to segments
+ $method = 'index';
+ $segments[] = $method;
+ }
+
+ // Load the 404 Controller and check the method
+ $this->CI->load->controller($subdir.$class);
+ if (in_array(strtolower($method), array_map('strtolower', get_class_methods($this->CI->$class))))
+ {
+ // Success!
+ return $segments;
+ }
+
+ // No dice
+ return FALSE;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
* Set the controller overrides
*
* @access public
@@ -463,7 +531,6 @@ function _set_overrides($routing)
}
}
-
}
// END Router Class

0 comments on commit 374c2d2

Please sign in to comment.
Something went wrong with that request. Please try again.