PluginManager.php 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  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. if (isset($metaData[$plugin]['description'])) {
  167. $metaData[$plugin]['description'] = t($metaData[$plugin]['description']);
  168. }
  169. // Read parameters and format them into an array.
  170. if (isset($metaData[$plugin]['parameters'])) {
  171. $params = explode(';', $metaData[$plugin]['parameters']);
  172. } else {
  173. $params = array();
  174. }
  175. $metaData[$plugin]['parameters'] = array();
  176. foreach ($params as $param) {
  177. if (empty($param)) {
  178. continue;
  179. }
  180. $metaData[$plugin]['parameters'][$param]['value'] = '';
  181. // Optional parameter description in parameter.PARAM_NAME=
  182. if (isset($metaData[$plugin]['parameter.'. $param])) {
  183. $metaData[$plugin]['parameters'][$param]['desc'] = t($metaData[$plugin]['parameter.'. $param]);
  184. }
  185. }
  186. }
  187. return $metaData;
  188. }
  189. /**
  190. * Return the list of encountered errors.
  191. *
  192. * @return array List of errors (empty array if none exists).
  193. */
  194. public function getErrors()
  195. {
  196. return $this->errors;
  197. }
  198. }
  199. /**
  200. * Class PluginFileNotFoundException
  201. *
  202. * Raise when plugin files can't be found.
  203. */
  204. class PluginFileNotFoundException extends Exception
  205. {
  206. /**
  207. * Construct exception with plugin name.
  208. * Generate message.
  209. *
  210. * @param string $pluginName name of the plugin not found
  211. */
  212. public function __construct($pluginName)
  213. {
  214. $this->message = sprintf(t('Plugin "%s" files not found.'), $pluginName);
  215. }
  216. }