<?php

/*
=====================================================
 ExpressionEngine - by pMachine
-----------------------------------------------------
 http://www.pmachine.com/
-----------------------------------------------------
 Copyright (c) 2003 - 2007 pMachine, Inc.
=====================================================
 THIS IS COPYRIGHTED SOFTWARE
 PLEASE READ THE LICENSE AGREEMENT
 http://eedocs.pmachine.com/license.html
=====================================================
 File: core.extensions.php
-----------------------------------------------------
 Purpose: Controls Any EE Extensions.
=====================================================
*/



if ( ! defined('EXT'))
{
    exit('Invalid file request');
}


class Extensions {  
   
    var $extensions 	= array();
    var $OBJ			= array();		// Current Instantiated Object
    var $end_script		= FALSE;	// To return or not to return
    var $last_call		= FALSE;	// The data returned from the last called method for this hook
    var $in_progress	= '';		// Last hook called.  Prevents loops.
    
    var $s_cache	= array();	// Array of previously unserialized settings
  
	/** ---------------------------------------
    /**  Contstructor
    /** ---------------------------------------*/
    
    function Extensions()
  	{
  		global $DB, $PREFS;
  		
  		if ($PREFS->ini('allow_extensions') == 'y')
  		{
  			$query = $DB->query("SELECT DISTINCT ee.* FROM exp_extensions ee WHERE enabled = 'y' ORDER BY hook, priority ASC, class");
  			
  			if ($query->num_rows > 0)
  			{
  				$this->extensions = array();
  				
  				foreach($query->result as $row)
  				{
  					// There is a possiblity that there will be three extensions for a given
  					// hook and that two of them will call the same class but different methods
  					// while the third will have a priority that places it between those two.
  					// The chance is pretty remote and I cannot think offhand why someone
  					// would do this, but I have learned that our users and developers are
  					// a crazy bunch so I should make shite like this work initially and not
  					// just fix it later.
  					
  					// However, it makes no sense for a person to call the same class but different
  					// methods for the same hook at the same priority.  I feel confident in this.
  					// If someone does do this I will just have to point out the fact that they
  					// are a complete nutter.
  					
  					$this->extensions[$row['hook']][$row['priority']][$row['class']] = array($row['method'], $row['settings'], $row['version']);
  				}
  			}
  		}
  	}
  	/* END */
	
	
	/** ---------------------------------------
    /**  Handles the calling of extensions
    /** ---------------------------------------*/

	function call_extension($which, $args='')
	{
		global $PREFS, $REGX, $FNS, $OUT;
		
		/** -----------------------------
		/**  Reset Our Variables
		/** -----------------------------*/
		
		$this->end_script	= FALSE;
		$this->last_call	= FALSE;
		
		/** -----------------------------
		/**  Anything to Do Here?
		/** -----------------------------*/
		
		if ( ! isset($this->extensions[$which])) return;
		if ($PREFS->ini('allow_extensions') != 'y') return;
		if ($this->in_progress == $which) return;
		
		$this->in_progress = $which;
		
		/** -----------------------------
		/**  Retrieve arguments for function
		/** -----------------------------*/
		
		$args = array_slice(func_get_args(), 1);
		
		/** ------------------------------------------
		/**  Go through all the calls for this hook
		/** ------------------------------------------*/
		
		foreach($this->extensions[$which] as $priority => $calls)
		{
			foreach($calls as $class => $metadata)
			{
				/** ---------------------------------
				/**  Determine Path of Extension
				/** ---------------------------------*/
				
				$class_name = ucfirst($class);
				$path = PATH_EXT.'ext.'.$FNS->filename_security(strtolower($class)).EXT;
			
				if ( ! file_exists($path))
				{
					$error = 'Unable to load the following extension file:<br /><br />'.'ext.'.$FNS->filename_security(strtolower($class)).EXT;
					return $OUT->fatal_error($error);
				}
				
				/** --------------------------------
				/**  Include File
				/** --------------------------------*/
			
				if ( ! class_exists($class_name))
    	    	{
    	        	require $path;
    	    	}
    	    	
    	    	/** --------------------------------
				/**  A Bit of Meta
				/** --------------------------------*/
    	    	
    	    	$method	= $metadata['0'];
    	    	
    	    	// Unserializing and serializing is relatively slow, so we 
    	    	// cache the settings just in case multiple hooks are calling the 
    	    	// same extension multiple times during a single page load.  
    	    	// Thus, speeding it all up a bit.
    	    	
    	    	if (isset($this->s_cache[$class_name]))
    	    	{
    	    		$settings = $this->s_cache[$class_name];
    	    	}
    	    	else
    	    	{
    	    		$settings = ($metadata['1'] == '') ? '' : $REGX->array_stripslashes(unserialize($metadata['1']));
    	    		$this->s_cache[$class_name] = $settings;
    	    	}
    	    	
    	    	$version = $metadata['2'];
				
				// -----------------------------------------
    	    	//  Call the class(s)
    	    	//
    	    	//  Each method could easily have its own settings,
    	    	//  so we have to send the settings each time
    	    	// -----------------------------------------
    	    	
    	    	$this->OBJ[$class_name] = new $class_name($settings);
    	    	
    	    	/** -----------------------------------------
    	    	/**  Update Extension First?
    	    	/** -----------------------------------------*/
    	    	
    	    	if ($version != $this->OBJ[$class_name]->version && method_exists($this->OBJ[$class_name], 'update_extension') === TRUE)
    	    	{
    	    		$update = call_user_func_array(array(&$this->OBJ[$class_name], 'update_extension'), array($version));
    	    	}
    	    	
    	    	// -----------------------------------------
    	    	//  Call Method and Store Returned Data
    	    	//
    	    	//  We put this in a class variable so that any extensions 
    	    	//  called after this one can retrieve the returned data from
    	    	//  previous methods and view/maniuplate that returned data
    	    	//  opposed to any original arguments the hook sent. In theory...
    	    	// -----------------------------------------
    	    	
				$this->last_call = call_user_func_array(array(&$this->OBJ[$class_name], $method), $args);
				
				$this->in_progress = '';
				
				// --------------------------------
				//  A $EXT->end_script value of TRUE means that the called 
				//	method wishes us to stop the calling of the main script.
				//  In this case, even if there are methods after this one for
				//  the hook we still stop the script now because extensions with
				//  a higher priority call the shots and thus override any 
				//  extensions with a lower priority.
				// --------------------------------
				
				if ($this->end_script === TRUE) return $this->last_call;
			}
		}
		
		/** --------------------------
		/**  Bottom's Up!
		/** --------------------------*/
		
		return $this->last_call;
	}
	/* END */
	
	/** ---------------------------------------
    /**  Check If Hook Has Activated Extension
    /** ---------------------------------------*/

	function active_hook($which)
	{
		if (isset($this->extensions[$which])) return TRUE;
		
		return FALSE;
    }
    /* END */

}
// END CLASS
?>