Hooks.php CI Explained

I’ve stopped work on this series to start a new one for CI 3.

Introduction

Hooks.php is a core CodeIgniter class. This post is part of a series which explains the CodeIgniter (CI) source code. This post explains the CI 2.1.3 Hooks.php.

./system/core/Hooks.php

Related: ./application/config/hooks.php

Also see: Hooks – Extending the Framework Core

The Script’s Boilerplate

<?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');
/**
 * CodeIgniter
 *
 * An open source application development framework for PHP 5.1.6 or newer
 *
 * @package		CodeIgniter
 * @author		ExpressionEngine Dev Team
 * @copyright	Copyright (c) 2008 - 2011, EllisLab, Inc.
 * @license		http://codeigniter.com/user_guide/license.html
 * @link		http://codeigniter.com
 * @since		Version 1.0
 * @filesource
 */

// ------------------------------------------------------------------------

/**
 * CodeIgniter Hooks Class
 *
 * Provides a mechanism to extend the base system without hacking.
 *
 * @package		CodeIgniter
 * @subpackage	Libraries
 * @category	Libraries
 * @author		ExpressionEngine Dev Team
 * @link		http://codeigniter.com/user_guide/libraries/encryption.html
 */
class CI_Hooks {

Declaration of Properties

/**
 * Determines wether hooks are enabled
 *
 * @var bool
 */
var $enabled		= FALSE;
/**
 * List of all hooks set in config/hooks.php
 *
 * @var array
 */
var $hooks			= array();
/**
 * Determines wether hook is in progress, used to prevent infinte loops
 *
 * @var bool
 */
var $in_progress	= FALSE;

Code: __construct()

	function __construct()
	{
		$this->_initialize();
		log_message('debug', "Hooks Class Initialized");
	}

Any class method named _initialize gets coded so as to assign the values of the main class properties. In this case those are $enabled $hooks $in_progress .

Code: _initialize()

/**
 * Initialize the Hooks Preferences
 *
 * @access	private
 * @return	void
 */
function _initialize()
{
	$CFG =& load_class('Config', 'core');

	// If hooks are not enabled in the config file
	// there is nothing else to do

	if ($CFG->item('enable_hooks') == FALSE)
	{
		return;
	}

	// Grab the "hooks" definition file.
	// If there are no hooks, we're done.

	if (defined('ENVIRONMENT') AND is_file(APPPATH.'config/'.ENVIRONMENT.'/hooks.php'))
	{
	    include(APPPATH.'config/'.ENVIRONMENT.'/hooks.php');
	}
	elseif (is_file(APPPATH.'config/hooks.php'))
	{
		include(APPPATH.'config/hooks.php');
	}


	if ( ! isset($hook) OR ! is_array($hook))
	{
		return;
	}

	$this->hooks =& $hook;
	$this->enabled = TRUE;
}

breaking-it-down

$CFG =& load_class('Config', 'core');

Assign (by-reference) $CFG the singleton object for Config class.

The load_class function takes a simple class name as its first parameter. In this case the actually class which will be loaded will most likely be CI_Config.

		// If hooks are not enabled in the config file
		// there is nothing else to do

		if ($CFG->item('enable_hooks') == FALSE)
		{
			return;
		}

There’s no need to set the $enabled property to FALSE because that’s its default declaration value.

		// Grab the "hooks" definition file.
		// If there are no hooks, we're done.

		if (defined('ENVIRONMENT') AND is_file(APPPATH.'config/'.ENVIRONMENT.'/hooks.php'))
		{
		    include(APPPATH.'config/'.ENVIRONMENT.'/hooks.php');
		}
		elseif (is_file(APPPATH.'config/hooks.php'))
		{
			include(APPPATH.'config/hooks.php');
		}


		if ( ! isset($hook) OR ! is_array($hook))
		{
			return;
		}

		$this->hooks =& $hook;
		$this->enabled = TRUE;

There is a configuration file which has the definitions of the hooks which a developer may have. This file is ./application/config/hooks.php. See: Hooks – Extending the Framework Core

Code: _call_hook()

	/**
	 * Call Hook
	 *
	 * Calls a particular hook
	 *
	 * @access	private
	 * @param	string	the hook name
	 * @return	mixed
	 */
	function _call_hook($which = '')
	{
		if ( ! $this->enabled OR ! isset($this->hooks[$which]))
		{
			return FALSE;
		}

		if (isset($this->hooks[$which][0]) AND is_array($this->hooks[$which][0]))
		{
			foreach ($this->hooks[$which] as $val)
			{
				$this->_run_hook($val);
			}
		}
		else
		{
			$this->_run_hook($this->hooks[$which]);
		}

		return TRUE;
	}

Some background information:

The hooks property is a by-reference property pointing to the hook array established by ./application/config/hooks.php . Therefore, hooks has an element for each hook point name which the developer chose to configure. If the developer enabled that hook point name he/she could have configured one hook for it or multiple hooks for it. If he/she configured one hook for the hook point name then the array element for that hook point name will be an array of configuration settings for that one hook. On the other hand if the developer configured multiple hooks for the hook point name then the array element for that hook point name will be an array containing multiple hooks—each of which has it’s own array for configuration.

What _call_hook() does:

  1. Returns FALSE if hooks are disabled (or if the hook point name is not configured.)
  2. If the hooks array element for the hook point name has as its first element an array then there’s multiple hooks for this hook point name; therefore, apply _run_hook() on each hook.
  3. Otherwise, apply _run_hook() on the single hook associated with the hook point name.

The parameter for _run_hook() will an array containing the configuration items for a hook.

Code: _run_hook()

	/**
	 * Run Hook
	 *
	 * Runs a particular hook
	 *
	 * @access	private
	 * @param	array	the hook details
	 * @return	bool
	 */
	function _run_hook($data)
	{
		if ( ! is_array($data))
		{
			return FALSE;
		}

		// -----------------------------------
		// Safety - Prevents run-away loops
		// -----------------------------------

		// If the script being called happens to have the same
		// hook call within it a loop can happen

		if ($this->in_progress == TRUE)
		{
			return;
		}

		// -----------------------------------
		// Set file path
		// -----------------------------------

		if ( ! isset($data['filepath']) OR ! isset($data['filename']))
		{
			return FALSE;
		}

		$filepath = APPPATH.$data['filepath'].'/'.$data['filename'];

		if ( ! file_exists($filepath))
		{
			return FALSE;
		}

		// -----------------------------------
		// Set class/function name
		// -----------------------------------

		$class		= FALSE;
		$function	= FALSE;
		$params		= '';

		if (isset($data['class']) AND $data['class'] != '')
		{
			$class = $data['class'];
		}

		if (isset($data['function']))
		{
			$function = $data['function'];
		}

		if (isset($data['params']))
		{
			$params = $data['params'];
		}

		if ($class === FALSE AND $function === FALSE)
		{
			return FALSE;
		}

		// -----------------------------------
		// Set the in_progress flag
		// -----------------------------------

		$this->in_progress = TRUE;

		// -----------------------------------
		// Call the requested class and/or function
		// -----------------------------------

		if ($class !== FALSE)
		{
			if ( ! class_exists($class))
			{
				require($filepath);
			}

			$HOOK = new $class;
			$HOOK->$function($params);
		}
		else
		{
			if ( ! function_exists($function))
			{
				require($filepath);
			}

			$function($params);
		}

		$this->in_progress = FALSE;
		return TRUE;
	}

}

Generally this is what _run_hook() does (sequentially:)

  1. takes array $data as a parameter. This array is the hook configuration.
  2. $filepath becomes the path to the hook script
  3. $class becomes the class name or FALSE
  4. $function becomes the function name or FALSE
  5. $params becomes an array for parameters; or $params becomes an empty string.
  6. if both $class and $function are FALSE then it aborts
  7. set the in_progress flag to TRUE.
  8. if the hook is a class then execute the script ([if the class does not exist — ] to make the class exist), instantiate an object of that class, and call the method whose name is the value of $function passing it the array $params.
  9. otherwise (the hook is a function,) execute the script ([if the function does not exist — ] to make the function exist) and call the function whose name is the value of $function (passing it the array $params.)
  10. return the value of the property in_progress to FALSE
Advertisements

About samehramzylabib

See About on https://samehramzylabib.wordpress.com
This entry was posted in CI Source Code Explained. Bookmark the permalink.

One Response to Hooks.php CI Explained

  1. Pingback: Bootstrap Source Code for CI Explained | Sam's PHP

Comment

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s