PluginManager.php 6.6 KB

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