get() method. * * @todo Add methods to enable and disable plugins * @author TechFuze * @copyright Copyright (c) 2013 - 2019, TechFuze. (http://techfuze.net) */ class Plugins { use ComponentPathsTrait; /** * Array of loaded Plugins, so that they won't be reloaded * Plugins only end up here after being explicitly loaded. Header files do NOT count. * * @var array Array of loaded plugins */ protected array $plugins = array(); /** * Array of plugin header classes. * Loaded upon startup. Provide details on what class should load for the plugin. * * @var array Array of loaded plugin header classes */ protected array $headers = array(); /** * Config file for the plugin system * * @var ConfigORM */ protected ConfigORM $cfg; /** * Called upon initialization of the Container * * @throws FactoryException * @codeCoverageIgnore */ public function init() { $this->cfg = Factory::getInstance()->config->getConfig('plugins'); } /** * Load the header files of all plugins. */ public function loadHeadersFromPluginPaths() { // Cycle through all pluginPaths for ($i=Priority::getHighestPriority(); $i<=Priority::getLowestPriority(); $i++) { if (!isset($this->componentPaths[$i])) continue; foreach ($this->componentPaths[$i] as $pluginPath) { // If directory does not exist, skip it if (!file_exists($pluginPath) || !is_dir($pluginPath)) continue; // Fetch the contents of the path $pluginPathContents = array_diff(scandir($pluginPath), array('..', '.')); // Now go through each entry in the plugin folder foreach ($pluginPathContents as $pluginFolder) { // @codeCoverageIgnoreStart if (!is_dir($pluginPath . DS . $pluginFolder)) continue; // @codeCoverageIgnoreEnd // If a header file exists, use it $file = $pluginPath . DS . $pluginFolder . DS . 'header.php'; $pluginFolder = ucfirst($pluginFolder); $className = '\Application\Plugin\\'.$pluginFolder.'Header'; if (file_exists($file)) { // Load the header file require_once($file); $header = new $className(); if (!$header instanceof iPluginHeader) continue; // Load the header $this->loadHeader($header); } } } } } /** * Load a header object. * * The provided header will be loaded into the header registry and initialized. * * @param iPluginHeader $header * @return bool */ protected function loadHeader(iPluginHeader $header): bool { // Fetch the name $pluginName = ucfirst($header->getName()); // Check if the plugin is disabled if (in_array($pluginName, $this->cfg->get('disabled_plugins'))) { $this->headers[$pluginName] = 'disabled'; return false; } // Initialize it $h = $this->headers[$pluginName] = $header; $h->init(); // And log it Logger::log('Loaded Plugin Header: \'' . $pluginName . '\''); return true; } /** * Add a Plugin to FuzeWorks * * The provided plugin header will be loaded into the registry and initialized * * @param iPluginHeader $header * @return bool */ public function addPlugin(iPluginHeader $header): bool { return $this->loadHeader($header); } /** * Get a plugin. * * @param string $pluginName Name of the plugin * @param array|null $parameters Parameters to send to the __construct() method * @return mixed Plugin on success, bool on cancellation * @throws Exception\EventException * @throws PluginException */ public function get(string $pluginName, array $parameters = null) { if (empty($pluginName)) throw new PluginException("Could not load plugin. No name provided", 1); // Determine the name of the plugin $pluginName = ucfirst($pluginName); // Fire pluginGetEvent, and cancel or return custom plugin if required /** @var PluginGetEvent $event */ $event = Events::fireEvent('pluginGetEvent', $pluginName); if ($event->isCancelled()) return false; elseif ($event->getPlugin() != null) return $event->getPlugin(); // Otherwise, just set the variables $pluginName = $event->pluginName; // Check if the plugin is already loaded and return directly if (isset($this->plugins[$pluginName])) return $this->plugins[$pluginName]; // Check if the plugin header exists if (!isset($this->headers[$pluginName])) throw new PluginException("Could not load plugin. Plugin header does not exist", 1); // If disabled, don't bother if (in_array($pluginName, $this->cfg->get('disabled_plugins'))) throw new PluginException("Could not load plugin. Plugin is disabled", 1); // Determine what file to load /** @var iPluginHeader $header */ $header = $this->headers[$pluginName]; // Add to autoloader $headerReflection = new ReflectionClass( get_class($header) ); $prefix = $header->getClassesPrefix(); $filePath = dirname($headerReflection->getFileName()) . (!empty($header->getSourceDirectory()) ? DS . $header->getSourceDirectory() : ''); $pluginClass = $header->getPluginClass(); if (!is_null($prefix) && !is_null($filePath)) { try { Core::addAutoloadMap($prefix, $filePath); } catch (CoreException $e) { throw new PluginException("Could not load plugin. Autoloader invalid: '".$e->getMessage()."'"); } } // If a 'getPlugin' method is found in the header, call that instead if (method_exists($header, 'getPlugin')) { $this->plugins[$pluginName] = $header->getPlugin(); Logger::log('Loaded Plugin: \'' . $pluginName . '\''); return $this->plugins[$pluginName]; } // Attempt to load the plugin if (!class_exists($pluginClass, true)) throw new PluginException("Could not load plugin. Class does not exist", 1); $this->plugins[$pluginName] = new $pluginClass($parameters); Logger::log('Loaded Plugin: \'' . $pluginName . '\''); // And return it return $this->plugins[$pluginName]; } }