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