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