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