gnu-social/plugins/Irc/extlib/phergie/Phergie/Plugin/Handler.php
2010-08-12 11:58:53 -07:00

502 lines
14 KiB
PHP
Executable File

<?php
/**
* Phergie
*
* PHP version 5
*
* LICENSE
*
* This source file is subject to the new BSD license that is bundled
* with this package in the file LICENSE.
* It is also available through the world-wide-web at this URL:
* http://phergie.org/license
*
* @category Phergie
* @package Phergie
* @author Phergie Development Team <team@phergie.org>
* @copyright 2008-2010 Phergie Development Team (http://phergie.org)
* @license http://phergie.org/license New BSD License
* @link http://pear.phergie.org/package/Phergie
*/
/**
* Handles on-demand loading of, iteration over, and access to plugins.
*
* @category Phergie
* @package Phergie
* @author Phergie Development Team <team@phergie.org>
* @license http://phergie.org/license New BSD License
* @link http://pear.phergie.org/package/Phergie
*/
class Phergie_Plugin_Handler implements IteratorAggregate, Countable
{
/**
* Current list of plugin instances
*
* @var array
*/
protected $plugins;
/**
* Paths in which to search for plugin class files
*
* @var array
*/
protected $paths;
/**
* Flag indicating whether plugin classes should be instantiated on
* demand if they are requested but no instance currently exists
*
* @var bool
*/
protected $autoload;
/**
* Phergie_Config instance that should be passed in to any plugin
* instantiated within the handler
*
* @var Phergie_Config
*/
protected $config;
/**
* Phergie_Event_Handler instance that should be passed in to any plugin
* instantiated within the handler
*
* @var Phergie_Event_Handler
*/
protected $events;
/**
* Name of the class to use for iterating over all currently loaded
* plugins
*
* @var string
*/
protected $iteratorClass = 'Phergie_Plugin_Iterator';
/**
* Constructor to initialize class properties and add the path for core
* plugins.
*
* @param Phergie_Config $config configuration to pass to any
* instantiated plugin
* @param Phergie_Event_Handler $events event handler to pass to any
* instantiated plugin
*
* @return void
*/
public function __construct(
Phergie_Config $config,
Phergie_Event_Handler $events
) {
$this->config = $config;
$this->events = $events;
$this->plugins = array();
$this->paths = array();
$this->autoload = false;
if (!empty($config['plugins.paths'])) {
foreach ($config['plugins.paths'] as $dir => $prefix) {
$this->addPath($dir, $prefix);
}
}
$this->addPath(dirname(__FILE__), 'Phergie_Plugin_');
}
/**
* Adds a path to search for plugin class files. Paths are searched in
* the reverse order in which they are added.
*
* @param string $path Filesystem directory path
* @param string $prefix Optional class name prefix corresponding to the
* path
*
* @return Phergie_Plugin_Handler Provides a fluent interface
* @throws Phergie_Plugin_Exception
*/
public function addPath($path, $prefix = '')
{
if (!is_readable($path)) {
throw new Phergie_Plugin_Exception(
'Path "' . $path . '" does not reference a readable directory',
Phergie_Plugin_Exception::ERR_DIRECTORY_NOT_READABLE
);
}
$this->paths[] = array(
'path' => rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR,
'prefix' => $prefix
);
return $this;
}
/**
* Returns metadata corresponding to a specified plugin.
*
* @param string $plugin Short name of the plugin class
*
* @throws Phergie_Plugin_Exception Class file can't be found
*
* @return array|boolean Associative array containing the path to the
* class file and its containing directory as well as the full
* class name
*/
public function getPluginInfo($plugin)
{
foreach (array_reverse($this->paths) as $path) {
$file = $path['path'] . $plugin . '.php';
if (file_exists($file)) {
$path = array(
'dir' => $path['path'],
'file' => $file,
'class' => $path['prefix'] . $plugin,
);
return $path;
}
}
// If the class can't be found, display an error
throw new Phergie_Plugin_Exception(
'Class file for plugin "' . $plugin . '" cannot be found',
Phergie_Plugin_Exception::ERR_CLASS_NOT_FOUND
);
}
/**
* Adds a plugin instance to the handler.
*
* @param string|Phergie_Plugin_Abstract $plugin Short name of the
* plugin class or a plugin object
* @param array $args Optional array of
* arguments to pass to the plugin constructor if a short name is
* passed for $plugin
*
* @return Phergie_Plugin_Abstract New plugin instance
*/
public function addPlugin($plugin, array $args = null)
{
// If a short plugin name is specified...
if (is_string($plugin)) {
$index = strtolower($plugin);
if (isset($this->plugins[$index])) {
return $this->plugins[$index];
}
// Attempt to locate and load the class
$info = $this->getPluginInfo($plugin);
$file = $info['file'];
$class = $info['class'];
include_once $file;
if (!class_exists($class, false)) {
throw new Phergie_Plugin_Exception(
'File "' . $file . '" does not contain class "' . $class . '"',
Phergie_Plugin_Exception::ERR_CLASS_NOT_FOUND
);
}
// Check to ensure the class is a plugin class
if (!is_subclass_of($class, 'Phergie_Plugin_Abstract')) {
$msg
= 'Class for plugin "' . $plugin .
'" does not extend Phergie_Plugin_Abstract';
throw new Phergie_Plugin_Exception(
$msg,
Phergie_Plugin_Exception::ERR_INCORRECT_BASE_CLASS
);
}
// Check to ensure the class can be instantiated
$reflection = new ReflectionClass($class);
if (!$reflection->isInstantiable()) {
throw new Phergie_Plugin_Exception(
'Class for plugin "' . $plugin . '" cannot be instantiated',
Phergie_Plugin_Exception::ERR_CLASS_NOT_INSTANTIABLE
);
}
// If the class is found, instantiate it
if (!empty($args)) {
$instance = $reflection->newInstanceArgs($args);
} else {
$instance = new $class;
}
// Store the instance
$this->plugins[$index] = $instance;
$plugin = $instance;
} elseif ($plugin instanceof Phergie_Plugin_Abstract) {
// If a plugin instance is specified...
// Add the plugin instance to the list of plugins
$this->plugins[strtolower($plugin->getName())] = $plugin;
}
// Configure and initialize the instance
$plugin->setPluginHandler($this);
$plugin->setConfig($this->config);
$plugin->setEventHandler($this->events);
$plugin->onLoad();
return $plugin;
}
/**
* Adds multiple plugin instances to the handler.
*
* @param array $plugins List of elements where each is of the form
* 'ShortPluginName' or array('ShortPluginName', array($arg1,
* ..., $argN))
*
* @return Phergie_Plugin_Handler Provides a fluent interface
*/
public function addPlugins(array $plugins)
{
foreach ($plugins as $plugin) {
if (is_array($plugin)) {
$this->addPlugin($plugin[0], $plugin[1]);
} else {
$this->addPlugin($plugin);
}
}
return $this;
}
/**
* Removes a plugin instance from the handler.
*
* @param string|Phergie_Plugin_Abstract $plugin Short name of the
* plugin class or a plugin object
*
* @return Phergie_Plugin_Handler Provides a fluent interface
*/
public function removePlugin($plugin)
{
if ($plugin instanceof Phergie_Plugin_Abstract) {
$plugin = $plugin->getName();
}
$plugin = strtolower($plugin);
unset($this->plugins[$plugin]);
return $this;
}
/**
* Returns the corresponding instance for a specified plugin, loading it
* if it is not already loaded and autoloading is enabled.
*
* @param string $name Short name of the plugin class
*
* @return Phergie_Plugin_Abstract Plugin instance
*/
public function getPlugin($name)
{
// If the plugin is loaded, return the instance
$lower = strtolower($name);
if (isset($this->plugins[$lower])) {
return $this->plugins[$lower];
}
// If autoloading is disabled, display an error
if (!$this->autoload) {
$msg
= 'Plugin "' . $name . '" has been requested, ' .
'is not loaded, and autoload is disabled';
throw new Phergie_Plugin_Exception(
$msg,
Phergie_Plugin_Exception::ERR_PLUGIN_NOT_LOADED
);
}
// If autoloading is enabled, attempt to load the plugin
$plugin = $this->addPlugin($name);
// Return the added plugin
return $plugin;
}
/**
* Returns the corresponding instances for multiple specified plugins,
* loading them if they are not already loaded and autoloading is
* enabled.
*
* @param array $names Optional list of short names of the plugin
* classes to which the returned plugin list will be limited,
* defaults to all presently loaded plugins
*
* @return array Associative array mapping lowercased plugin class short
* names to corresponding plugin instances
*/
public function getPlugins(array $names = array())
{
if (empty($names)) {
return $this->plugins;
}
$plugins = array();
foreach ($names as $name) {
$plugins[strtolower($name)] = $this->getPlugin($name);
}
return $plugins;
}
/**
* Returns whether or not at least one instance of a specified plugin
* class is loaded.
*
* @param string $name Short name of the plugin class
*
* @return bool TRUE if an instance exists, FALSE otherwise
*/
public function hasPlugin($name)
{
return isset($this->plugins[strtolower($name)]);
}
/**
* Sets a flag used to determine whether plugins should be loaded
* automatically if they have not been explicitly loaded.
*
* @param bool $flag TRUE to have plugins autoload (default), FALSE
* otherwise
*
* @return Phergie_Plugin_Handler Provides a fluent interface.
*/
public function setAutoload($flag = true)
{
$this->autoload = $flag;
return $this;
}
/**
* Returns the value of a flag used to determine whether plugins should
* be loaded automatically if they have not been explicitly loaded.
*
* @return bool TRUE if autoloading is enabled, FALSE otherwise
*/
public function getAutoload()
{
return $this->autoload;
}
/**
* Allows plugin instances to be accessed as properties of the handler.
*
* @param string $name Short name of the plugin
*
* @return Phergie_Plugin_Abstract Requested plugin instance
*/
public function __get($name)
{
return $this->getPlugin($name);
}
/**
* Allows plugin instances to be detected as properties of the handler.
*
* @param string $name Short name of the plugin
*
* @return bool TRUE if the plugin is loaded, FALSE otherwise
*/
public function __isset($name)
{
return $this->hasPlugin($name);
}
/**
* Allows plugin instances to be removed as properties of handler.
*
* @param string $name Short name of the plugin
*
* @return void
*/
public function __unset($name)
{
$this->removePlugin($name);
}
/**
* Returns an iterator for all currently loaded plugin instances.
*
* @return ArrayIterator
*/
public function getIterator()
{
return new $this->iteratorClass(
new ArrayIterator($this->plugins)
);
}
/**
* Sets the iterator class used for all currently loaded plugin
* instances.
*
* @param string $class Name of a class that extends FilterIterator
*
* @return Phergie_Plugin_Handler Provides a fluent API
* @throws Phergie_Plugin_Exception Class cannot be found or is not an
* FilterIterator-based class
*/
public function setIteratorClass($class)
{
$valid = true;
try {
$error_reporting = error_reporting(0); // ignore autoloader errors
$r = new ReflectionClass($class);
error_reporting($error_reporting);
if (!$r->isSubclassOf('FilterIterator')) {
$message = 'Class ' . $class . ' is not a subclass of FilterIterator';
$valid = false;
}
} catch (ReflectionException $e) {
$message = $e->getMessage();
$valid = false;
}
if (!$valid) {
throw new Phergie_Plugin_Exception(
$message,
Phergie_Plugin_Exception::ERR_INVALID_ITERATOR_CLASS
);
}
$this->iteratorClass = $class;
}
/**
* Proxies method calls to all plugins containing the called method.
*
* @param string $name Name of the method called
* @param array $args Arguments passed in the method call
*
* @return void
*/
public function __call($name, array $args)
{
foreach ($this->getIterator() as $plugin) {
call_user_func_array(array($plugin, $name), $args);
}
return true;
}
/**
* Returns the number of plugins contained within the handler.
*
* @return int Plugin count
*/
public function count()
{
return count($this->plugins);
}
}