Browse files

Completed CI class unit test and added README

  • Loading branch information...
1 parent 3ae52e6 commit 11f0e9e29ed0facedcbe5565bad19af40fcb3092 @dchill42 committed Sep 4, 2012
@@ -0,0 +1,157 @@
+# CodeIgniter System Core #
+### Introduction:
+As of CodeIgniter 3.0, the core has been adapted to include HMVC support
+and a number of related features. HMVC stands for *Heirarchical Model
+View Controller*, and in the simplest terms, this means you can call other
+controllers from the controller routed by the request.
+### Subcontrollers:
+The first change that goes with this feature is that the Loader class now
+offers a controller() method for loading sub-controllers. This method
+accepts a URI-type string to identify what method of which controller to
+run, much like you would find in your browser's address bar.
+Any routes defined in your routes.php configuration file are applied to the
+controller path, and the default "index" method is added if a method name is
+not identified. Here are some examples of handing off control to a sub-controller:
+ $this->load->controller('subhandler'); // Loads and calls Subhandler->index()
+ $this->load->controller('handle/task'); // Loads and calls Handle->task()
+ $this->load->controller('weirdness', 'normal'); // Loads Weirdness as 'normal' and calls index()
+ $this->load->controller('notyet', '', FALSE); // Loads Notyet, but does not call a method
+In addition, controllers may be loaded out of the application directory or any
+paths added with the Loader add_package_path() method. Another new feature is
+that package paths may be located in the PHP include directories, if you have
+access to them. Paths relative to includes are resolved at the time they are added.
+### CodeIgniter Object:
+In order to allow more than one controller to be loaded at a time, the heart of
+the system had to be separated from the Controller class. As a result, we now
+have the CodeIgniter class, from which we create the single, central core of
+the application. This is the object to which all other loaded objects are
+attached, including controllers.
+Now, from inside a controller, you won't be able to tell the difference.
+Everything you are used to finding under $this - the core class objects,
+libraries you've lodaed, etc. - are all still accessible in exactly the
+same way. However, there are a few subtle differences. In reality, your
+controller now has a $this->CI member pointing to the CodeIgniter core,
+and anything not directly attached to your controller is automatically
+searched for there instead. This means it is now infinitessimally slower
+to call $this->load (or whatever) than $this->CI->load. With some of the
+other performance enhancements in the core, you probably couldn't measure
+the difference at runtime, but calling $this->CI is a good practice to adopt
+for the future.
+### Routed Controller:
+There is now a special object name to identify the original controller routed
+by a request (in a world where you can load as many controllers as you want).
+$this->CI->routed points to the original, which is also attached as its class
+name. This allows libraries and other controllers to identify who's in charge.
+### Config Files:
+In an effort to consolidate the machinations of loading config files, the
+Config class now offers two new methods:
+ $this->CI->Config->get('myconfig.php', 'config');
+This one loads the file (like "myconfig.php") and returns the specified
+array (such as $config). Files with the given name located in the config
+subdirectory of the application directory and all package paths are all loaded
+and their contents merged. This is the method used for all the config files
+loaded in the core classes and libraries, with the exception of config.php and
+autoload.php, which are singly read out of the application config directory
+during system bootstrapping.
+This one allows you to get extra variables to go with the main array in a
+config file:
+ $extras = array();
+ $this->CI->Config->get_ext('multiconfig.php', 'myvars', $extras);
+In addition to returning the $myvars array from multiconfig.php, the $extras
+array in this example will be populated with the names and values of all other
+variables encountered in the file(s). This is the variant used to read
+database.php, retrieving both the $db array and values like the active group.
+### Exceptions:
+Custom routes (those URI controller paths) can now be set in routes.php to
+direct errors and 404 messages to a controller for display. This gives infinite
+flexibility over error messages - far more than just the default templates.
+## Bootstrapping and Application Flow:
+These new features have necessitated some changes in the bootstrapping process.
+To the application's controllers these will be undetectable, but for the
+detail-minded, here is the sequence of events:
+* Assess environment, paths, and overrides (routing and assign_to_config)
+ in index.php and set constants (just as always)
+* Read config.php and autoload.php files from the application path
+* Apply assign_to_config overrides if present
+* Apply autoload package paths
+* Load CodeIgniter extension class (from application or autoloaded package
+ paths) if present
+* Instantiate the CodeIgniter object
+* Register the exception handler
+* Disable magic quotes for PHP < 5.4
+* Load Benchmark class
+* Mark total execution start time
+* Mark base class loading start time
+* Load Config class and pass the core config items established during
+ bootloading (including assign_to_config overrides)
+* Read constants.php file(s) from all the application/package paths
+* Load autoload config files
+* Load Hooks class
+* Call pre-system hook
+* Load Loader class and pass the base and application path lists with
+ autoloaded package paths applied
+* Load Utf8 class
+* Load URI class
+* Load Output class (to be prepared for 404 output)
+* Load Router class, set routing, and apply routing overrides
+* Call cache-override hook, and if not overridden, check for cache
+* If a valid cache is found, send it to Output and jump to the
+ display-override hook below
+* Load Security class
+* Load Input class
+* Load Lang class
+* Load autoload helpers, languages, libraries, drivers, controllers, and models
+ (in that order, and don't run controllers)
+* Mark base class loading end time
+* Call pre-controller hook
+* Mark controller execution start time
+* Load the routed controller (or 404 if not found)
+* Call post-controller-constructor hook
+* Call routed controller method (or remap) (or 404 if not found)
+* Mark controller execution end time
+* Call post-controller hook
+* Call display-override hook, and if not overridden, display output
+* Call post-system hook
+All core classes (now including Log) may be extended by classes with the
+configured subclass prefix existing anywhere in the package paths.
+The Log class no longer reads its own config items directly, but rather is
+passed all core config items starting with "log" found in config.php during
+bootloading (and subject to assign_to_config). This prevents any dependencies
+on other core classes so Log can be loaded at virtually any point in the
+process (such as at the end of the CodeIgniter constructor). It also prevents
+having to reload the same file that has already been processed.
+## Unit Testing
+Finally, the new CodeIgniter class, which internalizes the sequence listed
+above, is fully accessible and subject to unit testing. An exhaustive test case
+has been developed to verify each step and the sequence of events. Furthermore,
+all of that is done in isolation without dependence upon the other core classes.
@@ -83,6 +83,14 @@ class CodeIgniter {
protected $_is_running = FALSE;
+ * Display cache flag
+ *
+ * @access protected
+ * @var bool
+ */
+ protected $_display_cache = FALSE;
+ /**
* CodeIgniter singleton instance
* @access protected
@@ -339,10 +347,13 @@ protected static function _get_class()
* @param string Exit message
* @return void
- protected static function _status_exit($status, $msg)
+ protected static function _status_exit($status = 0, $msg = '')
// Set status header and exit with message
- set_status_header($status);
+ if ($status)
+ {
+ set_status_header($status);
+ }
@@ -376,9 +387,10 @@ public function load_core_class($class, $obj_name = '')
if ( ! class_exists($name))
// Look for file in core paths
+ $filenm = 'core/'.$class.'.php';
foreach ($this->base_paths as $path)
- $file = $path.'core/'.$class.'.php';
+ $file = $path.$filenm;
if (file_exists($file))
@@ -403,9 +415,10 @@ public function load_core_class($class, $obj_name = '')
// Look for file in extension paths
+ $filenm = 'core/'.$ext.'.php';
foreach ($this->app_paths as $path)
- $file = $path.'core/'.$this->subclass_prefix.$class.'.php';
+ $file = $path.$filenm;
if (file_exists($file))
@@ -521,6 +534,11 @@ public function call_controller($class, $method, array $args = array(), $name =
* Run the CodeIgniter application
+ * This function employs a simple mutex pattern to prevent recursive calls.
+ * By breaking down the run sequence, it makes it easier to override parts
+ * of the sequence in an extension class, and to unit test the operations
+ * in smaller chunks.
+ *
* @return void
public function run()
@@ -568,9 +586,8 @@ protected function _load_base()
- // Get Config and load constants
+ // Get Config
- $this->config->get('constants.php', NULL);
// Load the hooks class and call pre_system (depends on Config)
@@ -602,7 +619,7 @@ protected function _load_routing()
// Load the output class (depends on Config)
- // Note: By load Output before Router, we ensure it is available to support
+ // Note: By loading Output before Router, we ensure it is available to support
// 404 overrides in case of a call to show_404().
@@ -619,7 +636,8 @@ protected function _load_routing()
// Is there a valid cache file? If so, we're done...
if ($this->hooks->_call_hook('cache_override') === FALSE && $this->output->_display_cache() === TRUE)
- exit;
+ $this->_display_cache = TRUE;
+ static::_status_exit();
@@ -662,8 +680,9 @@ protected function _run_controller()
// Get the parsed route and identify class, method, and arguments
+ $rtr_class = get_class($this->router);
$route = $this->router->fetch_route();
- $args = array_slice($route, this_Router::SEG_CLASS);
+ $args = array_slice($route, $rtr_class::SEG_CLASS);
$class = strtolower(array_shift($args));
$method = array_shift($args);
@@ -692,23 +711,35 @@ protected function _run_controller()
* Finalize bencharks and hooks and send output
+ * This gets run from the destructor, so we check the existence of everything
+ * we need as opposed to assuming it's loaded.
+ *
* @access protected
* @return void
protected function _finalize()
// Check for Benchmark class
- if (isset($this->benchmark))
+ if (! $this->_display_cache && isset($this->benchmark) && isset($this->router))
+ // Get the parsed route and identify class and method
+ $rtr_class = get_class($this->router);
+ $route = $this->router->fetch_route();
+ $class = strtolower($route[$rtr_class::SEG_CLASS]);
+ $method = $route[$rtr_class::SEG_METHOD];
// Mark a benchmark end point
$this->benchmark->mark('controller_execution_time_( '.$class.' / '.$method.' )_end');
// Check for Hooks class
if (isset($this->hooks))
- // Call post_controller hook
- $this->hooks->_call_hook('post_controller');
+ // Call post_controller hook unless we're displaying a cache
+ if ( ! $this->_display_cache)
+ {
+ $this->hooks->_call_hook('post_controller');
+ }
// Send the final rendered output to the browser
if ($this->hooks->_call_hook('display_override') === FALSE && isset($this->output))
@@ -778,8 +809,6 @@ public function _exception_handler($severity, $message, $filepath, $line)
* Global function to get CodeIgniter instance
- * DEPRECATED - call CodeIgniter::instance() directly
- *
* @return object CodeIgniter instance
if ( ! function_exists('get_instance'))
@@ -86,6 +86,9 @@ public function __construct()
// Determine array merge function
$this->merge_arrays = is_php('5.3') ? 'array_replace_recursive' : array($this, '_merge_arrays');
+ // Establish any configured constants
+ $this->get('constants.php', NULL);
// Autoload any other config files
$autoload = $CI->_autoload;
if (is_array($autoload) && isset($autoload['config']))
Oops, something went wrong.

0 comments on commit 11f0e9e

Please sign in to comment.