]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - plugins/Irc/extlib/phergie/Phergie/Plugin/Handler.php
b2ef089b44cee0071f08126996dcda039216a138
[quix0rs-gnu-social.git] / plugins / Irc / extlib / phergie / Phergie / Plugin / Handler.php
1 <?php
2 /**
3  * Phergie
4  *
5  * PHP version 5
6  *
7  * LICENSE
8  *
9  * This source file is subject to the new BSD license that is bundled
10  * with this package in the file LICENSE.
11  * It is also available through the world-wide-web at this URL:
12  * http://phergie.org/license
13  *
14  * @category  Phergie
15  * @package   Phergie
16  * @author    Phergie Development Team <team@phergie.org>
17  * @copyright 2008-2010 Phergie Development Team (http://phergie.org)
18  * @license   http://phergie.org/license New BSD License
19  * @link      http://pear.phergie.org/package/Phergie
20  */
21
22 /**
23  * Handles on-demand loading of, iteration over, and access to plugins.
24  *
25  * @category Phergie
26  * @package  Phergie
27  * @author   Phergie Development Team <team@phergie.org>
28  * @license  http://phergie.org/license New BSD License
29  * @link     http://pear.phergie.org/package/Phergie
30  */
31 class Phergie_Plugin_Handler implements IteratorAggregate, Countable
32 {
33     /**
34      * Current list of plugin instances
35      *
36      * @var array
37      */
38     protected $plugins;
39
40     /**
41      * Paths in which to search for plugin class files
42      *
43      * @var array
44      */
45     protected $paths;
46
47     /**
48      * Flag indicating whether plugin classes should be instantiated on
49      * demand if they are requested but no instance currently exists
50      *
51      * @var bool
52      */
53     protected $autoload;
54
55     /**
56      * Phergie_Config instance that should be passed in to any plugin
57      * instantiated within the handler
58      *
59      * @var Phergie_Config
60      */
61     protected $config;
62
63     /**
64      * Phergie_Event_Handler instance that should be passed in to any plugin
65      * instantiated within the handler
66      *
67      * @var Phergie_Event_Handler
68      */
69     protected $events;
70
71     /**
72      * Constructor to initialize class properties and add the path for core
73      * plugins.
74      *
75      * @param Phergie_Config        $config configuration to pass to any
76      *        instantiated plugin
77      * @param Phergie_Event_Handler $events event handler to pass to any
78      *        instantiated plugin
79      *
80      * @return void
81      */
82     public function __construct(
83         Phergie_Config $config,
84         Phergie_Event_Handler $events
85     ) {
86         $this->config = $config;
87         $this->events = $events;
88
89         $this->plugins = array();
90         $this->paths = array();
91         $this->autoload = false;
92
93         $this->addPath(dirname(__FILE__), 'Phergie_Plugin_');
94     }
95
96
97     /**
98      * Adds a path to search for plugin class files. Paths are searched in
99      * the reverse order in which they are added.
100      *
101      * @param string $path   Filesystem directory path
102      * @param string $prefix Optional class name prefix corresponding to the
103      *        path
104      *
105      * @return Phergie_Plugin_Handler Provides a fluent interface
106      * @throws Phergie_Plugin_Exception
107      */
108     public function addPath($path, $prefix = '')
109     {
110         if (!is_readable($path)) {
111             throw new Phergie_Plugin_Exception(
112                 'Path "' . $path . '" does not reference a readable directory',
113                 Phergie_Plugin_Exception::ERR_DIRECTORY_NOT_READABLE
114             );
115         }
116
117         $this->paths[] = array(
118             'path' => rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR,
119             'prefix' => $prefix
120         );
121
122         return $this;
123     }
124
125     /**
126      * Returns metadata corresponding to a specified plugin.
127      *
128      * @param string $plugin Short name of the plugin class
129      * @throws Phergie_Plugin_Exception Class file can't be found
130      *
131      * @return array|boolean Associative array containing the path to the
132      *         class file and its containing directory as well as the full
133      *         class name
134      */
135     public function getPluginInfo($plugin)
136     {
137        foreach (array_reverse($this->paths) as $path) {
138             $file = $path['path'] . $plugin . '.php';
139             if (file_exists($file)) {
140                 $path = array(
141                     'dir' => $path['path'],
142                     'file' => $file,
143                     'class' => $path['prefix'] . $plugin,
144                 );
145                 return $path;
146             }
147         }
148
149         // If the class can't be found, display an error
150         throw new Phergie_Plugin_Exception(
151             'Class file for plugin "' . $plugin . '" cannot be found',
152             Phergie_Plugin_Exception::ERR_CLASS_NOT_FOUND
153         );
154     }
155
156     /**
157      * Adds a plugin instance to the handler.
158      *
159      * @param string|Phergie_Plugin_Abstract $plugin Short name of the
160      *        plugin class or a plugin object
161      * @param array                          $args   Optional array of
162      *        arguments to pass to the plugin constructor if a short name is
163      *        passed for $plugin
164      *
165      * @return Phergie_Plugin_Abstract New plugin instance
166      */
167     public function addPlugin($plugin, array $args = null)
168     {
169         // If a short plugin name is specified...
170         if (is_string($plugin)) {
171             $index = strtolower($plugin);
172             if (isset($this->plugins[$index])) {
173                 return $this->plugins[$index];
174             }
175
176             // Attempt to locate and load the class
177             $info = $this->getPluginInfo($plugin);
178             $file = $info['file'];
179             $class = $info['class'];
180             include_once $file;
181             if (!class_exists($class, false)) {
182                 throw new Phergie_Plugin_Exception(
183                     'File "' . $file . '" does not contain class "' . $class . '"',
184                     Phergie_Plugin_Exception::ERR_CLASS_NOT_FOUND
185                 );
186             }
187
188             // Check to ensure the class is a plugin class
189             if (!is_subclass_of($class, 'Phergie_Plugin_Abstract')) {
190                 $msg
191                     = 'Class for plugin "' . $plugin .
192                     '" does not extend Phergie_Plugin_Abstract';
193                 throw new Phergie_Plugin_Exception(
194                     $msg,
195                     Phergie_Plugin_Exception::ERR_INCORRECT_BASE_CLASS
196                 );
197             }
198
199             // Check to ensure the class can be instantiated
200             $reflection = new ReflectionClass($class);
201             if (!$reflection->isInstantiable()) {
202                 throw new Phergie_Plugin_Exception(
203                     'Class for plugin "' . $plugin . '" cannot be instantiated',
204                     Phergie_Plugin_Exception::ERR_CLASS_NOT_INSTANTIABLE
205                 );
206             }
207
208             // If the class is found, instantiate it
209             if (!empty($args)) {
210                 $instance = $reflection->newInstanceArgs($args);
211             } else {
212                 $instance = new $class;
213             }
214
215             // Store the instance
216             $this->plugins[$index] = $instance;
217             $plugin = $instance;
218
219         } elseif ($plugin instanceof Phergie_Plugin_Abstract) {
220             // If a plugin instance is specified...
221
222             // Add the plugin instance to the list of plugins
223             $this->plugins[strtolower($plugin->getName())] = $plugin;
224         }
225
226         // Configure and initialize the instance
227         $plugin->setPluginHandler($this);
228         $plugin->setConfig($this->config);
229         $plugin->setEventHandler($this->events);
230         $plugin->onLoad();
231
232         return $plugin;
233     }
234
235     /**
236      * Adds multiple plugin instances to the handler.
237      *
238      * @param array $plugins List of elements where each is of the form
239      *        'ShortPluginName' or array('ShortPluginName', array($arg1,
240      *        ..., $argN))
241      *
242      * @return Phergie_Plugin_Handler Provides a fluent interface
243      */
244     public function addPlugins(array $plugins)
245     {
246         foreach ($plugins as $plugin) {
247             if (is_array($plugin)) {
248                 $this->addPlugin($plugin[0], $plugin[1]);
249             } else {
250                 $this->addPlugin($plugin);
251             }
252         }
253
254         return $this;
255     }
256
257     /**
258      * Removes a plugin instance from the handler.
259      *
260      * @param string|Phergie_Plugin_Abstract $plugin Short name of the
261      *        plugin class or a plugin object
262      *
263      * @return Phergie_Plugin_Handler Provides a fluent interface
264      */
265     public function removePlugin($plugin)
266     {
267         if ($plugin instanceof Phergie_Plugin_Abstract) {
268             $plugin = $plugin->getName();
269         }
270         $plugin = strtolower($plugin);
271
272         unset($this->plugins[$plugin]);
273
274         return $this;
275     }
276
277     /**
278      * Returns the corresponding instance for a specified plugin, loading it
279      * if it is not already loaded and autoloading is enabled.
280      *
281      * @param string $name Short name of the plugin class
282      *
283      * @return Phergie_Plugin_Abstract Plugin instance
284      */
285     public function getPlugin($name)
286     {
287         // If the plugin is loaded, return the instance
288         $lower = strtolower($name);
289         if (isset($this->plugins[$lower])) {
290             return $this->plugins[$lower];
291         }
292
293         // If autoloading is disabled, display an error
294         if (!$this->autoload) {
295             $msg
296                 = 'Plugin "' . $name . '" has been requested, ' .
297                 'is not loaded, and autoload is disabled';
298             throw new Phergie_Plugin_Exception(
299                 $msg,
300                 Phergie_Plugin_Exception::ERR_PLUGIN_NOT_LOADED
301             );
302         }
303
304         // If autoloading is enabled, attempt to load the plugin
305         $plugin = $this->addPlugin($name);
306
307         // Return the added plugin
308         return $plugin;
309     }
310
311     /**
312      * Returns the corresponding instances for multiple specified plugins,
313      * loading them if they are not already loaded and autoloading is
314      * enabled.
315      *
316      * @param array $names Optional list of short names of the plugin
317      *        classes to which the returned plugin list will be limited,
318      *        defaults to all presently loaded plugins
319      *
320      * @return array Associative array mapping lowercased plugin class short
321      *         names to corresponding plugin instances
322      */
323     public function getPlugins(array $names = array())
324     {
325         if (empty($names)) {
326             return $this->plugins;
327         }
328
329         $plugins = array();
330         foreach ($names as $name) {
331             $plugins[$name] = $this->getPlugin($name);
332         }
333         return $plugins;
334     }
335
336     /**
337      * Returns whether or not at least one instance of a specified plugin
338      * class is loaded.
339      *
340      * @param string $name Short name of the plugin class
341      *
342      * @return bool TRUE if an instance exists, FALSE otherwise
343      */
344     public function hasPlugin($name)
345     {
346         return isset($this->plugins[strtolower($name)]);
347     }
348
349     /**
350      * Sets a flag used to determine whether plugins should be loaded
351      * automatically if they have not been explicitly loaded.
352      *
353      * @param bool $flag TRUE to have plugins autoload (default), FALSE
354      *        otherwise
355      *
356      * @return Phergie_Plugin_Handler Provides a fluent interface.
357      */
358     public function setAutoload($flag = true)
359     {
360         $this->autoload = $flag;
361
362         return $this;
363     }
364
365     /**
366      * Returns the value of a flag used to determine whether plugins should
367      * be loaded automatically if they have not been explicitly loaded.
368      *
369      * @return bool TRUE if autoloading is enabled, FALSE otherwise
370      */
371     public function getAutoload()
372     {
373         return $this->autoload;
374     }
375
376     /**
377      * Allows plugin instances to be accessed as properties of the handler.
378      *
379      * @param string $name Short name of the plugin
380      *
381      * @return Phergie_Plugin_Abstract Requested plugin instance
382      */
383     public function __get($name)
384     {
385         return $this->getPlugin($name);
386     }
387
388     /**
389      * Allows plugin instances to be detected as properties of the handler.
390      *
391      * @param string $name Short name of the plugin
392      *
393      * @return bool TRUE if the plugin is loaded, FALSE otherwise
394      */
395     public function __isset($name)
396     {
397         return $this->hasPlugin($name);
398     }
399
400     /**
401      * Allows plugin instances to be removed as properties of handler.
402      *
403      * @param string $name Short name of the plugin
404      *
405      * @return void
406      */
407     public function __unset($name)
408     {
409         $this->removePlugin($name);
410     }
411
412     /**
413      * Returns an iterator for all currently loaded plugin instances.
414      *
415      * @return ArrayIterator
416      */
417     public function getIterator()
418     {
419         return new ArrayIterator($this->plugins);
420     }
421
422     /**
423      * Proxies method calls to all plugins containing the called method. An
424      * individual plugin may short-circuit this process by explicitly
425      * returning FALSE.
426      *
427      * @param string $name Name of the method called
428      * @param array  $args Arguments passed in the method call
429      *
430      * @return bool FALSE if a plugin short-circuits processing by returning
431      *         FALSE, TRUE otherwise
432      */
433     public function __call($name, array $args)
434     {
435         foreach ($this->plugins as $plugin) {
436             if (call_user_func_array(array($plugin, $name), $args) === false) {
437                 return false;
438             }
439         }
440         return true;
441     }
442
443     /**
444      * Returns the number of plugins contained within the handler.
445      *
446      * @return int Plugin count
447      */
448     public function count()
449     {
450         return count($this->plugins);
451     }
452 }