PluginManager.php 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  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. } catch (PluginFileNotFoundException $e) {
  68. error_log($e->getMessage());
  69. }
  70. }
  71. }
  72. /**
  73. * Execute all plugins registered hook.
  74. *
  75. * @param string $hook name of the hook to trigger.
  76. * @param array $data list of data to manipulate passed by reference.
  77. * @param array $params additional parameters such as page target.
  78. *
  79. * @return void
  80. */
  81. public function executeHooks($hook, &$data, $params = array())
  82. {
  83. if (!empty($params['target'])) {
  84. $data['_PAGE_'] = $params['target'];
  85. }
  86. if (isset($params['loggedin'])) {
  87. $data['_LOGGEDIN_'] = $params['loggedin'];
  88. }
  89. foreach ($this->loadedPlugins as $plugin) {
  90. $hookFunction = $this->buildHookName($hook, $plugin);
  91. if (function_exists($hookFunction)) {
  92. $data = call_user_func($hookFunction, $data, $this->conf);
  93. }
  94. }
  95. }
  96. /**
  97. * Load a single plugin from its files.
  98. * Call the init function if it exists, and collect errors.
  99. * Add them in $loadedPlugins if successful.
  100. *
  101. * @param string $dir plugin's directory.
  102. * @param string $pluginName plugin's name.
  103. *
  104. * @return void
  105. * @throws PluginFileNotFoundException - plugin files not found.
  106. */
  107. private function loadPlugin($dir, $pluginName)
  108. {
  109. if (!is_dir($dir)) {
  110. throw new PluginFileNotFoundException($pluginName);
  111. }
  112. $pluginFilePath = $dir . '/' . $pluginName . '.php';
  113. if (!is_file($pluginFilePath)) {
  114. throw new PluginFileNotFoundException($pluginName);
  115. }
  116. $conf = $this->conf;
  117. include_once $pluginFilePath;
  118. $initFunction = $pluginName . '_init';
  119. if (function_exists($initFunction)) {
  120. $errors = call_user_func($initFunction, $this->conf);
  121. if (!empty($errors)) {
  122. $this->errors = array_merge($this->errors, $errors);
  123. }
  124. }
  125. $this->loadedPlugins[] = $pluginName;
  126. }
  127. /**
  128. * Construct normalize hook name for a specific plugin.
  129. *
  130. * Format:
  131. * hook_<plugin_name>_<hook_name>
  132. *
  133. * @param string $hook hook name.
  134. * @param string $pluginName plugin name.
  135. *
  136. * @return string - plugin's hook name.
  137. */
  138. public function buildHookName($hook, $pluginName)
  139. {
  140. return 'hook_' . $pluginName . '_' . $hook;
  141. }
  142. /**
  143. * Retrieve plugins metadata from *.meta (INI) files into an array.
  144. * Metadata contains:
  145. * - plugin description [description]
  146. * - parameters split with ';' [parameters]
  147. *
  148. * Respects plugins order from settings.
  149. *
  150. * @return array plugins metadata.
  151. */
  152. public function getPluginsMeta()
  153. {
  154. $metaData = array();
  155. $dirs = glob(self::$PLUGINS_PATH . '/*', GLOB_ONLYDIR | GLOB_MARK);
  156. // Browse all plugin directories.
  157. foreach ($dirs as $pluginDir) {
  158. $plugin = basename($pluginDir);
  159. $metaFile = $pluginDir . $plugin . '.' . self::$META_EXT;
  160. if (!is_file($metaFile) || !is_readable($metaFile)) {
  161. continue;
  162. }
  163. $metaData[$plugin] = parse_ini_file($metaFile);
  164. $metaData[$plugin]['order'] = array_search($plugin, $this->authorizedPlugins);
  165. if (isset($metaData[$plugin]['description'])) {
  166. $metaData[$plugin]['description'] = t($metaData[$plugin]['description']);
  167. }
  168. // Read parameters and format them into an array.
  169. if (isset($metaData[$plugin]['parameters'])) {
  170. $params = explode(';', $metaData[$plugin]['parameters']);
  171. } else {
  172. $params = array();
  173. }
  174. $metaData[$plugin]['parameters'] = array();
  175. foreach ($params as $param) {
  176. if (empty($param)) {
  177. continue;
  178. }
  179. $metaData[$plugin]['parameters'][$param]['value'] = '';
  180. // Optional parameter description in parameter.PARAM_NAME=
  181. if (isset($metaData[$plugin]['parameter.'. $param])) {
  182. $metaData[$plugin]['parameters'][$param]['desc'] = t($metaData[$plugin]['parameter.'. $param]);
  183. }
  184. }
  185. }
  186. return $metaData;
  187. }
  188. /**
  189. * Return the list of encountered errors.
  190. *
  191. * @return array List of errors (empty array if none exists).
  192. */
  193. public function getErrors()
  194. {
  195. return $this->errors;
  196. }
  197. }
  198. /**
  199. * Class PluginFileNotFoundException
  200. *
  201. * Raise when plugin files can't be found.
  202. */
  203. class PluginFileNotFoundException extends Exception
  204. {
  205. /**
  206. * Construct exception with plugin name.
  207. * Generate message.
  208. *
  209. * @param string $pluginName name of the plugin not found
  210. */
  211. public function __construct($pluginName)
  212. {
  213. $this->message = sprintf(t('Plugin "%s" files not found.'), $pluginName);
  214. }
  215. }