/Phergie/Plugin/Handler.php
PHP | 501 lines | 221 code | 51 blank | 229 comment | 18 complexity | c37f03506f9c6857c5eab42ded23f2e6 MD5 | raw 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;
- }
- } elseif ($plugin instanceof Phergie_Plugin_Abstract) {
- // If a plugin instance is specified...
- // Add the plugin instance to the list of plugins
- $index = strtolower($plugin->getName());
- $instance = $plugin;
- }
- // Configure and initialize the instance
- $instance->setPluginHandler($this);
- $instance->setConfig($this->config);
- $instance->setEventHandler($this->events);
- $instance->onLoad();
- // Store the instance
- $this->plugins[$index] = $instance;
- return $instance;
- }
- /**
- * 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);
- }
- }