layout; * * The Factory class allows the user to replace dependencies on the fly. It is possible for a class * to replace a dependency, like Logger, on the fly by calling the $factory->newInstance('Logger'); or the * $factory->setInstance('Logger', $object); This allows for creative ways to do dependency injection, or keep classes * separated. * * It is also possible to load a cloned instance of the Factory class, so that all properties are independant as well, * all to suit your very needs. * * The Factory class is also extendible. This allows classes that extend Factory to access all it's properties. * * @author TechFuze * @copyright Copyright (c) 2013 - 2019, TechFuze. (http://techfuze.net) */ class Factory { /** * The Factory instance that is shared by default when calling Factory::getInstance(); * * @var Factory|null Default shared instance */ private static ?Factory $sharedFactoryInstance = null; /** * Whether the Factory has been initialized or not * * @var bool $initialized */ private bool $initialized = false; /** * Config Object * @var Config */ public Config $config; /** * Logger Object * @var Logger */ public Logger $logger; /** * Events Object * @var Events */ public Events $events; /** * Libraries Object * @var Libraries */ public Libraries $libraries; /** * Helpers Object * @var Helpers */ public Helpers $helpers; /** * Plugins Object * @var Plugins */ public Plugins $plugins; /** * Factory instance constructor. Should only really be called once * @throws FactoryException */ public function __construct() { // If there is no sharedFactoryInstance, prepare it if (is_null(self::$sharedFactoryInstance)) { // @codeCoverageIgnoreStart self::$sharedFactoryInstance = $this; $this->config = new Config(); $this->logger = new Logger(); $this->events = new Events(); $this->libraries = new Libraries(); $this->helpers = new Helpers(); $this->plugins = new Plugins(); return; } // @codeCoverageIgnoreEnd // Otherwise, copy the existing instances $x = self::getInstance(); foreach ($x as $key => $value) $this->{$key} = $value; } /** * Finalizes the Factory and sends out a coreStartEvent * * @return Factory * @throws CoreException */ public function initFactory(): Factory { // If already initialized, cancel if ($this->initialized) return $this; // Load the config file of the FuzeWorks core try { $cfg = $this->config->get('core'); } catch (ConfigException $e) { throw new CoreException("Could not initiate Factory. Config 'core' could not be found."); } // Disable events if requested to do so if (!$cfg->get('enable_events')) Events::disable(); // Initialize all components foreach ($this as $component) { if (!is_object($component)) continue; if (method_exists($component, 'init')) $component->init(); } // Initialize all plugins $this->plugins->loadHeadersFromPluginPaths(); // Log actions Logger::logInfo("FuzeWorks initialized. Firing coreStartEvent."); // And fire the coreStartEvent try { Events::fireEvent('coreStartEvent'); } catch (EventException $e) { throw new CoreException("Could not initiate Factory. coreStartEvent threw exception: ".$e->getMessage()); } return $this; } /** * Get an instance of a componentClass. * * @param string|null $instanceName * @return mixed * @throws FactoryException */ public static function getInstance(string $instanceName = null) { if (is_null($instanceName)) return self::$sharedFactoryInstance; // Determine the instance name $instanceName = strtolower($instanceName); if (!isset(self::$sharedFactoryInstance->{$instanceName})) throw new FactoryException("Could not get instance. Instance was not found."); return self::$sharedFactoryInstance->{$instanceName}; } /** * Create a new instance of one of the loaded classes. * It reloads the class. It does NOT clone it. * * @param string $className The name of the loaded class, WITHOUT the namespace * @param string $namespace Optional namespace. Defaults to 'FuzeWorks\' * @return Factory Instance * @throws FactoryException */ public function newInstance(string $className, string $namespace = 'FuzeWorks\\'): self { // Determine the class to load $instanceName = strtolower($className); $className = $namespace.ucfirst($className); if (!isset($this->{$instanceName})) { throw new FactoryException("Could not load new instance of '".$instanceName."'. Instance was not found.", 1); } elseif (!class_exists($className, false)) { throw new FactoryException("Could not load new instance of '".$instanceName."'. Class not found.", 1); } // Remove the current instance unset($this->{$instanceName}); // And set the new one $this->{$instanceName} = new $className(); // Return itself return $this; } /** * Clone an instance of one of the loaded classes. * It clones the class. It does NOT re-create it. * * If the $onlyReturn = true is provided, the cloned instance will only be returned, and not set to the factory. * * @param string $className The name of the loaded class, WITHOUT the namespace * @param bool $onlyReturn * @return mixed * @throws FactoryException */ public static function cloneInstance(string $className, bool $onlyReturn = false) { // Determine the class to load $instanceName = strtolower($className); if (!isset(self::$sharedFactoryInstance->{$instanceName})) throw new FactoryException("Could not clone instance of '".$instanceName."'. Instance was not found.", 1); if ($onlyReturn) return clone self::$sharedFactoryInstance->{$instanceName}; // Clone the instance self::$sharedFactoryInstance->{$instanceName} = clone self::$sharedFactoryInstance->{$instanceName}; // Return itself return self::$sharedFactoryInstance->{$instanceName}; } /** * Set an instance of one of the loaded classes with your own $object. * Replace the existing class with one of your own. * * @param string $objectName The name of the loaded class, WITHOUT the namespace * @param mixed $object Object to replace the class with * @return Factory Instance */ public function setInstance(string $objectName, $object): self { // Determine the instance name $instanceName = strtolower($objectName); // Unset and set unset($this->{$instanceName}); $this->{$instanceName} = $object; // Return itself return $this; } /** * Remove an instance of one of the loaded classes. * * @param string $className The name of the loaded class, WITHOUT the namespace * @return Factory Factory Instance * @throws FactoryException */ public function removeInstance(string $className): self { // Determine the instance name $instanceName = strtolower($className); if (!isset($this->{$instanceName})) { throw new FactoryException("Could not remove instance of '".$instanceName."'. Instance was not found.", 1); } // Unset unset($this->{$instanceName}); // Return itself return $this; } /** * Returns true if component is part of this Factory. * * @param $componentName * @return bool */ public function instanceIsset($componentName): bool { return isset($this->{$componentName}); } }