<?php
//
// $Header: /cvsroot/my-php/lib/plugins-manager.inc.php,v 1.6 2005/04/14 12:51:00 zaufi Exp $
//

// This script may only be included - so its better to die if called directly.
if (strpos($_SERVER['SCRIPT_NAME'], basename(__FILE__)) != false)
{
    header('location: index.php');
    die;
}

$__mfmr_redefined = true;
if (!defined('MODULE_FILENAME_MATCH_REGEX'))
{
    define('MODULE_FILENAME_MATCH_REGEX', '(.*)(\.(module|plugin|class))\.php');
    $__mfmr_redefined = false;
}
if (!defined('MODULE_NAME_VALUE_PART_NUMBER'))
{
    if (!$__mfmr_redefined)
        define('MODULE_NAME_VALUE_PART_NUMBER', 1);
    else
        throw new Exception('MODULE_FILENAME_MATCH_REGEX redefined but MODULE_NAME_VALUE_PART_NUMBER still not');
}
if (!defined('MODULE_FILENAME_MATCH_REGEX_FLAGS'))
    define('MODULE_FILENAME_MATCH_REGEX_FLAGS', 'U');

//
//
//
class Plugin
{
    protected $m_module_name;
    protected $m_factory_function_name;
    //
    function __construct($m)
    {
        $this->m_module_name = $m;
        $this->m_factory_function_name = null;
    }
    // 'class_factory' method overloader
    function __call($method_name, $params)
    {
        // Is plugin module already loaded??
        if ($this->m_factory_function_name == null)
        {
            // Load plugin module
            if (! @ include_once ($this->m_module_name))
                throw new Exception('Unable to load plugable module `'.$this->m_module_name.'`');
            // check if required class factory present
            preg_match(
                ','.MODULE_FILENAME_MATCH_REGEX.','.MODULE_FILENAME_MATCH_REGEX_FLAGS
              , basename($this->m_module_name)
              , $f
              );
            $this->m_factory_function_name = preg_replace(',[^0-9_A-Za-z],', '_', $f[MODULE_NAME_VALUE_PART_NUMBER]);
            if (!function_exists($this->m_factory_function_name .= '_class_factory'))
                throw new Exception('Plugable module `'.$this->m_module_name.'` have no class factory defined');
        }
        if ($method_name != 'class_factory')
            trigger_error(sprintf('Call to undefined function: %s::%s().', get_class($this), $method_name), E_USER_ERROR);
        return call_user_func_array($this->m_factory_function_name, $params);
    }
    //
    function name()
    {
        return $this->m_module_name;
    }
};

/**
 * \internal
 */
class PluginsDirectoryReader extends FilterIterator
{
    //
    function __construct($it)
    {
        parent::__construct($it);
    }
    function accept()
    {
        return preg_match(
            ','.MODULE_FILENAME_MATCH_REGEX.','.MODULE_FILENAME_MATCH_REGEX_FLAGS
          , basename($this->current())
          );
    }
    function current()
    {
        return parent::current()->getPathname();
    }
};

//
//
//
class PluginsManager implements Iterator, ArrayAccess
{
    protected $m_plugins_dir;
    protected $m_plugins = array();

    function __construct($path)
    {
        if (!is_dir($path))
            throw new Exception('Given name is not a directory (`'.$path.'`)');
        //
        $it = new PluginsDirectoryReader(
            new RecursiveIteratorIterator(
                new RecursiveDirectoryIterator($this->m_plugins_dir = $path)
              )
          );
        foreach ($it as $plugin)
            $this->m_plugins[] = $plugin;
    }
    // Iterator interface
    function rewind()
    {
        reset($this->m_plugins);
    }
    function current()
    {
        return ($a = current($this->m_plugins)) !== false ? new Plugin($a) : false;
    }
    function key()
    {
        return key($this->m_plugins);
    }
    function next()
    {
        return ($a = next($this->m_plugins)) !== false ? new Plugin($a) : false;
    }
    function valid()
    {
        return $this->current() !== false;
    }
    // ArrayAccess interface
    function offsetSet($key, $value)
    {
        throw new Exception('This operation disallowed by this class');
    }
    function offsetGet($key)
    {
        return (($idx = array_search($key, $this->m_plugins)) !== false)
          ? new Plugin($this->m_plugins_dir.'/'.$this->m_plugins[$idx])
          : null;
    }
    function offsetUnset($key)
    {
        if (($idx = array_search($key, $this->m_plugins)) !== false)
            unset($this->m_plugins[$idx]);
    }
    function offsetExists($offset)
    {
        return in_array($offset, $this->m_plugins);
    }
};

?>
