AbstractPicoPlugin.php 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. <?php
  2. /**
  3. * Abstract class to extend from when implementing a Pico plugin
  4. *
  5. * @see PicoPluginInterface
  6. *
  7. * @author Daniel Rudolf
  8. * @link http://picocms.org
  9. * @license http://opensource.org/licenses/MIT
  10. * @version 1.0
  11. */
  12. abstract class AbstractPicoPlugin implements PicoPluginInterface
  13. {
  14. /**
  15. * Current instance of Pico
  16. *
  17. * @see PicoPluginInterface::getPico()
  18. * @var Pico
  19. */
  20. private $pico;
  21. /**
  22. * Boolean indicating if this plugin is enabled (true) or disabled (false)
  23. *
  24. * @see PicoPluginInterface::isEnabled()
  25. * @see PicoPluginInterface::setEnabled()
  26. * @var boolean
  27. */
  28. protected $enabled = true;
  29. /**
  30. * Boolean indicating if this plugin was ever enabled/disabled manually
  31. *
  32. * @see PicoPluginInterface::isStatusChanged()
  33. * @var boolean
  34. */
  35. protected $statusChanged = false;
  36. /**
  37. * List of plugins which this plugin depends on
  38. *
  39. * @see AbstractPicoPlugin::checkDependencies()
  40. * @see PicoPluginInterface::getDependencies()
  41. * @var string[]
  42. */
  43. protected $dependsOn = array();
  44. /**
  45. * List of plugin which depend on this plugin
  46. *
  47. * @see AbstractPicoPlugin::checkDependants()
  48. * @see PicoPluginInterface::getDependants()
  49. * @var object[]
  50. */
  51. private $dependants;
  52. /**
  53. * @see PicoPluginInterface::__construct()
  54. */
  55. public function __construct(Pico $pico)
  56. {
  57. $this->pico = $pico;
  58. }
  59. /**
  60. * @see PicoPluginInterface::handleEvent()
  61. */
  62. public function handleEvent($eventName, array $params)
  63. {
  64. // plugins can be enabled/disabled using the config
  65. if ($eventName === 'onConfigLoaded') {
  66. $pluginEnabled = $this->getConfig(get_called_class() . '.enabled');
  67. if ($pluginEnabled !== null) {
  68. $this->setEnabled($pluginEnabled);
  69. } else {
  70. $pluginConfig = $this->getConfig(get_called_class());
  71. if (is_array($pluginConfig) && isset($pluginConfig['enabled'])) {
  72. $this->setEnabled($pluginConfig['enabled']);
  73. } elseif ($this->enabled) {
  74. // make sure dependencies are already fulfilled,
  75. // otherwise the plugin needs to be enabled manually
  76. try {
  77. $this->checkDependencies(false);
  78. } catch (RuntimeException $e) {
  79. $this->enabled = false;
  80. }
  81. }
  82. }
  83. }
  84. if ($this->isEnabled() || ($eventName === 'onPluginsLoaded')) {
  85. if (method_exists($this, $eventName)) {
  86. call_user_func_array(array($this, $eventName), $params);
  87. }
  88. }
  89. }
  90. /**
  91. * @see PicoPluginInterface::setEnabled()
  92. */
  93. public function setEnabled($enabled, $recursive = true, $auto = false)
  94. {
  95. $this->statusChanged = (!$this->statusChanged) ? !$auto : true;
  96. $this->enabled = (bool) $enabled;
  97. if ($enabled) {
  98. $this->checkDependencies($recursive);
  99. } else {
  100. $this->checkDependants($recursive);
  101. }
  102. }
  103. /**
  104. * @see PicoPluginInterface::isEnabled()
  105. */
  106. public function isEnabled()
  107. {
  108. return $this->enabled;
  109. }
  110. /**
  111. * @see PicoPluginInterface::isStatusChanged()
  112. */
  113. public function isStatusChanged()
  114. {
  115. return $this->statusChanged;
  116. }
  117. /**
  118. * @see PicoPluginInterface::getPico()
  119. */
  120. public function getPico()
  121. {
  122. return $this->pico;
  123. }
  124. /**
  125. * Passes all not satisfiable method calls to Pico
  126. *
  127. * @see Pico
  128. * @param string $methodName name of the method to call
  129. * @param array $params parameters to pass
  130. * @return mixed return value of the called method
  131. */
  132. public function __call($methodName, array $params)
  133. {
  134. if (method_exists($this->getPico(), $methodName)) {
  135. return call_user_func_array(array($this->getPico(), $methodName), $params);
  136. }
  137. throw new BadMethodCallException(
  138. 'Call to undefined method ' . get_class($this->getPico()) . '::' . $methodName . '() '
  139. . 'through ' . get_called_class() . '::__call()'
  140. );
  141. }
  142. /**
  143. * Enables all plugins which this plugin depends on
  144. *
  145. * @see PicoPluginInterface::getDependencies()
  146. * @param boolean $recursive enable required plugins automatically
  147. * @return void
  148. * @throws RuntimeException thrown when a dependency fails
  149. */
  150. protected function checkDependencies($recursive)
  151. {
  152. foreach ($this->getDependencies() as $pluginName) {
  153. try {
  154. $plugin = $this->getPlugin($pluginName);
  155. } catch (RuntimeException $e) {
  156. throw new RuntimeException(
  157. "Unable to enable plugin '" . get_called_class() . "': "
  158. . "Required plugin '" . $pluginName . "' not found"
  159. );
  160. }
  161. // plugins which don't implement PicoPluginInterface are always enabled
  162. if (is_a($plugin, 'PicoPluginInterface') && !$plugin->isEnabled()) {
  163. if ($recursive) {
  164. if (!$plugin->isStatusChanged()) {
  165. $plugin->setEnabled(true, true, true);
  166. } else {
  167. throw new RuntimeException(
  168. "Unable to enable plugin '" . get_called_class() . "': "
  169. . "Required plugin '" . $pluginName . "' was disabled manually"
  170. );
  171. }
  172. } else {
  173. throw new RuntimeException(
  174. "Unable to enable plugin '" . get_called_class() . "': "
  175. . "Required plugin '" . $pluginName . "' is disabled"
  176. );
  177. }
  178. }
  179. }
  180. }
  181. /**
  182. * @see PicoPluginInterface::getDependencies()
  183. */
  184. public function getDependencies()
  185. {
  186. return (array) $this->dependsOn;
  187. }
  188. /**
  189. * Disables all plugins which depend on this plugin
  190. *
  191. * @see PicoPluginInterface::getDependants()
  192. * @param boolean $recursive disabled dependant plugins automatically
  193. * @return void
  194. * @throws RuntimeException thrown when a dependency fails
  195. */
  196. protected function checkDependants($recursive)
  197. {
  198. $dependants = $this->getDependants();
  199. if (!empty($dependants)) {
  200. if ($recursive) {
  201. foreach ($this->getDependants() as $pluginName => $plugin) {
  202. if ($plugin->isEnabled()) {
  203. if (!$plugin->isStatusChanged()) {
  204. $plugin->setEnabled(false, true, true);
  205. } else {
  206. throw new RuntimeException(
  207. "Unable to disable plugin '" . get_called_class() . "': "
  208. . "Required by manually enabled plugin '" . $pluginName . "'"
  209. );
  210. }
  211. }
  212. }
  213. } else {
  214. $dependantsList = 'plugin' . ((count($dependants) > 1) ? 's' : '') . ' ';
  215. $dependantsList .= "'" . implode("', '", array_keys($dependants)) . "'";
  216. throw new RuntimeException(
  217. "Unable to disable plugin '" . get_called_class() . "': "
  218. . "Required by " . $dependantsList
  219. );
  220. }
  221. }
  222. }
  223. /**
  224. * @see PicoPluginInterface::getDependants()
  225. */
  226. public function getDependants()
  227. {
  228. if ($this->dependants === null) {
  229. $this->dependants = array();
  230. foreach ($this->getPlugins() as $pluginName => $plugin) {
  231. // only plugins which implement PicoPluginInterface support dependencies
  232. if (is_a($plugin, 'PicoPluginInterface')) {
  233. $dependencies = $plugin->getDependencies();
  234. if (in_array(get_called_class(), $dependencies)) {
  235. $this->dependants[$pluginName] = $plugin;
  236. }
  237. }
  238. }
  239. }
  240. return $this->dependants;
  241. }
  242. }