PluginManager.php 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. <?php
  2. /**
  3. * Class PluginManager
  4. *
  5. * Use to manage, load and execute plugins.
  6. *
  7. * Using Singleton design pattern.
  8. */
  9. class PluginManager
  10. {
  11. /**
  12. * PluginManager singleton instance.
  13. * @var PluginManager $instance
  14. */
  15. private static $instance;
  16. /**
  17. * List of authorized plugins from configuration file.
  18. * @var array $authorizedPlugins
  19. */
  20. private $authorizedPlugins;
  21. /**
  22. * List of loaded plugins.
  23. * @var array $loadedPlugins
  24. */
  25. private $loadedPlugins = array();
  26. /**
  27. * Plugins subdirectory.
  28. * @var string $PLUGINS_PATH
  29. */
  30. public static $PLUGINS_PATH = 'plugins';
  31. /**
  32. * Private constructor: new instances not allowed.
  33. */
  34. private function __construct()
  35. {
  36. }
  37. /**
  38. * Cloning isn't allowed either.
  39. *
  40. * @return void
  41. */
  42. private function __clone()
  43. {
  44. }
  45. /**
  46. * Return existing instance of PluginManager, or create it.
  47. *
  48. * @return PluginManager instance.
  49. */
  50. public static function getInstance()
  51. {
  52. if (!(self::$instance instanceof self)) {
  53. self::$instance = new self();
  54. }
  55. return self::$instance;
  56. }
  57. /**
  58. * Load plugins listed in $authorizedPlugins.
  59. *
  60. * @param array $authorizedPlugins Names of plugin authorized to be loaded.
  61. *
  62. * @return void
  63. */
  64. public function load($authorizedPlugins)
  65. {
  66. $this->authorizedPlugins = $authorizedPlugins;
  67. $dirs = glob(self::$PLUGINS_PATH . '/*', GLOB_ONLYDIR);
  68. $dirnames = array_map('basename', $dirs);
  69. foreach ($this->authorizedPlugins as $plugin) {
  70. $index = array_search($plugin, $dirnames);
  71. // plugin authorized, but its folder isn't listed
  72. if ($index === false) {
  73. continue;
  74. }
  75. try {
  76. $this->loadPlugin($dirs[$index], $plugin);
  77. }
  78. catch (PluginFileNotFoundException $e) {
  79. error_log($e->getMessage());
  80. }
  81. }
  82. }
  83. /**
  84. * Execute all plugins registered hook.
  85. *
  86. * @param string $hook name of the hook to trigger.
  87. * @param array $data list of data to manipulate passed by reference.
  88. * @param array $params additional parameters such as page target.
  89. *
  90. * @return void
  91. */
  92. public function executeHooks($hook, &$data, $params = array())
  93. {
  94. if (!empty($params['target'])) {
  95. $data['_PAGE_'] = $params['target'];
  96. }
  97. if (isset($params['loggedin'])) {
  98. $data['_LOGGEDIN_'] = $params['loggedin'];
  99. }
  100. foreach ($this->loadedPlugins as $plugin) {
  101. $hookFunction = $this->buildHookName($hook, $plugin);
  102. if (function_exists($hookFunction)) {
  103. $data = call_user_func($hookFunction, $data);
  104. }
  105. }
  106. }
  107. /**
  108. * Load a single plugin from its files.
  109. * Add them in $loadedPlugins if successful.
  110. *
  111. * @param string $dir plugin's directory.
  112. * @param string $pluginName plugin's name.
  113. *
  114. * @return void
  115. * @throws PluginFileNotFoundException - plugin files not found.
  116. */
  117. private function loadPlugin($dir, $pluginName)
  118. {
  119. if (!is_dir($dir)) {
  120. throw new PluginFileNotFoundException($pluginName);
  121. }
  122. $pluginFilePath = $dir . '/' . $pluginName . '.php';
  123. if (!is_file($pluginFilePath)) {
  124. throw new PluginFileNotFoundException($pluginName);
  125. }
  126. include_once $pluginFilePath;
  127. $this->loadedPlugins[] = $pluginName;
  128. }
  129. /**
  130. * Construct normalize hook name for a specific plugin.
  131. *
  132. * Format:
  133. * hook_<plugin_name>_<hook_name>
  134. *
  135. * @param string $hook hook name.
  136. * @param string $pluginName plugin name.
  137. *
  138. * @return string - plugin's hook name.
  139. */
  140. public function buildHookName($hook, $pluginName)
  141. {
  142. return 'hook_' . $pluginName . '_' . $hook;
  143. }
  144. }
  145. /**
  146. * Class PluginFileNotFoundException
  147. *
  148. * Raise when plugin files can't be found.
  149. */
  150. class PluginFileNotFoundException extends Exception
  151. {
  152. /**
  153. * Construct exception with plugin name.
  154. * Generate message.
  155. *
  156. * @param string $pluginName name of the plugin not found
  157. */
  158. public function __construct($pluginName)
  159. {
  160. $this->message = 'Plugin "'. $pluginName .'" files not found.';
  161. }
  162. }