]> git.immae.eu Git - github/shaarli/Shaarli.git/blobdiff - application/plugin/PluginManager.php
namespacing: \Shaarli\Plugin\PluginManager
[github/shaarli/Shaarli.git] / application / plugin / PluginManager.php
diff --git a/application/plugin/PluginManager.php b/application/plugin/PluginManager.php
new file mode 100644 (file)
index 0000000..f7b24a8
--- /dev/null
@@ -0,0 +1,233 @@
+<?php
+namespace Shaarli\Plugin;
+
+use Shaarli\Config\ConfigManager;
+use Shaarli\Plugin\Exception\PluginFileNotFoundException;
+
+/**
+ * Class PluginManager
+ *
+ * Use to manage, load and execute plugins.
+ */
+class PluginManager
+{
+    /**
+     * List of authorized plugins from configuration file.
+     *
+     * @var array $authorizedPlugins
+     */
+    private $authorizedPlugins;
+
+    /**
+     * List of loaded plugins.
+     *
+     * @var array $loadedPlugins
+     */
+    private $loadedPlugins = array();
+
+    /**
+     * @var ConfigManager Configuration Manager instance.
+     */
+    protected $conf;
+
+    /**
+     * @var array List of plugin errors.
+     */
+    protected $errors;
+
+    /**
+     * Plugins subdirectory.
+     *
+     * @var string $PLUGINS_PATH
+     */
+    public static $PLUGINS_PATH = 'plugins';
+
+    /**
+     * Plugins meta files extension.
+     *
+     * @var string $META_EXT
+     */
+    public static $META_EXT = 'meta';
+
+    /**
+     * Constructor.
+     *
+     * @param ConfigManager $conf Configuration Manager instance.
+     */
+    public function __construct(&$conf)
+    {
+        $this->conf = $conf;
+        $this->errors = array();
+    }
+
+    /**
+     * Load plugins listed in $authorizedPlugins.
+     *
+     * @param array $authorizedPlugins Names of plugin authorized to be loaded.
+     *
+     * @return void
+     */
+    public function load($authorizedPlugins)
+    {
+        $this->authorizedPlugins = $authorizedPlugins;
+
+        $dirs = glob(self::$PLUGINS_PATH . '/*', GLOB_ONLYDIR);
+        $dirnames = array_map('basename', $dirs);
+        foreach ($this->authorizedPlugins as $plugin) {
+            $index = array_search($plugin, $dirnames);
+
+            // plugin authorized, but its folder isn't listed
+            if ($index === false) {
+                continue;
+            }
+
+            try {
+                $this->loadPlugin($dirs[$index], $plugin);
+            } catch (PluginFileNotFoundException $e) {
+                error_log($e->getMessage());
+            }
+        }
+    }
+
+    /**
+     * Execute all plugins registered hook.
+     *
+     * @param string $hook   name of the hook to trigger.
+     * @param array  $data   list of data to manipulate passed by reference.
+     * @param array  $params additional parameters such as page target.
+     *
+     * @return void
+     */
+    public function executeHooks($hook, &$data, $params = array())
+    {
+        if (!empty($params['target'])) {
+            $data['_PAGE_'] = $params['target'];
+        }
+
+        if (isset($params['loggedin'])) {
+            $data['_LOGGEDIN_'] = $params['loggedin'];
+        }
+
+        foreach ($this->loadedPlugins as $plugin) {
+            $hookFunction = $this->buildHookName($hook, $plugin);
+
+            if (function_exists($hookFunction)) {
+                $data = call_user_func($hookFunction, $data, $this->conf);
+            }
+        }
+    }
+
+    /**
+     * Load a single plugin from its files.
+     * Call the init function if it exists, and collect errors.
+     * Add them in $loadedPlugins if successful.
+     *
+     * @param string $dir        plugin's directory.
+     * @param string $pluginName plugin's name.
+     *
+     * @return void
+     * @throws \Shaarli\Plugin\Exception\PluginFileNotFoundException - plugin files not found.
+     */
+    private function loadPlugin($dir, $pluginName)
+    {
+        if (!is_dir($dir)) {
+            throw new PluginFileNotFoundException($pluginName);
+        }
+
+        $pluginFilePath = $dir . '/' . $pluginName . '.php';
+        if (!is_file($pluginFilePath)) {
+            throw new PluginFileNotFoundException($pluginName);
+        }
+
+        $conf = $this->conf;
+        include_once $pluginFilePath;
+
+        $initFunction = $pluginName . '_init';
+        if (function_exists($initFunction)) {
+            $errors = call_user_func($initFunction, $this->conf);
+            if (!empty($errors)) {
+                $this->errors = array_merge($this->errors, $errors);
+            }
+        }
+
+        $this->loadedPlugins[] = $pluginName;
+    }
+
+    /**
+     * Construct normalize hook name for a specific plugin.
+     *
+     * Format:
+     *      hook_<plugin_name>_<hook_name>
+     *
+     * @param string $hook       hook name.
+     * @param string $pluginName plugin name.
+     *
+     * @return string - plugin's hook name.
+     */
+    public function buildHookName($hook, $pluginName)
+    {
+        return 'hook_' . $pluginName . '_' . $hook;
+    }
+
+    /**
+     * Retrieve plugins metadata from *.meta (INI) files into an array.
+     * Metadata contains:
+     *   - plugin description [description]
+     *   - parameters split with ';' [parameters]
+     *
+     * Respects plugins order from settings.
+     *
+     * @return array plugins metadata.
+     */
+    public function getPluginsMeta()
+    {
+        $metaData = array();
+        $dirs = glob(self::$PLUGINS_PATH . '/*', GLOB_ONLYDIR | GLOB_MARK);
+
+        // Browse all plugin directories.
+        foreach ($dirs as $pluginDir) {
+            $plugin = basename($pluginDir);
+            $metaFile = $pluginDir . $plugin . '.' . self::$META_EXT;
+            if (!is_file($metaFile) || !is_readable($metaFile)) {
+                continue;
+            }
+
+            $metaData[$plugin] = parse_ini_file($metaFile);
+            $metaData[$plugin]['order'] = array_search($plugin, $this->authorizedPlugins);
+
+            if (isset($metaData[$plugin]['description'])) {
+                $metaData[$plugin]['description'] = t($metaData[$plugin]['description']);
+            }
+            // Read parameters and format them into an array.
+            if (isset($metaData[$plugin]['parameters'])) {
+                $params = explode(';', $metaData[$plugin]['parameters']);
+            } else {
+                $params = array();
+            }
+            $metaData[$plugin]['parameters'] = array();
+            foreach ($params as $param) {
+                if (empty($param)) {
+                    continue;
+                }
+
+                $metaData[$plugin]['parameters'][$param]['value'] = '';
+                // Optional parameter description in parameter.PARAM_NAME=
+                if (isset($metaData[$plugin]['parameter.' . $param])) {
+                    $metaData[$plugin]['parameters'][$param]['desc'] = t($metaData[$plugin]['parameter.' . $param]);
+                }
+            }
+        }
+
+        return $metaData;
+    }
+
+    /**
+     * Return the list of encountered errors.
+     *
+     * @return array List of errors (empty array if none exists).
+     */
+    public function getErrors()
+    {
+        return $this->errors;
+    }
+}