AbstractPicoPlugin.php.txt 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  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. }
  74. }
  75. }
  76. if ($this->isEnabled() || ($eventName === 'onPluginsLoaded')) {
  77. if (method_exists($this, $eventName)) {
  78. call_user_func_array(array($this, $eventName), $params);
  79. }
  80. }
  81. }
  82. /**
  83. * @see PicoPluginInterface::setEnabled()
  84. */
  85. public function setEnabled($enabled, $recursive = true, $auto = false)
  86. {
  87. $this->statusChanged = (!$this->statusChanged) ? !$auto : true;
  88. $this->enabled = (bool) $enabled;
  89. if ($enabled) {
  90. $this->checkDependencies($recursive);
  91. } else {
  92. $this->checkDependants($recursive);
  93. }
  94. }
  95. /**
  96. * @see PicoPluginInterface::isEnabled()
  97. */
  98. public function isEnabled()
  99. {
  100. return $this->enabled;
  101. }
  102. /**
  103. * @see PicoPluginInterface::isStatusChanged()
  104. */
  105. public function isStatusChanged()
  106. {
  107. return $this->statusChanged;
  108. }
  109. /**
  110. * @see PicoPluginInterface::getPico()
  111. */
  112. public function getPico()
  113. {
  114. return $this->pico;
  115. }
  116. /**
  117. * Passes all not satisfiable method calls to Pico
  118. *
  119. * @see Pico
  120. * @param string $methodName name of the method to call
  121. * @param array $params parameters to pass
  122. * @return mixed return value of the called method
  123. */
  124. public function __call($methodName, array $params)
  125. {
  126. if (method_exists($this->getPico(), $methodName)) {
  127. return call_user_func_array(array($this->getPico(), $methodName), $params);
  128. }
  129. throw new BadMethodCallException(
  130. 'Call to undefined method ' . get_class($this->getPico()) . '::' . $methodName . '() '
  131. . 'through ' . get_called_class() . '::__call()'
  132. );
  133. }
  134. /**
  135. * Enables all plugins which this plugin depends on
  136. *
  137. * @see PicoPluginInterface::getDependencies()
  138. * @param boolean $recursive enable required plugins automatically
  139. * @return void
  140. * @throws RuntimeException thrown when a dependency fails
  141. */
  142. protected function checkDependencies($recursive)
  143. {
  144. foreach ($this->getDependencies() as $pluginName) {
  145. try {
  146. $plugin = $this->getPlugin($pluginName);
  147. } catch (RuntimeException $e) {
  148. throw new RuntimeException(
  149. "Unable to enable plugin '" . get_called_class() . "':"
  150. . "Required plugin '" . $pluginName . "' not found"
  151. );
  152. }
  153. // plugins which don't implement PicoPluginInterface are always enabled
  154. if (is_a($plugin, 'PicoPluginInterface') && !$plugin->isEnabled()) {
  155. if ($recursive) {
  156. if (!$plugin->isStatusChanged()) {
  157. $plugin->setEnabled(true, true, true);
  158. } else {
  159. throw new RuntimeException(
  160. "Unable to enable plugin '" . get_called_class() . "':"
  161. . "Required plugin '" . $pluginName . "' was disabled manually"
  162. );
  163. }
  164. } else {
  165. throw new RuntimeException(
  166. "Unable to enable plugin '" . get_called_class() . "':"
  167. . "Required plugin '" . $pluginName . "' is disabled"
  168. );
  169. }
  170. }
  171. }
  172. }
  173. /**
  174. * @see PicoPluginInterface::getDependencies()
  175. */
  176. public function getDependencies()
  177. {
  178. return (array) $this->dependsOn;
  179. }
  180. /**
  181. * Disables all plugins which depend on this plugin
  182. *
  183. * @see PicoPluginInterface::getDependants()
  184. * @param boolean $recursive disabled dependant plugins automatically
  185. * @return void
  186. * @throws RuntimeException thrown when a dependency fails
  187. */
  188. protected function checkDependants($recursive)
  189. {
  190. $dependants = $this->getDependants();
  191. if (!empty($dependants)) {
  192. if ($recursive) {
  193. foreach ($this->getDependants() as $pluginName => $plugin) {
  194. if ($plugin->isEnabled()) {
  195. if (!$plugin->isStatusChanged()) {
  196. $plugin->setEnabled(false, true, true);
  197. } else {
  198. throw new RuntimeException(
  199. "Unable to disable plugin '" . get_called_class() . "': "
  200. . "Required by manually enabled plugin '" . $pluginName . "'"
  201. );
  202. }
  203. }
  204. }
  205. } else {
  206. $dependantsList = 'plugin' . ((count($dependants) > 1) ? 's' : '') . ' ';
  207. $dependantsList .= "'" . implode("', '", array_keys($dependants)) . "'";
  208. throw new RuntimeException(
  209. "Unable to disable plugin '" . get_called_class() . "': "
  210. . "Required by " . $dependantsList
  211. );
  212. }
  213. }
  214. }
  215. /**
  216. * @see PicoPluginInterface::getDependants()
  217. */
  218. public function getDependants()
  219. {
  220. if ($this->dependants === null) {
  221. $this->dependants = array();
  222. foreach ($this->getPlugins() as $pluginName => $plugin) {
  223. // only plugins which implement PicoPluginInterface support dependencies
  224. if (is_a($plugin, 'PicoPluginInterface')) {
  225. $dependencies = $plugin->getDependencies();
  226. if (in_array(get_called_class(), $dependencies)) {
  227. $this->dependants[$pluginName] = $plugin;
  228. }
  229. }
  230. }
  231. }
  232. return $this->dependants;
  233. }
  234. }