xref: /dokuwiki/inc/Extension/PluginController.php (revision ec34bb300b254ecd0dba0fac22d8115635141cc5)
13a7140a1SAndreas Gohr<?php
2c904b9fbSAndreas Gohr
3c904b9fbSAndreas Gohrnamespace dokuwiki\Extension;
4c904b9fbSAndreas Gohr
5642e976cSAndreas Gohruse dokuwiki\ErrorHandler;
6642e976cSAndreas Gohr
73a7140a1SAndreas Gohr/**
83a7140a1SAndreas Gohr * Class to encapsulate access to dokuwiki plugins
93a7140a1SAndreas Gohr *
103a7140a1SAndreas Gohr * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
113a7140a1SAndreas Gohr * @author     Christopher Smith <chris@jalakai.co.uk>
123a7140a1SAndreas Gohr */
133a7140a1SAndreas Gohrclass PluginController
143a7140a1SAndreas Gohr{
151935a891SAndreas Gohr    /** @var array the types of plugins DokuWiki supports */
161935a891SAndreas Gohr    const PLUGIN_TYPES = ['auth', 'admin', 'syntax', 'action', 'renderer', 'helper', 'remote', 'cli'];
173a7140a1SAndreas Gohr
18c904b9fbSAndreas Gohr    protected $listByType = [];
19c904b9fbSAndreas Gohr    /** @var array all installed plugins and their enabled state [plugin=>enabled] */
20c904b9fbSAndreas Gohr    protected $masterList = [];
21c904b9fbSAndreas Gohr    protected $pluginCascade = ['default' => [], 'local' => [], 'protected' => []];
22c904b9fbSAndreas Gohr    protected $lastLocalConfigFile = '';
233a7140a1SAndreas Gohr
243a7140a1SAndreas Gohr    /**
253a7140a1SAndreas Gohr     * Populates the master list of plugins
263a7140a1SAndreas Gohr     */
273a7140a1SAndreas Gohr    public function __construct()
283a7140a1SAndreas Gohr    {
293a7140a1SAndreas Gohr        $this->loadConfig();
30c904b9fbSAndreas Gohr        $this->populateMasterList();
313a7140a1SAndreas Gohr    }
323a7140a1SAndreas Gohr
333a7140a1SAndreas Gohr    /**
343a7140a1SAndreas Gohr     * Returns a list of available plugins of given type
353a7140a1SAndreas Gohr     *
363a7140a1SAndreas Gohr     * @param $type  string, plugin_type name;
373a7140a1SAndreas Gohr     *               the type of plugin to return,
383a7140a1SAndreas Gohr     *               use empty string for all types
393a7140a1SAndreas Gohr     * @param $all   bool;
403a7140a1SAndreas Gohr     *               false to only return enabled plugins,
413a7140a1SAndreas Gohr     *               true to return both enabled and disabled plugins
423a7140a1SAndreas Gohr     *
433a7140a1SAndreas Gohr     * @return       array of
443a7140a1SAndreas Gohr     *                  - plugin names when $type = ''
453a7140a1SAndreas Gohr     *                  - or plugin component names when a $type is given
463a7140a1SAndreas Gohr     *
473a7140a1SAndreas Gohr     * @author Andreas Gohr <andi@splitbrain.org>
483a7140a1SAndreas Gohr     */
493a7140a1SAndreas Gohr    public function getList($type = '', $all = false)
503a7140a1SAndreas Gohr    {
513a7140a1SAndreas Gohr
523a7140a1SAndreas Gohr        // request the complete list
533a7140a1SAndreas Gohr        if (!$type) {
54c904b9fbSAndreas Gohr            return $all ? array_keys($this->masterList) : array_keys(array_filter($this->masterList));
553a7140a1SAndreas Gohr        }
563a7140a1SAndreas Gohr
57c904b9fbSAndreas Gohr        if (!isset($this->listByType[$type]['enabled'])) {
58c904b9fbSAndreas Gohr            $this->listByType[$type]['enabled'] = $this->getListByType($type, true);
593a7140a1SAndreas Gohr        }
60c904b9fbSAndreas Gohr        if ($all && !isset($this->listByType[$type]['disabled'])) {
61c904b9fbSAndreas Gohr            $this->listByType[$type]['disabled'] = $this->getListByType($type, false);
623a7140a1SAndreas Gohr        }
633a7140a1SAndreas Gohr
643a7140a1SAndreas Gohr        return $all
65c904b9fbSAndreas Gohr            ? array_merge($this->listByType[$type]['enabled'], $this->listByType[$type]['disabled'])
66c904b9fbSAndreas Gohr            : $this->listByType[$type]['enabled'];
673a7140a1SAndreas Gohr    }
683a7140a1SAndreas Gohr
693a7140a1SAndreas Gohr    /**
703a7140a1SAndreas Gohr     * Loads the given plugin and creates an object of it
713a7140a1SAndreas Gohr     *
723a7140a1SAndreas Gohr     * @param  $type     string type of plugin to load
733a7140a1SAndreas Gohr     * @param  $name     string name of the plugin to load
743a7140a1SAndreas Gohr     * @param  $new      bool   true to return a new instance of the plugin, false to use an already loaded instance
753a7140a1SAndreas Gohr     * @param  $disabled bool   true to load even disabled plugins
763a7140a1SAndreas Gohr     * @return PluginInterface|null  the plugin object or null on failure
77c904b9fbSAndreas Gohr     * @author Andreas Gohr <andi@splitbrain.org>
78c904b9fbSAndreas Gohr     *
793a7140a1SAndreas Gohr     */
803a7140a1SAndreas Gohr    public function load($type, $name, $new = false, $disabled = false)
813a7140a1SAndreas Gohr    {
823a7140a1SAndreas Gohr
833a7140a1SAndreas Gohr        //we keep all loaded plugins available in global scope for reuse
843a7140a1SAndreas Gohr        global $DOKU_PLUGINS;
853a7140a1SAndreas Gohr
86c904b9fbSAndreas Gohr        list($plugin, /* $component */) = $this->splitName($name);
873a7140a1SAndreas Gohr
883a7140a1SAndreas Gohr        // check if disabled
89c904b9fbSAndreas Gohr        if (!$disabled && !$this->isEnabled($plugin)) {
903a7140a1SAndreas Gohr            return null;
913a7140a1SAndreas Gohr        }
923a7140a1SAndreas Gohr
933a7140a1SAndreas Gohr        $class = $type . '_plugin_' . $name;
943a7140a1SAndreas Gohr
95642e976cSAndreas Gohr        try {
963a7140a1SAndreas Gohr            //plugin already loaded?
973a7140a1SAndreas Gohr            if (!empty($DOKU_PLUGINS[$type][$name])) {
983a7140a1SAndreas Gohr                if ($new || !$DOKU_PLUGINS[$type][$name]->isSingleton()) {
99642e976cSAndreas Gohr
1003a7140a1SAndreas Gohr                    return class_exists($class, true) ? new $class : null;
1013a7140a1SAndreas Gohr                }
102c904b9fbSAndreas Gohr
103c904b9fbSAndreas Gohr                return $DOKU_PLUGINS[$type][$name];
1043a7140a1SAndreas Gohr            }
1053a7140a1SAndreas Gohr
1063a7140a1SAndreas Gohr            //construct class and instantiate
1073a7140a1SAndreas Gohr            if (!class_exists($class, true)) {
1083a7140a1SAndreas Gohr                # the plugin might be in the wrong directory
109f219f385SAndreas Gohr                $inf = confToHash(DOKU_PLUGIN . "$plugin/plugin.info.txt");
1103a7140a1SAndreas Gohr                if ($inf['base'] && $inf['base'] != $plugin) {
1113a7140a1SAndreas Gohr                    msg(
1123a7140a1SAndreas Gohr                        sprintf(
1133a7140a1SAndreas Gohr                            "Plugin installed incorrectly. Rename plugin directory '%s' to '%s'.",
1143a7140a1SAndreas Gohr                            hsc($plugin),
1153a7140a1SAndreas Gohr                            hsc(
1163a7140a1SAndreas Gohr                                $inf['base']
1173a7140a1SAndreas Gohr                            )
1183a7140a1SAndreas Gohr                        ), -1
1193a7140a1SAndreas Gohr                    );
1203a7140a1SAndreas Gohr                } elseif (preg_match('/^' . DOKU_PLUGIN_NAME_REGEX . '$/', $plugin) !== 1) {
1213a7140a1SAndreas Gohr                    msg(
1223a7140a1SAndreas Gohr                        sprintf(
12303e8a69aSAndreas Gohr                            "Plugin name '%s' is not a valid plugin name, only the characters a-z ".
12403e8a69aSAndreas Gohr                            "and 0-9 are allowed. " .
1253a7140a1SAndreas Gohr                            'Maybe the plugin has been installed in the wrong directory?', hsc($plugin)
1263a7140a1SAndreas Gohr                        ), -1
1273a7140a1SAndreas Gohr                    );
1283a7140a1SAndreas Gohr                }
1293a7140a1SAndreas Gohr                return null;
1303a7140a1SAndreas Gohr            }
1313a7140a1SAndreas Gohr            $DOKU_PLUGINS[$type][$name] = new $class;
132642e976cSAndreas Gohr
133ffa84f81SAndreas Gohr        } catch (\Throwable $e) {
134642e976cSAndreas Gohr            ErrorHandler::showExceptionMsg($e, sprintf('Failed to load plugin %s', $plugin));
135642e976cSAndreas Gohr            return null;
136642e976cSAndreas Gohr        }
137642e976cSAndreas Gohr
1383a7140a1SAndreas Gohr        return $DOKU_PLUGINS[$type][$name];
1393a7140a1SAndreas Gohr    }
1403a7140a1SAndreas Gohr
1413a7140a1SAndreas Gohr    /**
1423a7140a1SAndreas Gohr     * Whether plugin is disabled
1433a7140a1SAndreas Gohr     *
1443a7140a1SAndreas Gohr     * @param string $plugin name of plugin
1453a7140a1SAndreas Gohr     * @return bool  true disabled, false enabled
146fbccc3e6SAndreas Gohr     * @deprecated in favor of the more sensible isEnabled where the return value matches the enabled state
1473a7140a1SAndreas Gohr     */
148fbccc3e6SAndreas Gohr    public function isDisabled($plugin)
1493a7140a1SAndreas Gohr    {
150fbccc3e6SAndreas Gohr        dbg_deprecated('isEnabled()');
151fbccc3e6SAndreas Gohr        return !$this->isEnabled($plugin);
152fbccc3e6SAndreas Gohr    }
153fbccc3e6SAndreas Gohr
154fbccc3e6SAndreas Gohr    /**
155fbccc3e6SAndreas Gohr     * Check whether plugin is disabled
156fbccc3e6SAndreas Gohr     *
157fbccc3e6SAndreas Gohr     * @param string $plugin name of plugin
158fbccc3e6SAndreas Gohr     * @return bool  true enabled, false disabled
159fbccc3e6SAndreas Gohr     */
160fbccc3e6SAndreas Gohr    public function isEnabled($plugin)
161fbccc3e6SAndreas Gohr    {
162c904b9fbSAndreas Gohr        return !empty($this->masterList[$plugin]);
1633a7140a1SAndreas Gohr    }
1643a7140a1SAndreas Gohr
1653a7140a1SAndreas Gohr    /**
1663a7140a1SAndreas Gohr     * Disable the plugin
1673a7140a1SAndreas Gohr     *
1683a7140a1SAndreas Gohr     * @param string $plugin name of plugin
1693a7140a1SAndreas Gohr     * @return bool  true saving succeed, false saving failed
1703a7140a1SAndreas Gohr     */
1713a7140a1SAndreas Gohr    public function disable($plugin)
1723a7140a1SAndreas Gohr    {
173c904b9fbSAndreas Gohr        if (array_key_exists($plugin, $this->pluginCascade['protected'])) return false;
174c904b9fbSAndreas Gohr        $this->masterList[$plugin] = 0;
1753a7140a1SAndreas Gohr        return $this->saveList();
1763a7140a1SAndreas Gohr    }
1773a7140a1SAndreas Gohr
1783a7140a1SAndreas Gohr    /**
1793a7140a1SAndreas Gohr     * Enable the plugin
1803a7140a1SAndreas Gohr     *
1813a7140a1SAndreas Gohr     * @param string $plugin name of plugin
1823a7140a1SAndreas Gohr     * @return bool  true saving succeed, false saving failed
1833a7140a1SAndreas Gohr     */
1843a7140a1SAndreas Gohr    public function enable($plugin)
1853a7140a1SAndreas Gohr    {
186c904b9fbSAndreas Gohr        if (array_key_exists($plugin, $this->pluginCascade['protected'])) return false;
187c904b9fbSAndreas Gohr        $this->masterList[$plugin] = 1;
1883a7140a1SAndreas Gohr        return $this->saveList();
1893a7140a1SAndreas Gohr    }
1903a7140a1SAndreas Gohr
1913a7140a1SAndreas Gohr    /**
1923a7140a1SAndreas Gohr     * Returns cascade of the config files
1933a7140a1SAndreas Gohr     *
1943a7140a1SAndreas Gohr     * @return array with arrays of plugin configs
1953a7140a1SAndreas Gohr     */
1963a7140a1SAndreas Gohr    public function getCascade()
1973a7140a1SAndreas Gohr    {
198c904b9fbSAndreas Gohr        return $this->pluginCascade;
1993a7140a1SAndreas Gohr    }
2003a7140a1SAndreas Gohr
201c904b9fbSAndreas Gohr    /**
202c904b9fbSAndreas Gohr     * Read all installed plugins and their current enabled state
203c904b9fbSAndreas Gohr     */
204c904b9fbSAndreas Gohr    protected function populateMasterList()
2053a7140a1SAndreas Gohr    {
2063a7140a1SAndreas Gohr        if ($dh = @opendir(DOKU_PLUGIN)) {
2073a7140a1SAndreas Gohr            $all_plugins = array();
2083a7140a1SAndreas Gohr            while (false !== ($plugin = readdir($dh))) {
209c904b9fbSAndreas Gohr                if ($plugin[0] === '.') continue;               // skip hidden entries
2103a7140a1SAndreas Gohr                if (is_file(DOKU_PLUGIN . $plugin)) continue;    // skip files, we're only interested in directories
2113a7140a1SAndreas Gohr
212c904b9fbSAndreas Gohr                if (array_key_exists($plugin, $this->masterList) && $this->masterList[$plugin] == 0) {
2133a7140a1SAndreas Gohr                    $all_plugins[$plugin] = 0;
2143a7140a1SAndreas Gohr
215c904b9fbSAndreas Gohr                } elseif (array_key_exists($plugin, $this->masterList) && $this->masterList[$plugin] == 1) {
2163a7140a1SAndreas Gohr                    $all_plugins[$plugin] = 1;
2173a7140a1SAndreas Gohr                } else {
2183a7140a1SAndreas Gohr                    $all_plugins[$plugin] = 1;
2193a7140a1SAndreas Gohr                }
2203a7140a1SAndreas Gohr            }
221c904b9fbSAndreas Gohr            $this->masterList = $all_plugins;
222c904b9fbSAndreas Gohr            if (!file_exists($this->lastLocalConfigFile)) {
2233a7140a1SAndreas Gohr                $this->saveList(true);
2243a7140a1SAndreas Gohr            }
2253a7140a1SAndreas Gohr        }
2263a7140a1SAndreas Gohr    }
2273a7140a1SAndreas Gohr
2283a7140a1SAndreas Gohr    /**
2293a7140a1SAndreas Gohr     * Includes the plugin config $files
2303a7140a1SAndreas Gohr     * and returns the entries of the $plugins array set in these files
2313a7140a1SAndreas Gohr     *
2323a7140a1SAndreas Gohr     * @param array $files list of files to include, latter overrides previous
2333a7140a1SAndreas Gohr     * @return array with entries of the $plugins arrays of the included files
2343a7140a1SAndreas Gohr     */
2353a7140a1SAndreas Gohr    protected function checkRequire($files)
2363a7140a1SAndreas Gohr    {
2373a7140a1SAndreas Gohr        $plugins = array();
2383a7140a1SAndreas Gohr        foreach ($files as $file) {
2393a7140a1SAndreas Gohr            if (file_exists($file)) {
2403a7140a1SAndreas Gohr                include_once($file);
2413a7140a1SAndreas Gohr            }
2423a7140a1SAndreas Gohr        }
2433a7140a1SAndreas Gohr        return $plugins;
2443a7140a1SAndreas Gohr    }
2453a7140a1SAndreas Gohr
2463a7140a1SAndreas Gohr    /**
2473a7140a1SAndreas Gohr     * Save the current list of plugins
2483a7140a1SAndreas Gohr     *
2493a7140a1SAndreas Gohr     * @param bool $forceSave ;
2503a7140a1SAndreas Gohr     *              false to save only when config changed
2513a7140a1SAndreas Gohr     *              true to always save
2523a7140a1SAndreas Gohr     * @return bool  true saving succeed, false saving failed
2533a7140a1SAndreas Gohr     */
2543a7140a1SAndreas Gohr    protected function saveList($forceSave = false)
2553a7140a1SAndreas Gohr    {
2563a7140a1SAndreas Gohr        global $conf;
2573a7140a1SAndreas Gohr
258c904b9fbSAndreas Gohr        if (empty($this->masterList)) return false;
2593a7140a1SAndreas Gohr
2603a7140a1SAndreas Gohr        // Rebuild list of local settings
2613a7140a1SAndreas Gohr        $local_plugins = $this->rebuildLocal();
262c904b9fbSAndreas Gohr        if ($local_plugins != $this->pluginCascade['local'] || $forceSave) {
263c904b9fbSAndreas Gohr            $file = $this->lastLocalConfigFile;
2643a7140a1SAndreas Gohr            $out = "<?php\n/*\n * Local plugin enable/disable settings\n" .
2653a7140a1SAndreas Gohr                " * Auto-generated through plugin/extension manager\n *\n" .
2663a7140a1SAndreas Gohr                " * NOTE: Plugins will not be added to this file unless there " .
2673a7140a1SAndreas Gohr                "is a need to override a default setting. Plugins are\n" .
2683a7140a1SAndreas Gohr                " *       enabled by default.\n */\n";
2693a7140a1SAndreas Gohr            foreach ($local_plugins as $plugin => $value) {
2703a7140a1SAndreas Gohr                $out .= "\$plugins['$plugin'] = $value;\n";
2713a7140a1SAndreas Gohr            }
2723a7140a1SAndreas Gohr            // backup current file (remove any existing backup)
2733a7140a1SAndreas Gohr            if (file_exists($file)) {
2743a7140a1SAndreas Gohr                $backup = $file . '.bak';
2753a7140a1SAndreas Gohr                if (file_exists($backup)) @unlink($backup);
2763a7140a1SAndreas Gohr                if (!@copy($file, $backup)) return false;
27723420346SDamien Regad                if ($conf['fperm']) chmod($backup, $conf['fperm']);
2783a7140a1SAndreas Gohr            }
2793a7140a1SAndreas Gohr            //check if can open for writing, else restore
2803a7140a1SAndreas Gohr            return io_saveFile($file, $out);
2813a7140a1SAndreas Gohr        }
2823a7140a1SAndreas Gohr        return false;
2833a7140a1SAndreas Gohr    }
2843a7140a1SAndreas Gohr
2853a7140a1SAndreas Gohr    /**
2863a7140a1SAndreas Gohr     * Rebuild the set of local plugins
2873a7140a1SAndreas Gohr     *
2883a7140a1SAndreas Gohr     * @return array array of plugins to be saved in end($config_cascade['plugins']['local'])
2893a7140a1SAndreas Gohr     */
2903a7140a1SAndreas Gohr    protected function rebuildLocal()
2913a7140a1SAndreas Gohr    {
2923a7140a1SAndreas Gohr        //assign to local variable to avoid overwriting
293c904b9fbSAndreas Gohr        $backup = $this->masterList;
2943a7140a1SAndreas Gohr        //Can't do anything about protected one so rule them out completely
295c904b9fbSAndreas Gohr        $local_default = array_diff_key($backup, $this->pluginCascade['protected']);
2963a7140a1SAndreas Gohr        //Diff between local+default and default
2973a7140a1SAndreas Gohr        //gives us the ones we need to check and save
298c904b9fbSAndreas Gohr        $diffed_ones = array_diff_key($local_default, $this->pluginCascade['default']);
2993a7140a1SAndreas Gohr        //The ones which we are sure of (list of 0s not in default)
3003a7140a1SAndreas Gohr        $sure_plugins = array_filter($diffed_ones, array($this, 'negate'));
3013a7140a1SAndreas Gohr        //the ones in need of diff
3023a7140a1SAndreas Gohr        $conflicts = array_diff_key($local_default, $diffed_ones);
3033a7140a1SAndreas Gohr        //The final list
304c904b9fbSAndreas Gohr        return array_merge($sure_plugins, array_diff_assoc($conflicts, $this->pluginCascade['default']));
3053a7140a1SAndreas Gohr    }
3063a7140a1SAndreas Gohr
3073a7140a1SAndreas Gohr    /**
3083a7140a1SAndreas Gohr     * Build the list of plugins and cascade
3093a7140a1SAndreas Gohr     *
3103a7140a1SAndreas Gohr     */
3113a7140a1SAndreas Gohr    protected function loadConfig()
3123a7140a1SAndreas Gohr    {
3133a7140a1SAndreas Gohr        global $config_cascade;
3143a7140a1SAndreas Gohr        foreach (array('default', 'protected') as $type) {
3153a7140a1SAndreas Gohr            if (array_key_exists($type, $config_cascade['plugins'])) {
316c904b9fbSAndreas Gohr                $this->pluginCascade[$type] = $this->checkRequire($config_cascade['plugins'][$type]);
3173a7140a1SAndreas Gohr            }
3183a7140a1SAndreas Gohr        }
3193a7140a1SAndreas Gohr        $local = $config_cascade['plugins']['local'];
320c904b9fbSAndreas Gohr        $this->lastLocalConfigFile = array_pop($local);
321c904b9fbSAndreas Gohr        $this->pluginCascade['local'] = $this->checkRequire(array($this->lastLocalConfigFile));
3223a7140a1SAndreas Gohr        if (is_array($local)) {
323c904b9fbSAndreas Gohr            $this->pluginCascade['default'] = array_merge(
324c904b9fbSAndreas Gohr                $this->pluginCascade['default'],
3253a7140a1SAndreas Gohr                $this->checkRequire($local)
3263a7140a1SAndreas Gohr            );
3273a7140a1SAndreas Gohr        }
328c904b9fbSAndreas Gohr        $this->masterList = array_merge(
329c904b9fbSAndreas Gohr            $this->pluginCascade['default'],
330c904b9fbSAndreas Gohr            $this->pluginCascade['local'],
331c904b9fbSAndreas Gohr            $this->pluginCascade['protected']
3323a7140a1SAndreas Gohr        );
3333a7140a1SAndreas Gohr    }
3343a7140a1SAndreas Gohr
3353a7140a1SAndreas Gohr    /**
3363a7140a1SAndreas Gohr     * Returns a list of available plugin components of given type
3373a7140a1SAndreas Gohr     *
3383a7140a1SAndreas Gohr     * @param string $type plugin_type name; the type of plugin to return,
3393a7140a1SAndreas Gohr     * @param bool $enabled true to return enabled plugins,
3403a7140a1SAndreas Gohr     *                          false to return disabled plugins
3413a7140a1SAndreas Gohr     * @return array of plugin components of requested type
3423a7140a1SAndreas Gohr     */
343c904b9fbSAndreas Gohr    protected function getListByType($type, $enabled)
3443a7140a1SAndreas Gohr    {
3453a7140a1SAndreas Gohr        $master_list = $enabled
346c904b9fbSAndreas Gohr            ? array_keys(array_filter($this->masterList))
347c904b9fbSAndreas Gohr            : array_keys(array_filter($this->masterList, array($this, 'negate')));
3483a7140a1SAndreas Gohr        $plugins = array();
3493a7140a1SAndreas Gohr
3503a7140a1SAndreas Gohr        foreach ($master_list as $plugin) {
3513a7140a1SAndreas Gohr
352f219f385SAndreas Gohr            if (file_exists(DOKU_PLUGIN . "$plugin/$type.php")) {
3533a7140a1SAndreas Gohr                $plugins[] = $plugin;
3543a7140a1SAndreas Gohr                continue;
3553a7140a1SAndreas Gohr            }
3563a7140a1SAndreas Gohr
357f219f385SAndreas Gohr            $typedir = DOKU_PLUGIN . "$plugin/$type/";
3583a7140a1SAndreas Gohr            if (is_dir($typedir)) {
3593a7140a1SAndreas Gohr                if ($dp = opendir($typedir)) {
3603a7140a1SAndreas Gohr                    while (false !== ($component = readdir($dp))) {
361c47e6665SAndreas Gohr                        if (strpos($component, '.') === 0 || strtolower(substr($component, -4)) !== '.php') continue;
3623a7140a1SAndreas Gohr                        if (is_file($typedir . $component)) {
3633a7140a1SAndreas Gohr                            $plugins[] = $plugin . '_' . substr($component, 0, -4);
3643a7140a1SAndreas Gohr                        }
3653a7140a1SAndreas Gohr                    }
3663a7140a1SAndreas Gohr                    closedir($dp);
3673a7140a1SAndreas Gohr                }
3683a7140a1SAndreas Gohr            }
3693a7140a1SAndreas Gohr
3703a7140a1SAndreas Gohr        }//foreach
3713a7140a1SAndreas Gohr
3723a7140a1SAndreas Gohr        return $plugins;
3733a7140a1SAndreas Gohr    }
3743a7140a1SAndreas Gohr
3753a7140a1SAndreas Gohr    /**
3763a7140a1SAndreas Gohr     * Split name in a plugin name and a component name
3773a7140a1SAndreas Gohr     *
3783a7140a1SAndreas Gohr     * @param string $name
3793a7140a1SAndreas Gohr     * @return array with
3803a7140a1SAndreas Gohr     *              - plugin name
3813a7140a1SAndreas Gohr     *              - and component name when available, otherwise empty string
3823a7140a1SAndreas Gohr     */
383c904b9fbSAndreas Gohr    protected function splitName($name)
3843a7140a1SAndreas Gohr    {
385c47e6665SAndreas Gohr        if (!isset($this->masterList[$name])) {
386*ec34bb30SAndreas Gohr            return sexplode('_', $name, 2, '');
3873a7140a1SAndreas Gohr        }
3883a7140a1SAndreas Gohr
3893a7140a1SAndreas Gohr        return array($name, '');
3903a7140a1SAndreas Gohr    }
3913a7140a1SAndreas Gohr
3923a7140a1SAndreas Gohr    /**
3933a7140a1SAndreas Gohr     * Returns inverse boolean value of the input
3943a7140a1SAndreas Gohr     *
3953a7140a1SAndreas Gohr     * @param mixed $input
3963a7140a1SAndreas Gohr     * @return bool inversed boolean value of input
3973a7140a1SAndreas Gohr     */
3983a7140a1SAndreas Gohr    protected function negate($input)
3993a7140a1SAndreas Gohr    {
4003a7140a1SAndreas Gohr        return !(bool)$input;
4013a7140a1SAndreas Gohr    }
4023a7140a1SAndreas Gohr}
403