xref: /dokuwiki/inc/Extension/PluginController.php (revision 3a7140a158be7afab7773c232f6a21a68ec807a8)
1*3a7140a1SAndreas Gohr<?php
2*3a7140a1SAndreas Gohr/**
3*3a7140a1SAndreas Gohr * Class to encapsulate access to dokuwiki plugins
4*3a7140a1SAndreas Gohr *
5*3a7140a1SAndreas Gohr * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
6*3a7140a1SAndreas Gohr * @author     Christopher Smith <chris@jalakai.co.uk>
7*3a7140a1SAndreas Gohr */
8*3a7140a1SAndreas Gohr
9*3a7140a1SAndreas Gohrnamespace dokuwiki\Extension;
10*3a7140a1SAndreas Gohr
11*3a7140a1SAndreas Gohrclass PluginController
12*3a7140a1SAndreas Gohr{
13*3a7140a1SAndreas Gohr
14*3a7140a1SAndreas Gohr    protected $list_bytype = array();
15*3a7140a1SAndreas Gohr    protected $tmp_plugins = array();
16*3a7140a1SAndreas Gohr    protected $plugin_cascade = array('default' => array(), 'local' => array(), 'protected' => array());
17*3a7140a1SAndreas Gohr    protected $last_local_config_file = '';
18*3a7140a1SAndreas Gohr
19*3a7140a1SAndreas Gohr    /**
20*3a7140a1SAndreas Gohr     * Populates the master list of plugins
21*3a7140a1SAndreas Gohr     */
22*3a7140a1SAndreas Gohr    public function __construct()
23*3a7140a1SAndreas Gohr    {
24*3a7140a1SAndreas Gohr        $this->loadConfig();
25*3a7140a1SAndreas Gohr        $this->_populateMasterList();
26*3a7140a1SAndreas Gohr    }
27*3a7140a1SAndreas Gohr
28*3a7140a1SAndreas Gohr    /**
29*3a7140a1SAndreas Gohr     * Returns a list of available plugins of given type
30*3a7140a1SAndreas Gohr     *
31*3a7140a1SAndreas Gohr     * @param $type  string, plugin_type name;
32*3a7140a1SAndreas Gohr     *               the type of plugin to return,
33*3a7140a1SAndreas Gohr     *               use empty string for all types
34*3a7140a1SAndreas Gohr     * @param $all   bool;
35*3a7140a1SAndreas Gohr     *               false to only return enabled plugins,
36*3a7140a1SAndreas Gohr     *               true to return both enabled and disabled plugins
37*3a7140a1SAndreas Gohr     *
38*3a7140a1SAndreas Gohr     * @return       array of
39*3a7140a1SAndreas Gohr     *                  - plugin names when $type = ''
40*3a7140a1SAndreas Gohr     *                  - or plugin component names when a $type is given
41*3a7140a1SAndreas Gohr     *
42*3a7140a1SAndreas Gohr     * @author Andreas Gohr <andi@splitbrain.org>
43*3a7140a1SAndreas Gohr     */
44*3a7140a1SAndreas Gohr    public function getList($type = '', $all = false)
45*3a7140a1SAndreas Gohr    {
46*3a7140a1SAndreas Gohr
47*3a7140a1SAndreas Gohr        // request the complete list
48*3a7140a1SAndreas Gohr        if (!$type) {
49*3a7140a1SAndreas Gohr            return $all ? array_keys($this->tmp_plugins) : array_keys(array_filter($this->tmp_plugins));
50*3a7140a1SAndreas Gohr        }
51*3a7140a1SAndreas Gohr
52*3a7140a1SAndreas Gohr        if (!isset($this->list_bytype[$type]['enabled'])) {
53*3a7140a1SAndreas Gohr            $this->list_bytype[$type]['enabled'] = $this->_getListByType($type, true);
54*3a7140a1SAndreas Gohr        }
55*3a7140a1SAndreas Gohr        if ($all && !isset($this->list_bytype[$type]['disabled'])) {
56*3a7140a1SAndreas Gohr            $this->list_bytype[$type]['disabled'] = $this->_getListByType($type, false);
57*3a7140a1SAndreas Gohr        }
58*3a7140a1SAndreas Gohr
59*3a7140a1SAndreas Gohr        return $all
60*3a7140a1SAndreas Gohr            ? array_merge($this->list_bytype[$type]['enabled'], $this->list_bytype[$type]['disabled'])
61*3a7140a1SAndreas Gohr            : $this->list_bytype[$type]['enabled'];
62*3a7140a1SAndreas Gohr    }
63*3a7140a1SAndreas Gohr
64*3a7140a1SAndreas Gohr    /**
65*3a7140a1SAndreas Gohr     * Loads the given plugin and creates an object of it
66*3a7140a1SAndreas Gohr     *
67*3a7140a1SAndreas Gohr     * @author Andreas Gohr <andi@splitbrain.org>
68*3a7140a1SAndreas Gohr     *
69*3a7140a1SAndreas Gohr     * @param  $type     string type of plugin to load
70*3a7140a1SAndreas Gohr     * @param  $name     string name of the plugin to load
71*3a7140a1SAndreas Gohr     * @param  $new      bool   true to return a new instance of the plugin, false to use an already loaded instance
72*3a7140a1SAndreas Gohr     * @param  $disabled bool   true to load even disabled plugins
73*3a7140a1SAndreas Gohr     * @return PluginInterface|null  the plugin object or null on failure
74*3a7140a1SAndreas Gohr     */
75*3a7140a1SAndreas Gohr    public function load($type, $name, $new = false, $disabled = false)
76*3a7140a1SAndreas Gohr    {
77*3a7140a1SAndreas Gohr
78*3a7140a1SAndreas Gohr        //we keep all loaded plugins available in global scope for reuse
79*3a7140a1SAndreas Gohr        global $DOKU_PLUGINS;
80*3a7140a1SAndreas Gohr
81*3a7140a1SAndreas Gohr        list($plugin, /* $component */) = $this->_splitName($name);
82*3a7140a1SAndreas Gohr
83*3a7140a1SAndreas Gohr        // check if disabled
84*3a7140a1SAndreas Gohr        if (!$disabled && $this->isdisabled($plugin)) {
85*3a7140a1SAndreas Gohr            return null;
86*3a7140a1SAndreas Gohr        }
87*3a7140a1SAndreas Gohr
88*3a7140a1SAndreas Gohr        $class = $type . '_plugin_' . $name;
89*3a7140a1SAndreas Gohr
90*3a7140a1SAndreas Gohr        //plugin already loaded?
91*3a7140a1SAndreas Gohr        if (!empty($DOKU_PLUGINS[$type][$name])) {
92*3a7140a1SAndreas Gohr            if ($new || !$DOKU_PLUGINS[$type][$name]->isSingleton()) {
93*3a7140a1SAndreas Gohr                return class_exists($class, true) ? new $class : null;
94*3a7140a1SAndreas Gohr            } else {
95*3a7140a1SAndreas Gohr                return $DOKU_PLUGINS[$type][$name];
96*3a7140a1SAndreas Gohr            }
97*3a7140a1SAndreas Gohr        }
98*3a7140a1SAndreas Gohr
99*3a7140a1SAndreas Gohr        //construct class and instantiate
100*3a7140a1SAndreas Gohr        if (!class_exists($class, true)) {
101*3a7140a1SAndreas Gohr
102*3a7140a1SAndreas Gohr            # the plugin might be in the wrong directory
103*3a7140a1SAndreas Gohr            $dir = $this->get_directory($plugin);
104*3a7140a1SAndreas Gohr            $inf = confToHash(DOKU_PLUGIN . "$dir/plugin.info.txt");
105*3a7140a1SAndreas Gohr            if ($inf['base'] && $inf['base'] != $plugin) {
106*3a7140a1SAndreas Gohr                msg(
107*3a7140a1SAndreas Gohr                    sprintf(
108*3a7140a1SAndreas Gohr                        "Plugin installed incorrectly. Rename plugin directory '%s' to '%s'.",
109*3a7140a1SAndreas Gohr                        hsc($plugin),
110*3a7140a1SAndreas Gohr                        hsc(
111*3a7140a1SAndreas Gohr                            $inf['base']
112*3a7140a1SAndreas Gohr                        )
113*3a7140a1SAndreas Gohr                    ), -1
114*3a7140a1SAndreas Gohr                );
115*3a7140a1SAndreas Gohr            } elseif (preg_match('/^' . DOKU_PLUGIN_NAME_REGEX . '$/', $plugin) !== 1) {
116*3a7140a1SAndreas Gohr                msg(
117*3a7140a1SAndreas Gohr                    sprintf(
118*3a7140a1SAndreas Gohr                        "Plugin name '%s' is not a valid plugin name, only the characters a-z and 0-9 are allowed. " .
119*3a7140a1SAndreas Gohr                        'Maybe the plugin has been installed in the wrong directory?', hsc($plugin)
120*3a7140a1SAndreas Gohr                    ), -1
121*3a7140a1SAndreas Gohr                );
122*3a7140a1SAndreas Gohr            }
123*3a7140a1SAndreas Gohr            return null;
124*3a7140a1SAndreas Gohr        }
125*3a7140a1SAndreas Gohr
126*3a7140a1SAndreas Gohr        $DOKU_PLUGINS[$type][$name] = new $class;
127*3a7140a1SAndreas Gohr        return $DOKU_PLUGINS[$type][$name];
128*3a7140a1SAndreas Gohr    }
129*3a7140a1SAndreas Gohr
130*3a7140a1SAndreas Gohr    /**
131*3a7140a1SAndreas Gohr     * Whether plugin is disabled
132*3a7140a1SAndreas Gohr     *
133*3a7140a1SAndreas Gohr     * @param string $plugin name of plugin
134*3a7140a1SAndreas Gohr     * @return bool  true disabled, false enabled
135*3a7140a1SAndreas Gohr     */
136*3a7140a1SAndreas Gohr    public function isdisabled($plugin)
137*3a7140a1SAndreas Gohr    {
138*3a7140a1SAndreas Gohr        return empty($this->tmp_plugins[$plugin]);
139*3a7140a1SAndreas Gohr    }
140*3a7140a1SAndreas Gohr
141*3a7140a1SAndreas Gohr    /**
142*3a7140a1SAndreas Gohr     * Disable the plugin
143*3a7140a1SAndreas Gohr     *
144*3a7140a1SAndreas Gohr     * @param string $plugin name of plugin
145*3a7140a1SAndreas Gohr     * @return bool  true saving succeed, false saving failed
146*3a7140a1SAndreas Gohr     */
147*3a7140a1SAndreas Gohr    public function disable($plugin)
148*3a7140a1SAndreas Gohr    {
149*3a7140a1SAndreas Gohr        if (array_key_exists($plugin, $this->plugin_cascade['protected'])) return false;
150*3a7140a1SAndreas Gohr        $this->tmp_plugins[$plugin] = 0;
151*3a7140a1SAndreas Gohr        return $this->saveList();
152*3a7140a1SAndreas Gohr    }
153*3a7140a1SAndreas Gohr
154*3a7140a1SAndreas Gohr    /**
155*3a7140a1SAndreas Gohr     * Enable the plugin
156*3a7140a1SAndreas Gohr     *
157*3a7140a1SAndreas Gohr     * @param string $plugin name of plugin
158*3a7140a1SAndreas Gohr     * @return bool  true saving succeed, false saving failed
159*3a7140a1SAndreas Gohr     */
160*3a7140a1SAndreas Gohr    public function enable($plugin)
161*3a7140a1SAndreas Gohr    {
162*3a7140a1SAndreas Gohr        if (array_key_exists($plugin, $this->plugin_cascade['protected'])) return false;
163*3a7140a1SAndreas Gohr        $this->tmp_plugins[$plugin] = 1;
164*3a7140a1SAndreas Gohr        return $this->saveList();
165*3a7140a1SAndreas Gohr    }
166*3a7140a1SAndreas Gohr
167*3a7140a1SAndreas Gohr    /**
168*3a7140a1SAndreas Gohr     * Returns directory name of plugin
169*3a7140a1SAndreas Gohr     *
170*3a7140a1SAndreas Gohr     * @param string $plugin name of plugin
171*3a7140a1SAndreas Gohr     * @return string name of directory
172*3a7140a1SAndreas Gohr     */
173*3a7140a1SAndreas Gohr    public function get_directory($plugin)
174*3a7140a1SAndreas Gohr    {
175*3a7140a1SAndreas Gohr        return $plugin;
176*3a7140a1SAndreas Gohr    }
177*3a7140a1SAndreas Gohr
178*3a7140a1SAndreas Gohr    /**
179*3a7140a1SAndreas Gohr     * Returns cascade of the config files
180*3a7140a1SAndreas Gohr     *
181*3a7140a1SAndreas Gohr     * @return array with arrays of plugin configs
182*3a7140a1SAndreas Gohr     */
183*3a7140a1SAndreas Gohr    public function getCascade()
184*3a7140a1SAndreas Gohr    {
185*3a7140a1SAndreas Gohr        return $this->plugin_cascade;
186*3a7140a1SAndreas Gohr    }
187*3a7140a1SAndreas Gohr
188*3a7140a1SAndreas Gohr    protected function _populateMasterList()
189*3a7140a1SAndreas Gohr    {
190*3a7140a1SAndreas Gohr        global $conf;
191*3a7140a1SAndreas Gohr
192*3a7140a1SAndreas Gohr        if ($dh = @opendir(DOKU_PLUGIN)) {
193*3a7140a1SAndreas Gohr            $all_plugins = array();
194*3a7140a1SAndreas Gohr            while (false !== ($plugin = readdir($dh))) {
195*3a7140a1SAndreas Gohr                if ($plugin[0] == '.') continue;               // skip hidden entries
196*3a7140a1SAndreas Gohr                if (is_file(DOKU_PLUGIN . $plugin)) continue;    // skip files, we're only interested in directories
197*3a7140a1SAndreas Gohr
198*3a7140a1SAndreas Gohr                if (array_key_exists($plugin, $this->tmp_plugins) && $this->tmp_plugins[$plugin] == 0) {
199*3a7140a1SAndreas Gohr                    $all_plugins[$plugin] = 0;
200*3a7140a1SAndreas Gohr
201*3a7140a1SAndreas Gohr                } elseif ((array_key_exists($plugin, $this->tmp_plugins) && $this->tmp_plugins[$plugin] == 1)) {
202*3a7140a1SAndreas Gohr                    $all_plugins[$plugin] = 1;
203*3a7140a1SAndreas Gohr                } else {
204*3a7140a1SAndreas Gohr                    $all_plugins[$plugin] = 1;
205*3a7140a1SAndreas Gohr                }
206*3a7140a1SAndreas Gohr            }
207*3a7140a1SAndreas Gohr            $this->tmp_plugins = $all_plugins;
208*3a7140a1SAndreas Gohr            if (!file_exists($this->last_local_config_file)) {
209*3a7140a1SAndreas Gohr                $this->saveList(true);
210*3a7140a1SAndreas Gohr            }
211*3a7140a1SAndreas Gohr        }
212*3a7140a1SAndreas Gohr    }
213*3a7140a1SAndreas Gohr
214*3a7140a1SAndreas Gohr    /**
215*3a7140a1SAndreas Gohr     * Includes the plugin config $files
216*3a7140a1SAndreas Gohr     * and returns the entries of the $plugins array set in these files
217*3a7140a1SAndreas Gohr     *
218*3a7140a1SAndreas Gohr     * @param array $files list of files to include, latter overrides previous
219*3a7140a1SAndreas Gohr     * @return array with entries of the $plugins arrays of the included files
220*3a7140a1SAndreas Gohr     */
221*3a7140a1SAndreas Gohr    protected function checkRequire($files)
222*3a7140a1SAndreas Gohr    {
223*3a7140a1SAndreas Gohr        $plugins = array();
224*3a7140a1SAndreas Gohr        foreach ($files as $file) {
225*3a7140a1SAndreas Gohr            if (file_exists($file)) {
226*3a7140a1SAndreas Gohr                include_once($file);
227*3a7140a1SAndreas Gohr            }
228*3a7140a1SAndreas Gohr        }
229*3a7140a1SAndreas Gohr        return $plugins;
230*3a7140a1SAndreas Gohr    }
231*3a7140a1SAndreas Gohr
232*3a7140a1SAndreas Gohr    /**
233*3a7140a1SAndreas Gohr     * Save the current list of plugins
234*3a7140a1SAndreas Gohr     *
235*3a7140a1SAndreas Gohr     * @param bool $forceSave ;
236*3a7140a1SAndreas Gohr     *              false to save only when config changed
237*3a7140a1SAndreas Gohr     *              true to always save
238*3a7140a1SAndreas Gohr     * @return bool  true saving succeed, false saving failed
239*3a7140a1SAndreas Gohr     */
240*3a7140a1SAndreas Gohr    protected function saveList($forceSave = false)
241*3a7140a1SAndreas Gohr    {
242*3a7140a1SAndreas Gohr        global $conf;
243*3a7140a1SAndreas Gohr
244*3a7140a1SAndreas Gohr        if (empty($this->tmp_plugins)) return false;
245*3a7140a1SAndreas Gohr
246*3a7140a1SAndreas Gohr        // Rebuild list of local settings
247*3a7140a1SAndreas Gohr        $local_plugins = $this->rebuildLocal();
248*3a7140a1SAndreas Gohr        if ($local_plugins != $this->plugin_cascade['local'] || $forceSave) {
249*3a7140a1SAndreas Gohr            $file = $this->last_local_config_file;
250*3a7140a1SAndreas Gohr            $out = "<?php\n/*\n * Local plugin enable/disable settings\n" .
251*3a7140a1SAndreas Gohr                " * Auto-generated through plugin/extension manager\n *\n" .
252*3a7140a1SAndreas Gohr                " * NOTE: Plugins will not be added to this file unless there " .
253*3a7140a1SAndreas Gohr                "is a need to override a default setting. Plugins are\n" .
254*3a7140a1SAndreas Gohr                " *       enabled by default.\n */\n";
255*3a7140a1SAndreas Gohr            foreach ($local_plugins as $plugin => $value) {
256*3a7140a1SAndreas Gohr                $out .= "\$plugins['$plugin'] = $value;\n";
257*3a7140a1SAndreas Gohr            }
258*3a7140a1SAndreas Gohr            // backup current file (remove any existing backup)
259*3a7140a1SAndreas Gohr            if (file_exists($file)) {
260*3a7140a1SAndreas Gohr                $backup = $file . '.bak';
261*3a7140a1SAndreas Gohr                if (file_exists($backup)) @unlink($backup);
262*3a7140a1SAndreas Gohr                if (!@copy($file, $backup)) return false;
263*3a7140a1SAndreas Gohr                if (!empty($conf['fperm'])) chmod($backup, $conf['fperm']);
264*3a7140a1SAndreas Gohr            }
265*3a7140a1SAndreas Gohr            //check if can open for writing, else restore
266*3a7140a1SAndreas Gohr            return io_saveFile($file, $out);
267*3a7140a1SAndreas Gohr        }
268*3a7140a1SAndreas Gohr        return false;
269*3a7140a1SAndreas Gohr    }
270*3a7140a1SAndreas Gohr
271*3a7140a1SAndreas Gohr    /**
272*3a7140a1SAndreas Gohr     * Rebuild the set of local plugins
273*3a7140a1SAndreas Gohr     *
274*3a7140a1SAndreas Gohr     * @return array array of plugins to be saved in end($config_cascade['plugins']['local'])
275*3a7140a1SAndreas Gohr     */
276*3a7140a1SAndreas Gohr    protected function rebuildLocal()
277*3a7140a1SAndreas Gohr    {
278*3a7140a1SAndreas Gohr        //assign to local variable to avoid overwriting
279*3a7140a1SAndreas Gohr        $backup = $this->tmp_plugins;
280*3a7140a1SAndreas Gohr        //Can't do anything about protected one so rule them out completely
281*3a7140a1SAndreas Gohr        $local_default = array_diff_key($backup, $this->plugin_cascade['protected']);
282*3a7140a1SAndreas Gohr        //Diff between local+default and default
283*3a7140a1SAndreas Gohr        //gives us the ones we need to check and save
284*3a7140a1SAndreas Gohr        $diffed_ones = array_diff_key($local_default, $this->plugin_cascade['default']);
285*3a7140a1SAndreas Gohr        //The ones which we are sure of (list of 0s not in default)
286*3a7140a1SAndreas Gohr        $sure_plugins = array_filter($diffed_ones, array($this, 'negate'));
287*3a7140a1SAndreas Gohr        //the ones in need of diff
288*3a7140a1SAndreas Gohr        $conflicts = array_diff_key($local_default, $diffed_ones);
289*3a7140a1SAndreas Gohr        //The final list
290*3a7140a1SAndreas Gohr        return array_merge($sure_plugins, array_diff_assoc($conflicts, $this->plugin_cascade['default']));
291*3a7140a1SAndreas Gohr    }
292*3a7140a1SAndreas Gohr
293*3a7140a1SAndreas Gohr    /**
294*3a7140a1SAndreas Gohr     * Build the list of plugins and cascade
295*3a7140a1SAndreas Gohr     *
296*3a7140a1SAndreas Gohr     */
297*3a7140a1SAndreas Gohr    protected function loadConfig()
298*3a7140a1SAndreas Gohr    {
299*3a7140a1SAndreas Gohr        global $config_cascade;
300*3a7140a1SAndreas Gohr        foreach (array('default', 'protected') as $type) {
301*3a7140a1SAndreas Gohr            if (array_key_exists($type, $config_cascade['plugins'])) {
302*3a7140a1SAndreas Gohr                $this->plugin_cascade[$type] = $this->checkRequire($config_cascade['plugins'][$type]);
303*3a7140a1SAndreas Gohr            }
304*3a7140a1SAndreas Gohr        }
305*3a7140a1SAndreas Gohr        $local = $config_cascade['plugins']['local'];
306*3a7140a1SAndreas Gohr        $this->last_local_config_file = array_pop($local);
307*3a7140a1SAndreas Gohr        $this->plugin_cascade['local'] = $this->checkRequire(array($this->last_local_config_file));
308*3a7140a1SAndreas Gohr        if (is_array($local)) {
309*3a7140a1SAndreas Gohr            $this->plugin_cascade['default'] = array_merge(
310*3a7140a1SAndreas Gohr                $this->plugin_cascade['default'],
311*3a7140a1SAndreas Gohr                $this->checkRequire($local)
312*3a7140a1SAndreas Gohr            );
313*3a7140a1SAndreas Gohr        }
314*3a7140a1SAndreas Gohr        $this->tmp_plugins = array_merge(
315*3a7140a1SAndreas Gohr            $this->plugin_cascade['default'],
316*3a7140a1SAndreas Gohr            $this->plugin_cascade['local'],
317*3a7140a1SAndreas Gohr            $this->plugin_cascade['protected']
318*3a7140a1SAndreas Gohr        );
319*3a7140a1SAndreas Gohr    }
320*3a7140a1SAndreas Gohr
321*3a7140a1SAndreas Gohr    /**
322*3a7140a1SAndreas Gohr     * Returns a list of available plugin components of given type
323*3a7140a1SAndreas Gohr     *
324*3a7140a1SAndreas Gohr     * @param string $type plugin_type name; the type of plugin to return,
325*3a7140a1SAndreas Gohr     * @param bool $enabled true to return enabled plugins,
326*3a7140a1SAndreas Gohr     *                          false to return disabled plugins
327*3a7140a1SAndreas Gohr     * @return array of plugin components of requested type
328*3a7140a1SAndreas Gohr     */
329*3a7140a1SAndreas Gohr    protected function _getListByType($type, $enabled)
330*3a7140a1SAndreas Gohr    {
331*3a7140a1SAndreas Gohr        $master_list = $enabled
332*3a7140a1SAndreas Gohr            ? array_keys(array_filter($this->tmp_plugins))
333*3a7140a1SAndreas Gohr            : array_keys(array_filter($this->tmp_plugins, array($this, 'negate')));
334*3a7140a1SAndreas Gohr        $plugins = array();
335*3a7140a1SAndreas Gohr
336*3a7140a1SAndreas Gohr        foreach ($master_list as $plugin) {
337*3a7140a1SAndreas Gohr
338*3a7140a1SAndreas Gohr            $basedir = $this->get_directory($plugin);
339*3a7140a1SAndreas Gohr            if (file_exists(DOKU_PLUGIN . "$basedir/$type.php")) {
340*3a7140a1SAndreas Gohr                $plugins[] = $plugin;
341*3a7140a1SAndreas Gohr                continue;
342*3a7140a1SAndreas Gohr            }
343*3a7140a1SAndreas Gohr
344*3a7140a1SAndreas Gohr            $typedir = DOKU_PLUGIN . "$basedir/$type/";
345*3a7140a1SAndreas Gohr            if (is_dir($typedir)) {
346*3a7140a1SAndreas Gohr                if ($dp = opendir($typedir)) {
347*3a7140a1SAndreas Gohr                    while (false !== ($component = readdir($dp))) {
348*3a7140a1SAndreas Gohr                        if (substr($component, 0, 1) == '.' || strtolower(substr($component, -4)) != ".php") continue;
349*3a7140a1SAndreas Gohr                        if (is_file($typedir . $component)) {
350*3a7140a1SAndreas Gohr                            $plugins[] = $plugin . '_' . substr($component, 0, -4);
351*3a7140a1SAndreas Gohr                        }
352*3a7140a1SAndreas Gohr                    }
353*3a7140a1SAndreas Gohr                    closedir($dp);
354*3a7140a1SAndreas Gohr                }
355*3a7140a1SAndreas Gohr            }
356*3a7140a1SAndreas Gohr
357*3a7140a1SAndreas Gohr        }//foreach
358*3a7140a1SAndreas Gohr
359*3a7140a1SAndreas Gohr        return $plugins;
360*3a7140a1SAndreas Gohr    }
361*3a7140a1SAndreas Gohr
362*3a7140a1SAndreas Gohr    /**
363*3a7140a1SAndreas Gohr     * Split name in a plugin name and a component name
364*3a7140a1SAndreas Gohr     *
365*3a7140a1SAndreas Gohr     * @param string $name
366*3a7140a1SAndreas Gohr     * @return array with
367*3a7140a1SAndreas Gohr     *              - plugin name
368*3a7140a1SAndreas Gohr     *              - and component name when available, otherwise empty string
369*3a7140a1SAndreas Gohr     */
370*3a7140a1SAndreas Gohr    protected function _splitName($name)
371*3a7140a1SAndreas Gohr    {
372*3a7140a1SAndreas Gohr        if (array_search($name, array_keys($this->tmp_plugins)) === false) {
373*3a7140a1SAndreas Gohr            return explode('_', $name, 2);
374*3a7140a1SAndreas Gohr        }
375*3a7140a1SAndreas Gohr
376*3a7140a1SAndreas Gohr        return array($name, '');
377*3a7140a1SAndreas Gohr    }
378*3a7140a1SAndreas Gohr
379*3a7140a1SAndreas Gohr    /**
380*3a7140a1SAndreas Gohr     * Returns inverse boolean value of the input
381*3a7140a1SAndreas Gohr     *
382*3a7140a1SAndreas Gohr     * @param mixed $input
383*3a7140a1SAndreas Gohr     * @return bool inversed boolean value of input
384*3a7140a1SAndreas Gohr     */
385*3a7140a1SAndreas Gohr    protected function negate($input)
386*3a7140a1SAndreas Gohr    {
387*3a7140a1SAndreas Gohr        return !(bool)$input;
388*3a7140a1SAndreas Gohr    }
389*3a7140a1SAndreas Gohr}
390