xref: /dokuwiki/inc/Extension/PluginController.php (revision 73022918a947abda7eee4d7d2302ffd28fdb78e0)
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 */
1674981a4eSAndreas Gohr    public 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
86615810c5SAndreas Gohr        [$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
100*73022918SAndreas 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) {
121d9156e4dSAndreas Gohr                    msg(sprintf(
1221490c177SAndreas Gohr                        'Plugin name \'%s\' is not a valid plugin name, only the characters a-z and 0-9 are allowed. ' .
1233a7140a1SAndreas Gohr                        'Maybe the plugin has been installed in the wrong directory?', hsc($plugin)
124d9156e4dSAndreas Gohr                    ), -1);
1253a7140a1SAndreas Gohr                }
1263a7140a1SAndreas Gohr                return null;
1273a7140a1SAndreas Gohr            }
128*73022918SAndreas Gohr            $DOKU_PLUGINS[$type][$name] = new $class();
129642e976cSAndreas Gohr
130ffa84f81SAndreas Gohr        } catch (\Throwable $e) {
131642e976cSAndreas Gohr            ErrorHandler::showExceptionMsg($e, sprintf('Failed to load plugin %s', $plugin));
132642e976cSAndreas Gohr            return null;
133642e976cSAndreas Gohr        }
134642e976cSAndreas Gohr
1353a7140a1SAndreas Gohr        return $DOKU_PLUGINS[$type][$name];
1363a7140a1SAndreas Gohr    }
1373a7140a1SAndreas Gohr
1383a7140a1SAndreas Gohr    /**
1393a7140a1SAndreas Gohr     * Whether plugin is disabled
1403a7140a1SAndreas Gohr     *
1413a7140a1SAndreas Gohr     * @param string $plugin name of plugin
1423a7140a1SAndreas Gohr     * @return bool  true disabled, false enabled
143fbccc3e6SAndreas Gohr     * @deprecated in favor of the more sensible isEnabled where the return value matches the enabled state
1443a7140a1SAndreas Gohr     */
145fbccc3e6SAndreas Gohr    public function isDisabled($plugin)
1463a7140a1SAndreas Gohr    {
147fbccc3e6SAndreas Gohr        dbg_deprecated('isEnabled()');
148fbccc3e6SAndreas Gohr        return !$this->isEnabled($plugin);
149fbccc3e6SAndreas Gohr    }
150fbccc3e6SAndreas Gohr
151fbccc3e6SAndreas Gohr    /**
152fbccc3e6SAndreas Gohr     * Check whether plugin is disabled
153fbccc3e6SAndreas Gohr     *
154fbccc3e6SAndreas Gohr     * @param string $plugin name of plugin
155fbccc3e6SAndreas Gohr     * @return bool  true enabled, false disabled
156fbccc3e6SAndreas Gohr     */
157fbccc3e6SAndreas Gohr    public function isEnabled($plugin)
158fbccc3e6SAndreas Gohr    {
159c904b9fbSAndreas Gohr        return !empty($this->masterList[$plugin]);
1603a7140a1SAndreas Gohr    }
1613a7140a1SAndreas Gohr
1623a7140a1SAndreas Gohr    /**
1633a7140a1SAndreas Gohr     * Disable the plugin
1643a7140a1SAndreas Gohr     *
1653a7140a1SAndreas Gohr     * @param string $plugin name of plugin
1663a7140a1SAndreas Gohr     * @return bool  true saving succeed, false saving failed
1673a7140a1SAndreas Gohr     */
1683a7140a1SAndreas Gohr    public function disable($plugin)
1693a7140a1SAndreas Gohr    {
170c904b9fbSAndreas Gohr        if (array_key_exists($plugin, $this->pluginCascade['protected'])) return false;
171c904b9fbSAndreas Gohr        $this->masterList[$plugin] = 0;
1723a7140a1SAndreas Gohr        return $this->saveList();
1733a7140a1SAndreas Gohr    }
1743a7140a1SAndreas Gohr
1753a7140a1SAndreas Gohr    /**
1763a7140a1SAndreas Gohr     * Enable the plugin
1773a7140a1SAndreas Gohr     *
1783a7140a1SAndreas Gohr     * @param string $plugin name of plugin
1793a7140a1SAndreas Gohr     * @return bool  true saving succeed, false saving failed
1803a7140a1SAndreas Gohr     */
1813a7140a1SAndreas Gohr    public function enable($plugin)
1823a7140a1SAndreas Gohr    {
183c904b9fbSAndreas Gohr        if (array_key_exists($plugin, $this->pluginCascade['protected'])) return false;
184c904b9fbSAndreas Gohr        $this->masterList[$plugin] = 1;
1853a7140a1SAndreas Gohr        return $this->saveList();
1863a7140a1SAndreas Gohr    }
1873a7140a1SAndreas Gohr
1883a7140a1SAndreas Gohr    /**
1893a7140a1SAndreas Gohr     * Returns cascade of the config files
1903a7140a1SAndreas Gohr     *
1913a7140a1SAndreas Gohr     * @return array with arrays of plugin configs
1923a7140a1SAndreas Gohr     */
1933a7140a1SAndreas Gohr    public function getCascade()
1943a7140a1SAndreas Gohr    {
195c904b9fbSAndreas Gohr        return $this->pluginCascade;
1963a7140a1SAndreas Gohr    }
1973a7140a1SAndreas Gohr
198c904b9fbSAndreas Gohr    /**
199c904b9fbSAndreas Gohr     * Read all installed plugins and their current enabled state
200c904b9fbSAndreas Gohr     */
201c904b9fbSAndreas Gohr    protected function populateMasterList()
2023a7140a1SAndreas Gohr    {
2033a7140a1SAndreas Gohr        if ($dh = @opendir(DOKU_PLUGIN)) {
2041490c177SAndreas Gohr            $all_plugins = [];
2053a7140a1SAndreas Gohr            while (false !== ($plugin = readdir($dh))) {
206c904b9fbSAndreas Gohr                if ($plugin[0] === '.') continue;               // skip hidden entries
2073a7140a1SAndreas Gohr                if (is_file(DOKU_PLUGIN . $plugin)) continue;    // skip files, we're only interested in directories
2083a7140a1SAndreas Gohr
209c904b9fbSAndreas Gohr                if (array_key_exists($plugin, $this->masterList) && $this->masterList[$plugin] == 0) {
2103a7140a1SAndreas Gohr                    $all_plugins[$plugin] = 0;
2113a7140a1SAndreas Gohr
212c904b9fbSAndreas Gohr                } elseif (array_key_exists($plugin, $this->masterList) && $this->masterList[$plugin] == 1) {
2133a7140a1SAndreas Gohr                    $all_plugins[$plugin] = 1;
2143a7140a1SAndreas Gohr                } else {
2153a7140a1SAndreas Gohr                    $all_plugins[$plugin] = 1;
2163a7140a1SAndreas Gohr                }
2173a7140a1SAndreas Gohr            }
218c904b9fbSAndreas Gohr            $this->masterList = $all_plugins;
219c904b9fbSAndreas Gohr            if (!file_exists($this->lastLocalConfigFile)) {
2203a7140a1SAndreas Gohr                $this->saveList(true);
2213a7140a1SAndreas Gohr            }
2223a7140a1SAndreas Gohr        }
2233a7140a1SAndreas Gohr    }
2243a7140a1SAndreas Gohr
2253a7140a1SAndreas Gohr    /**
2263a7140a1SAndreas Gohr     * Includes the plugin config $files
2273a7140a1SAndreas Gohr     * and returns the entries of the $plugins array set in these files
2283a7140a1SAndreas Gohr     *
2293a7140a1SAndreas Gohr     * @param array $files list of files to include, latter overrides previous
2303a7140a1SAndreas Gohr     * @return array with entries of the $plugins arrays of the included files
2313a7140a1SAndreas Gohr     */
2323a7140a1SAndreas Gohr    protected function checkRequire($files)
2333a7140a1SAndreas Gohr    {
2341490c177SAndreas Gohr        $plugins = [];
2353a7140a1SAndreas Gohr        foreach ($files as $file) {
2363a7140a1SAndreas Gohr            if (file_exists($file)) {
2373a7140a1SAndreas Gohr                include_once($file);
2383a7140a1SAndreas Gohr            }
2393a7140a1SAndreas Gohr        }
2403a7140a1SAndreas Gohr        return $plugins;
2413a7140a1SAndreas Gohr    }
2423a7140a1SAndreas Gohr
2433a7140a1SAndreas Gohr    /**
2443a7140a1SAndreas Gohr     * Save the current list of plugins
2453a7140a1SAndreas Gohr     *
2463a7140a1SAndreas Gohr     * @param bool $forceSave ;
2473a7140a1SAndreas Gohr     *              false to save only when config changed
2483a7140a1SAndreas Gohr     *              true to always save
2493a7140a1SAndreas Gohr     * @return bool  true saving succeed, false saving failed
2503a7140a1SAndreas Gohr     */
2513a7140a1SAndreas Gohr    protected function saveList($forceSave = false)
2523a7140a1SAndreas Gohr    {
2533a7140a1SAndreas Gohr        global $conf;
2543a7140a1SAndreas Gohr
255c904b9fbSAndreas Gohr        if (empty($this->masterList)) return false;
2563a7140a1SAndreas Gohr
2573a7140a1SAndreas Gohr        // Rebuild list of local settings
2583a7140a1SAndreas Gohr        $local_plugins = $this->rebuildLocal();
259c904b9fbSAndreas Gohr        if ($local_plugins != $this->pluginCascade['local'] || $forceSave) {
260c904b9fbSAndreas Gohr            $file = $this->lastLocalConfigFile;
2613a7140a1SAndreas Gohr            $out = "<?php\n/*\n * Local plugin enable/disable settings\n" .
2623a7140a1SAndreas Gohr                " * Auto-generated through plugin/extension manager\n *\n" .
2633a7140a1SAndreas Gohr                " * NOTE: Plugins will not be added to this file unless there " .
2643a7140a1SAndreas Gohr                "is a need to override a default setting. Plugins are\n" .
2653a7140a1SAndreas Gohr                " *       enabled by default.\n */\n";
2663a7140a1SAndreas Gohr            foreach ($local_plugins as $plugin => $value) {
2673a7140a1SAndreas Gohr                $out .= "\$plugins['$plugin'] = $value;\n";
2683a7140a1SAndreas Gohr            }
2693a7140a1SAndreas Gohr            // backup current file (remove any existing backup)
2703a7140a1SAndreas Gohr            if (file_exists($file)) {
2713a7140a1SAndreas Gohr                $backup = $file . '.bak';
2723a7140a1SAndreas Gohr                if (file_exists($backup)) @unlink($backup);
2733a7140a1SAndreas Gohr                if (!@copy($file, $backup)) return false;
27423420346SDamien Regad                if ($conf['fperm']) chmod($backup, $conf['fperm']);
2753a7140a1SAndreas Gohr            }
2763a7140a1SAndreas Gohr            //check if can open for writing, else restore
2773a7140a1SAndreas Gohr            return io_saveFile($file, $out);
2783a7140a1SAndreas Gohr        }
2793a7140a1SAndreas Gohr        return false;
2803a7140a1SAndreas Gohr    }
2813a7140a1SAndreas Gohr
2823a7140a1SAndreas Gohr    /**
2833a7140a1SAndreas Gohr     * Rebuild the set of local plugins
2843a7140a1SAndreas Gohr     *
2853a7140a1SAndreas Gohr     * @return array array of plugins to be saved in end($config_cascade['plugins']['local'])
2863a7140a1SAndreas Gohr     */
2873a7140a1SAndreas Gohr    protected function rebuildLocal()
2883a7140a1SAndreas Gohr    {
2893a7140a1SAndreas Gohr        //assign to local variable to avoid overwriting
290c904b9fbSAndreas Gohr        $backup = $this->masterList;
2913a7140a1SAndreas Gohr        //Can't do anything about protected one so rule them out completely
292c904b9fbSAndreas Gohr        $local_default = array_diff_key($backup, $this->pluginCascade['protected']);
2933a7140a1SAndreas Gohr        //Diff between local+default and default
2943a7140a1SAndreas Gohr        //gives us the ones we need to check and save
295c904b9fbSAndreas Gohr        $diffed_ones = array_diff_key($local_default, $this->pluginCascade['default']);
2963a7140a1SAndreas Gohr        //The ones which we are sure of (list of 0s not in default)
2971490c177SAndreas Gohr        $sure_plugins = array_filter($diffed_ones, [$this, 'negate']);
2983a7140a1SAndreas Gohr        //the ones in need of diff
2993a7140a1SAndreas Gohr        $conflicts = array_diff_key($local_default, $diffed_ones);
3003a7140a1SAndreas Gohr        //The final list
301c904b9fbSAndreas Gohr        return array_merge($sure_plugins, array_diff_assoc($conflicts, $this->pluginCascade['default']));
3023a7140a1SAndreas Gohr    }
3033a7140a1SAndreas Gohr
3043a7140a1SAndreas Gohr    /**
3053a7140a1SAndreas Gohr     * Build the list of plugins and cascade
3063a7140a1SAndreas Gohr     *
3073a7140a1SAndreas Gohr     */
3083a7140a1SAndreas Gohr    protected function loadConfig()
3093a7140a1SAndreas Gohr    {
3103a7140a1SAndreas Gohr        global $config_cascade;
3111490c177SAndreas Gohr        foreach (['default', 'protected'] as $type) {
3123a7140a1SAndreas Gohr            if (array_key_exists($type, $config_cascade['plugins'])) {
313c904b9fbSAndreas Gohr                $this->pluginCascade[$type] = $this->checkRequire($config_cascade['plugins'][$type]);
3143a7140a1SAndreas Gohr            }
3153a7140a1SAndreas Gohr        }
3163a7140a1SAndreas Gohr        $local = $config_cascade['plugins']['local'];
317c904b9fbSAndreas Gohr        $this->lastLocalConfigFile = array_pop($local);
3181490c177SAndreas Gohr        $this->pluginCascade['local'] = $this->checkRequire([$this->lastLocalConfigFile]);
319c904b9fbSAndreas Gohr        $this->pluginCascade['default'] = array_merge(
320c904b9fbSAndreas Gohr            $this->pluginCascade['default'],
3213a7140a1SAndreas Gohr            $this->checkRequire($local)
3223a7140a1SAndreas Gohr        );
323c904b9fbSAndreas Gohr        $this->masterList = array_merge(
324c904b9fbSAndreas Gohr            $this->pluginCascade['default'],
325c904b9fbSAndreas Gohr            $this->pluginCascade['local'],
326c904b9fbSAndreas Gohr            $this->pluginCascade['protected']
3273a7140a1SAndreas Gohr        );
3283a7140a1SAndreas Gohr    }
3293a7140a1SAndreas Gohr
3303a7140a1SAndreas Gohr    /**
3313a7140a1SAndreas Gohr     * Returns a list of available plugin components of given type
3323a7140a1SAndreas Gohr     *
3333a7140a1SAndreas Gohr     * @param string $type plugin_type name; the type of plugin to return,
3343a7140a1SAndreas Gohr     * @param bool $enabled true to return enabled plugins,
3353a7140a1SAndreas Gohr     *                          false to return disabled plugins
3363a7140a1SAndreas Gohr     * @return array of plugin components of requested type
3373a7140a1SAndreas Gohr     */
338c904b9fbSAndreas Gohr    protected function getListByType($type, $enabled)
3393a7140a1SAndreas Gohr    {
3403a7140a1SAndreas Gohr        $master_list = $enabled
341c904b9fbSAndreas Gohr            ? array_keys(array_filter($this->masterList))
3421490c177SAndreas Gohr            : array_keys(array_filter($this->masterList, [$this, 'negate']));
3431490c177SAndreas Gohr        $plugins = [];
3443a7140a1SAndreas Gohr
3453a7140a1SAndreas Gohr        foreach ($master_list as $plugin) {
3463a7140a1SAndreas Gohr
347f219f385SAndreas Gohr            if (file_exists(DOKU_PLUGIN . "$plugin/$type.php")) {
3483a7140a1SAndreas Gohr                $plugins[] = $plugin;
3493a7140a1SAndreas Gohr                continue;
3503a7140a1SAndreas Gohr            }
3513a7140a1SAndreas Gohr
352f219f385SAndreas Gohr            $typedir = DOKU_PLUGIN . "$plugin/$type/";
3533a7140a1SAndreas Gohr            if (is_dir($typedir)) {
3543a7140a1SAndreas Gohr                if ($dp = opendir($typedir)) {
3553a7140a1SAndreas Gohr                    while (false !== ($component = readdir($dp))) {
356c47e6665SAndreas Gohr                        if (strpos($component, '.') === 0 || strtolower(substr($component, -4)) !== '.php') continue;
3573a7140a1SAndreas Gohr                        if (is_file($typedir . $component)) {
3583a7140a1SAndreas Gohr                            $plugins[] = $plugin . '_' . substr($component, 0, -4);
3593a7140a1SAndreas Gohr                        }
3603a7140a1SAndreas Gohr                    }
3613a7140a1SAndreas Gohr                    closedir($dp);
3623a7140a1SAndreas Gohr                }
3633a7140a1SAndreas Gohr            }
3643a7140a1SAndreas Gohr
3653a7140a1SAndreas Gohr        }//foreach
3663a7140a1SAndreas Gohr
3673a7140a1SAndreas Gohr        return $plugins;
3683a7140a1SAndreas Gohr    }
3693a7140a1SAndreas Gohr
3703a7140a1SAndreas Gohr    /**
3713a7140a1SAndreas Gohr     * Split name in a plugin name and a component name
3723a7140a1SAndreas Gohr     *
3733a7140a1SAndreas Gohr     * @param string $name
3743a7140a1SAndreas Gohr     * @return array with
3753a7140a1SAndreas Gohr     *              - plugin name
3763a7140a1SAndreas Gohr     *              - and component name when available, otherwise empty string
3773a7140a1SAndreas Gohr     */
378c904b9fbSAndreas Gohr    protected function splitName($name)
3793a7140a1SAndreas Gohr    {
380c47e6665SAndreas Gohr        if (!isset($this->masterList[$name])) {
381ec34bb30SAndreas Gohr            return sexplode('_', $name, 2, '');
3823a7140a1SAndreas Gohr        }
3833a7140a1SAndreas Gohr
3841490c177SAndreas Gohr        return [$name, ''];
3853a7140a1SAndreas Gohr    }
3863a7140a1SAndreas Gohr
3873a7140a1SAndreas Gohr    /**
3883a7140a1SAndreas Gohr     * Returns inverse boolean value of the input
3893a7140a1SAndreas Gohr     *
3903a7140a1SAndreas Gohr     * @param mixed $input
3913a7140a1SAndreas Gohr     * @return bool inversed boolean value of input
3923a7140a1SAndreas Gohr     */
3933a7140a1SAndreas Gohr    protected function negate($input)
3943a7140a1SAndreas Gohr    {
3953a7140a1SAndreas Gohr        return !(bool)$input;
3963a7140a1SAndreas Gohr    }
3973a7140a1SAndreas Gohr}
398