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