Plugins.php 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. <?php
  2. /**
  3. * @package Grav\Common
  4. *
  5. * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
  6. * @license MIT License; see LICENSE file for details.
  7. */
  8. namespace Grav\Common;
  9. use Exception;
  10. use Grav\Common\Config\Config;
  11. use Grav\Common\Data\Blueprints;
  12. use Grav\Common\Data\Data;
  13. use Grav\Common\File\CompiledYamlFile;
  14. use Grav\Events\PluginsLoadedEvent;
  15. use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
  16. use RuntimeException;
  17. use SplFileInfo;
  18. use Symfony\Component\EventDispatcher\EventDispatcher;
  19. use function get_class;
  20. use function is_object;
  21. /**
  22. * Class Plugins
  23. * @package Grav\Common
  24. */
  25. class Plugins extends Iterator
  26. {
  27. /** @var array|null */
  28. public $formFieldTypes;
  29. /** @var bool */
  30. private $plugins_initialized = false;
  31. /**
  32. * Plugins constructor.
  33. */
  34. public function __construct()
  35. {
  36. parent::__construct();
  37. /** @var UniformResourceLocator $locator */
  38. $locator = Grav::instance()['locator'];
  39. $iterator = $locator->getIterator('plugins://');
  40. $plugins = [];
  41. /** @var SplFileInfo $directory */
  42. foreach ($iterator as $directory) {
  43. if (!$directory->isDir()) {
  44. continue;
  45. }
  46. $plugins[] = $directory->getFilename();
  47. }
  48. sort($plugins, SORT_NATURAL | SORT_FLAG_CASE);
  49. foreach ($plugins as $plugin) {
  50. $object = $this->loadPlugin($plugin);
  51. if ($object) {
  52. $this->add($object);
  53. }
  54. }
  55. }
  56. /**
  57. * @return $this
  58. */
  59. public function setup()
  60. {
  61. $blueprints = [];
  62. $formFields = [];
  63. $grav = Grav::instance();
  64. /** @var Config $config */
  65. $config = $grav['config'];
  66. /** @var Plugin $plugin */
  67. foreach ($this->items as $plugin) {
  68. // Setup only enabled plugins.
  69. if ($config["plugins.{$plugin->name}.enabled"] && $plugin instanceof Plugin) {
  70. if (isset($plugin->features['blueprints'])) {
  71. $blueprints["plugin://{$plugin->name}/blueprints"] = $plugin->features['blueprints'];
  72. }
  73. if (method_exists($plugin, 'getFormFieldTypes')) {
  74. $formFields[get_class($plugin)] = $plugin->features['formfields'] ?? 0;
  75. }
  76. }
  77. }
  78. if ($blueprints) {
  79. // Order by priority.
  80. arsort($blueprints, SORT_NUMERIC);
  81. /** @var UniformResourceLocator $locator */
  82. $locator = $grav['locator'];
  83. $locator->addPath('blueprints', '', array_keys($blueprints), ['system', 'blueprints']);
  84. }
  85. if ($formFields) {
  86. // Order by priority.
  87. arsort($formFields, SORT_NUMERIC);
  88. $list = [];
  89. foreach ($formFields as $className => $priority) {
  90. $plugin = $this->items[$className];
  91. $list += $plugin->getFormFieldTypes();
  92. }
  93. $this->formFieldTypes = $list;
  94. }
  95. return $this;
  96. }
  97. /**
  98. * Registers all plugins.
  99. *
  100. * @return Plugin[] array of Plugin objects
  101. * @throws RuntimeException
  102. */
  103. public function init()
  104. {
  105. if ($this->plugins_initialized) {
  106. return $this->items;
  107. }
  108. $grav = Grav::instance();
  109. /** @var Config $config */
  110. $config = $grav['config'];
  111. /** @var EventDispatcher $events */
  112. $events = $grav['events'];
  113. foreach ($this->items as $instance) {
  114. // Register only enabled plugins.
  115. if ($config["plugins.{$instance->name}.enabled"] && $instance instanceof Plugin) {
  116. // Set plugin configuration.
  117. $instance->setConfig($config);
  118. // Register autoloader.
  119. if (method_exists($instance, 'autoload')) {
  120. $instance->autoload();
  121. }
  122. // Register event listeners.
  123. $events->addSubscriber($instance);
  124. }
  125. }
  126. // Plugins Loaded Event
  127. $event = new PluginsLoadedEvent($grav, $this);
  128. $grav->dispatchEvent($event);
  129. $this->plugins_initialized = true;
  130. return $this->items;
  131. }
  132. /**
  133. * Add a plugin
  134. *
  135. * @param Plugin $plugin
  136. * @return void
  137. */
  138. public function add($plugin)
  139. {
  140. if (is_object($plugin)) {
  141. $this->items[get_class($plugin)] = $plugin;
  142. }
  143. }
  144. /**
  145. * @return array
  146. */
  147. public function __debugInfo(): array
  148. {
  149. $array = (array)$this;
  150. unset($array["\0Grav\Common\Iterator\0iteratorUnset"]);
  151. return $array;
  152. }
  153. /**
  154. * @return Plugin[] Index of all plugins by plugin name.
  155. */
  156. public static function getPlugins(): array
  157. {
  158. /** @var Plugins $plugins */
  159. $plugins = Grav::instance()['plugins'];
  160. $list = [];
  161. foreach ($plugins as $instance) {
  162. $list[$instance->name] = $instance;
  163. }
  164. return $list;
  165. }
  166. /**
  167. * @param string $name Plugin name
  168. * @return Plugin|null Plugin object or null if plugin cannot be found.
  169. */
  170. public static function getPlugin(string $name)
  171. {
  172. $list = static::getPlugins();
  173. return $list[$name] ?? null;
  174. }
  175. /**
  176. * Return list of all plugin data with their blueprints.
  177. *
  178. * @return Data[]
  179. */
  180. public static function all()
  181. {
  182. $grav = Grav::instance();
  183. /** @var Plugins $plugins */
  184. $plugins = $grav['plugins'];
  185. $list = [];
  186. foreach ($plugins as $instance) {
  187. $name = $instance->name;
  188. try {
  189. $result = self::get($name);
  190. } catch (Exception $e) {
  191. $exception = new RuntimeException(sprintf('Plugin %s: %s', $name, $e->getMessage()), $e->getCode(), $e);
  192. /** @var Debugger $debugger */
  193. $debugger = $grav['debugger'];
  194. $debugger->addMessage("Plugin {$name} cannot be loaded, please check Exceptions tab", 'error');
  195. $debugger->addException($exception);
  196. continue;
  197. }
  198. if ($result) {
  199. $list[$name] = $result;
  200. }
  201. }
  202. return $list;
  203. }
  204. /**
  205. * Get a plugin by name
  206. *
  207. * @param string $name
  208. * @return Data|null
  209. */
  210. public static function get($name)
  211. {
  212. $blueprints = new Blueprints('plugins://');
  213. $blueprint = $blueprints->get("{$name}/blueprints");
  214. // Load default configuration.
  215. $file = CompiledYamlFile::instance("plugins://{$name}/{$name}" . YAML_EXT);
  216. // ensure this is a valid plugin
  217. if (!$file->exists()) {
  218. return null;
  219. }
  220. $obj = new Data((array)$file->content(), $blueprint);
  221. // Override with user configuration.
  222. $obj->merge(Grav::instance()['config']->get('plugins.' . $name) ?: []);
  223. // Save configuration always to user/config.
  224. $file = CompiledYamlFile::instance("config://plugins/{$name}.yaml");
  225. $obj->file($file);
  226. return $obj;
  227. }
  228. /**
  229. * @param string $name
  230. * @return Plugin|null
  231. */
  232. protected function loadPlugin($name)
  233. {
  234. // NOTE: ALL THE LOCAL VARIABLES ARE USED INSIDE INCLUDED FILE, DO NOT REMOVE THEM!
  235. $grav = Grav::instance();
  236. $locator = $grav['locator'];
  237. $file = $locator->findResource('plugins://' . $name . DS . $name . PLUGIN_EXT);
  238. if (is_file($file)) {
  239. // Local variables available in the file: $grav, $name, $file
  240. $class = include_once $file;
  241. if (!$class || !is_subclass_of($class, Plugin::class, true)) {
  242. $className = Inflector::camelize($name);
  243. $pluginClassFormat = [
  244. 'Grav\\Plugin\\' . ucfirst($name). 'Plugin',
  245. 'Grav\\Plugin\\' . $className . 'Plugin',
  246. 'Grav\\Plugin\\' . $className
  247. ];
  248. foreach ($pluginClassFormat as $pluginClass) {
  249. if (is_subclass_of($pluginClass, Plugin::class, true)) {
  250. $class = new $pluginClass($name, $grav);
  251. break;
  252. }
  253. }
  254. }
  255. } else {
  256. $grav['log']->addWarning(
  257. sprintf("Plugin '%s' enabled but not found! Try clearing cache with `bin/grav clearcache`", $name)
  258. );
  259. return null;
  260. }
  261. return $class;
  262. }
  263. }