]> git.immae.eu Git - github/shaarli/Shaarli.git/blob - application/PluginManager.php
1e132a7f652c78aa077a86ed3c1013ad58b7c95b
[github/shaarli/Shaarli.git] / application / PluginManager.php
1 <?php
2
3 /**
4 * Class PluginManager
5 *
6 * Use to manage, load and execute plugins.
7 */
8 class PluginManager
9 {
10 /**
11 * List of authorized plugins from configuration file.
12 * @var array $authorizedPlugins
13 */
14 private $authorizedPlugins;
15
16 /**
17 * List of loaded plugins.
18 * @var array $loadedPlugins
19 */
20 private $loadedPlugins = array();
21
22 /**
23 * @var ConfigManager Configuration Manager instance.
24 */
25 protected $conf;
26
27 /**
28 * Plugins subdirectory.
29 * @var string $PLUGINS_PATH
30 */
31 public static $PLUGINS_PATH = 'plugins';
32
33 /**
34 * Plugins meta files extension.
35 * @var string $META_EXT
36 */
37 public static $META_EXT = 'meta';
38
39 /**
40 * Constructor.
41 *
42 * @param ConfigManager $conf Configuration Manager instance.
43 */
44 public function __construct(&$conf)
45 {
46 $this->conf = $conf;
47 }
48
49 /**
50 * Load plugins listed in $authorizedPlugins.
51 *
52 * @param array $authorizedPlugins Names of plugin authorized to be loaded.
53 *
54 * @return void
55 */
56 public function load($authorizedPlugins)
57 {
58 $this->authorizedPlugins = $authorizedPlugins;
59
60 $dirs = glob(self::$PLUGINS_PATH . '/*', GLOB_ONLYDIR);
61 $dirnames = array_map('basename', $dirs);
62 foreach ($this->authorizedPlugins as $plugin) {
63 $index = array_search($plugin, $dirnames);
64
65 // plugin authorized, but its folder isn't listed
66 if ($index === false) {
67 continue;
68 }
69
70 try {
71 $this->loadPlugin($dirs[$index], $plugin);
72 }
73 catch (PluginFileNotFoundException $e) {
74 error_log($e->getMessage());
75 }
76 }
77 }
78
79 /**
80 * Execute all plugins registered hook.
81 *
82 * @param string $hook name of the hook to trigger.
83 * @param array $data list of data to manipulate passed by reference.
84 * @param array $params additional parameters such as page target.
85 *
86 * @return void
87 */
88 public function executeHooks($hook, &$data, $params = array())
89 {
90 if (!empty($params['target'])) {
91 $data['_PAGE_'] = $params['target'];
92 }
93
94 if (isset($params['loggedin'])) {
95 $data['_LOGGEDIN_'] = $params['loggedin'];
96 }
97
98 foreach ($this->loadedPlugins as $plugin) {
99 $hookFunction = $this->buildHookName($hook, $plugin);
100
101 if (function_exists($hookFunction)) {
102 $data = call_user_func($hookFunction, $data, $this->conf);
103 }
104 }
105 }
106
107 /**
108 * Load a single plugin from its files.
109 * Add them in $loadedPlugins if successful.
110 *
111 * @param string $dir plugin's directory.
112 * @param string $pluginName plugin's name.
113 *
114 * @return void
115 * @throws PluginFileNotFoundException - plugin files not found.
116 */
117 private function loadPlugin($dir, $pluginName)
118 {
119 if (!is_dir($dir)) {
120 throw new PluginFileNotFoundException($pluginName);
121 }
122
123 $pluginFilePath = $dir . '/' . $pluginName . '.php';
124 if (!is_file($pluginFilePath)) {
125 throw new PluginFileNotFoundException($pluginName);
126 }
127
128 $conf = $this->conf;
129 include_once $pluginFilePath;
130
131 $this->loadedPlugins[] = $pluginName;
132 }
133
134 /**
135 * Construct normalize hook name for a specific plugin.
136 *
137 * Format:
138 * hook_<plugin_name>_<hook_name>
139 *
140 * @param string $hook hook name.
141 * @param string $pluginName plugin name.
142 *
143 * @return string - plugin's hook name.
144 */
145 public function buildHookName($hook, $pluginName)
146 {
147 return 'hook_' . $pluginName . '_' . $hook;
148 }
149
150 /**
151 * Retrieve plugins metadata from *.meta (INI) files into an array.
152 * Metadata contains:
153 * - plugin description [description]
154 * - parameters split with ';' [parameters]
155 *
156 * Respects plugins order from settings.
157 *
158 * @return array plugins metadata.
159 */
160 public function getPluginsMeta()
161 {
162 $metaData = array();
163 $dirs = glob(self::$PLUGINS_PATH . '/*', GLOB_ONLYDIR | GLOB_MARK);
164
165 // Browse all plugin directories.
166 foreach ($dirs as $pluginDir) {
167 $plugin = basename($pluginDir);
168 $metaFile = $pluginDir . $plugin . '.' . self::$META_EXT;
169 if (!is_file($metaFile) || !is_readable($metaFile)) {
170 continue;
171 }
172
173 $metaData[$plugin] = parse_ini_file($metaFile);
174 $metaData[$plugin]['order'] = array_search($plugin, $this->authorizedPlugins);
175
176 // Read parameters and format them into an array.
177 if (isset($metaData[$plugin]['parameters'])) {
178 $params = explode(';', $metaData[$plugin]['parameters']);
179 } else {
180 $params = array();
181 }
182 $metaData[$plugin]['parameters'] = array();
183 foreach ($params as $param) {
184 if (empty($param)) {
185 continue;
186 }
187
188 $metaData[$plugin]['parameters'][$param]['value'] = '';
189 // Optional parameter description in parameter.PARAM_NAME=
190 if (isset($metaData[$plugin]['parameter.'. $param])) {
191 $metaData[$plugin]['parameters'][$param]['desc'] = $metaData[$plugin]['parameter.'. $param];
192 }
193 }
194 }
195
196 return $metaData;
197 }
198 }
199
200 /**
201 * Class PluginFileNotFoundException
202 *
203 * Raise when plugin files can't be found.
204 */
205 class PluginFileNotFoundException extends Exception
206 {
207 /**
208 * Construct exception with plugin name.
209 * Generate message.
210 *
211 * @param string $pluginName name of the plugin not found
212 */
213 public function __construct($pluginName)
214 {
215 $this->message = 'Plugin "'. $pluginName .'" files not found.';
216 }
217 }