From c1b29cea5f982060a762f038056655c533a85a31 Mon Sep 17 00:00:00 2001 From: Abel Hoogeveen Date: Mon, 22 Jul 2019 11:52:28 +0200 Subject: [PATCH 1/4] Fixed bug relating to configuration files not being read properly. - The plugins system wasn't properly registering the plugins config, because it was initialized at construct, which resulted in alternative directories not yet being loaded. The method has been changed to init() instead. --- src/FuzeWorks/Plugins.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/FuzeWorks/Plugins.php b/src/FuzeWorks/Plugins.php index a77cd98..ef2de7f 100644 --- a/src/FuzeWorks/Plugins.php +++ b/src/FuzeWorks/Plugins.php @@ -89,12 +89,12 @@ class Plugins protected $cfg; /** - * Called upon creation of the plugins class. + * Called upon initialization of the Container * * @throws ConfigException * @codeCoverageIgnore */ - public function __construct() + public function init() { $this->cfg = Factory::getInstance()->config->getConfig('plugins'); } From 8b01dd2f84839b840889fafabacd82c61e1f1150 Mon Sep 17 00:00:00 2001 From: Abel Hoogeveen Date: Mon, 22 Jul 2019 19:48:13 +0200 Subject: [PATCH 2/4] Implemented three requested features. - FuzeWorks\Core now manages autoloading of classes for Plugins. Plugins no longer need to implement this themselves. Autoloading for libraries to be added soon. - FuzeWorks\Factory::getInstance('') now returns a requested component. No arguments will result in the Factory being returned. This should simplify invoking components. cloneInstance() replaces old functionality. - FuzeWorks\Logger can now properly write logs to file. It's unknown how this issue wasn't discovered before. --- src/FuzeWorks/Core.php | 47 +++++++++- src/FuzeWorks/Factory.php | 75 ++++++--------- src/FuzeWorks/Libraries.php | 5 +- src/FuzeWorks/Logger.php | 9 +- src/FuzeWorks/Plugins.php | 53 +++++------ src/FuzeWorks/iPluginHeader.php | 44 +++++++++ src/Layout/layout.logger_file.php | 11 +-- test/core/core_factoryTest.php | 93 +++++++++++-------- test/core/core_pluginsTest.php | 9 +- test/plugins/TestDisabledPlugin/header.php | 15 +++ test/plugins/TestGetPluginMethod/header.php | 15 +++ .../OtherPlug.php} | 2 + .../header.php | 23 ++++- test/plugins/TestInvalidClass/header.php | 15 +++ .../loadHeader/{plugin.plug.php => Plug.php} | 2 +- .../TestLoadHeader/loadHeader/header.php | 15 +++ .../TestLoadPlugin.php} | 2 +- .../TestLoadHeaderNotIPluginHeader/header.php | 15 +++ .../TestLoadPlugin.php} | 2 +- test/plugins/TestLoadPlugin/header.php | 15 +++ test/plugins/TestMissingPlugin/header.php | 15 +++ ...treloadplugin.php => TestReloadPlugin.php} | 2 +- test/plugins/TestReloadPlugin/header.php | 15 +++ 23 files changed, 350 insertions(+), 149 deletions(-) rename test/plugins/{TestGetPluginWithClassFile/OtherPlugFile.php => TestGetPluginWithClassMap/OtherPlug.php} (95%) rename test/plugins/{TestGetPluginWithClassFile => TestGetPluginWithClassMap}/header.php (77%) rename test/plugins/TestLoadHeader/loadHeader/{plugin.plug.php => Plug.php} (96%) rename test/plugins/{TestLoadPlugin/plugin.testloadplugin.php => TestLoadHeaderNotIPluginHeader/TestLoadPlugin.php} (95%) rename test/plugins/{TestLoadHeaderNotIPluginHeader/plugin.testloadplugin.php => TestLoadPlugin/TestLoadPlugin.php} (96%) rename test/plugins/TestReloadPlugin/{plugin.testreloadplugin.php => TestReloadPlugin.php} (96%) diff --git a/src/FuzeWorks/Core.php b/src/FuzeWorks/Core.php index ce0c8e5..5aef261 100644 --- a/src/FuzeWorks/Core.php +++ b/src/FuzeWorks/Core.php @@ -36,6 +36,7 @@ namespace FuzeWorks; +use FuzeWorks\Exception\CoreException; use FuzeWorks\Exception\EventException; /** @@ -43,6 +44,7 @@ use FuzeWorks\Exception\EventException; * * Holds all the modules and starts the framework. Allows for starting and managing modules * + * @todo Implement unit tests for autoloader() methods * @author TechFuze * @copyright Copyright (c) 2013 - 2019, TechFuze. (http://techfuze.net) */ @@ -53,7 +55,7 @@ class Core * * @var string Framework version */ - public static $version = '1.0.0'; + public static $version = '1.2.0'; /** * Working directory of the Framework. @@ -108,6 +110,7 @@ class Core register_shutdown_function(array('\FuzeWorks\Core', 'shutdown')); set_error_handler(array('\FuzeWorks\Core', 'errorHandler'), E_ALL); set_exception_handler(array('\FuzeWorks\Core', 'exceptionHandler')); + spl_autoload_register(['\FuzeWorks\Core', 'autoloader'], true,false); // Return the Factory return new Factory(); @@ -186,7 +189,6 @@ class Core } } - /** * Add an exception handler to be called when an exception occurs * @@ -249,6 +251,47 @@ class Core } } + protected static $autoloadMap = []; + + /** + * @param string $nameSpacePrefix + * @param string $filePath + * @throws CoreException + */ + public static function addAutoloadMap(string $nameSpacePrefix, string $filePath) + { + // Remove leading slashes + $nameSpacePrefix = ltrim($nameSpacePrefix, '\\'); + + if (isset(self::$autoloadMap[$nameSpacePrefix])) + throw new CoreException("Could not add classes to autoloader. ClassMap already exists."); + + if (!file_exists($filePath) && !is_dir($filePath)) + throw new CoreException("Could not add classes to autoloader. Provided filePath does not exist."); + + self::$autoloadMap[$nameSpacePrefix] = $filePath; + } + + public static function autoloader(string $class) + { + // Remove leading slashes + $class = ltrim($class, '\\'); + + // First attempt and find if the prefix of the class is in the autoloadMap + foreach (self::$autoloadMap as $prefix => $path) + { + // If not, try next + if (strpos($class, $prefix) === false) + continue; + + // If it contains the prefix, attempt to find the file + $className = substr($class, strlen($prefix) + 1); + $filePath = $path . DIRECTORY_SEPARATOR . str_replace('\\', DIRECTORY_SEPARATOR, $className) . '.php'; + if (file_exists($filePath) && is_file($filePath)) + require($filePath); + } + } + /** * Tests for file writability * diff --git a/src/FuzeWorks/Factory.php b/src/FuzeWorks/Factory.php index 7642d48..37e1892 100644 --- a/src/FuzeWorks/Factory.php +++ b/src/FuzeWorks/Factory.php @@ -71,13 +71,6 @@ class Factory */ private static $sharedFactoryInstance; - /** - * Whether to clone all Factory instances upon calling Factory::getInstance() - * - * @var bool Clone all Factory instances. - */ - private static $cloneInstances = false; - /** * Whether the Factory has been initialized or not * @@ -124,6 +117,7 @@ class Factory /** * Factory instance constructor. Should only really be called once * @throws ConfigException + * @throws FactoryException */ public function __construct() { @@ -174,9 +168,7 @@ class Factory // Disable events if requested to do so if (!$cfg->get('enable_events')) - { Events::disable(); - } // Initialize all components foreach ($this as $component) @@ -201,40 +193,25 @@ class Factory return $this; } - /** - * Get a new instance of the Factory class. - * - * @param bool $cloneInstance Whether to get a cloned instance (true) or exactly the same instance (false) - * @return Factory Instance - */ - public static function getInstance($cloneInstance = false): Factory - { - if ($cloneInstance === true || self::$cloneInstances === true) - { - return clone self::$sharedFactoryInstance; - } + /** + * 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; - return self::$sharedFactoryInstance; - } + // Determine the instance name + $instanceName = strtolower($instanceName); - /** - * Enable cloning all Factory instances upon calling Factory::getInstance() - * - * @return void - */ - public static function enableCloneInstances() - { - self::$cloneInstances = true; - } + if (!isset(self::$sharedFactoryInstance->{$instanceName})) + throw new FactoryException("Could not get instance. Instance was not found."); - /** - * Disable cloning all Factory instances upon calling Factory::getInstance() - * - * @return void - */ - public static function disableCloneInstances() - { - self::$cloneInstances = false; + return self::$sharedFactoryInstance->{$instanceName}; } /** @@ -275,25 +252,29 @@ class Factory * 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 - * @return Factory Instance + * @param bool $onlyReturn + * @return mixed * @throws FactoryException */ - public function cloneInstance($className): self + public static function cloneInstance(string $className, bool $onlyReturn = false) { // Determine the class to load $instanceName = strtolower($className); - if (!isset($this->{$instanceName})) - { + 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 - $this->{$instanceName} = clone $this->{$instanceName}; + self::$sharedFactoryInstance->{$instanceName} = clone self::$sharedFactoryInstance->{$instanceName}; // Return itself - return $this; + return self::$sharedFactoryInstance->{$instanceName}; } /** diff --git a/src/FuzeWorks/Libraries.php b/src/FuzeWorks/Libraries.php index d18640d..52fccda 100644 --- a/src/FuzeWorks/Libraries.php +++ b/src/FuzeWorks/Libraries.php @@ -182,9 +182,7 @@ class Libraries { // First check to see if the library is already loaded if (!class_exists($libraryClass, false)) - { throw new LibraryException("Could not initiate library. Class not found", 1); - } // Determine what parameters to use if (empty($parameters)) @@ -200,6 +198,9 @@ class Libraries // Load the class object $classObject = new $libraryClass($parameters); + // @todo Check if library has all required methods (iLibrary) + // @todo Implement autoloading for libraries + // Check if the address is already reserved, if it is, we can presume that a new instance is requested. // Otherwise this code would not be reached diff --git a/src/FuzeWorks/Logger.php b/src/FuzeWorks/Logger.php index 7b19595..9b03349 100644 --- a/src/FuzeWorks/Logger.php +++ b/src/FuzeWorks/Logger.php @@ -223,9 +223,7 @@ class Logger { } if (self::$log_last_request == true) - { self::logLastRequest(); - } if (self::$log_errors_to_file == true) self::logErrorsToFile(); @@ -342,9 +340,8 @@ class Logger { require(dirname(__DIR__) . DS . 'Layout' . DS . 'layout.logger_file.php'); $contents = ob_get_clean(); $file = Core::$logDir . DS . 'fwlog_request.log'; - if (is_writable(dirname($file))) { + if (is_writable(dirname($file))) file_put_contents($file, $contents); - } } /** @@ -357,14 +354,14 @@ class Logger { $logs = []; foreach (self::$logs as $log) { - if ($log['type'] === 'ERROR') + if ($log['type'] === 'ERROR' || $log['type'] === 'EXCEPTION') $logs[] = $log; } require(dirname(__DIR__) . DS . 'Layout' . DS . 'layout.logger_file.php'); $contents = ob_get_clean(); $file = Core::$logDir . DS . 'fwlog_errors.log'; if (is_writable(dirname($file))) - file_put_contents($file, $contents, FILE_APPEND | LOCK_EX); + file_put_contents($file, $contents, FILE_APPEND); } /* =========================================LOGGING METHODS============================================================== */ diff --git a/src/FuzeWorks/Plugins.php b/src/FuzeWorks/Plugins.php index ef2de7f..b65ea05 100644 --- a/src/FuzeWorks/Plugins.php +++ b/src/FuzeWorks/Plugins.php @@ -38,6 +38,7 @@ namespace FuzeWorks; use FuzeWorks\ConfigORM\ConfigORM; use FuzeWorks\Event\PluginGetEvent; use FuzeWorks\Exception\ConfigException; +use FuzeWorks\Exception\CoreException; use FuzeWorks\Exception\PluginException; use ReflectionClass; use ReflectionException; @@ -210,10 +211,8 @@ class Plugins */ public function get($pluginName, array $parameters = null) { - if (empty($pluginName)) - { + if (empty($pluginName)) throw new PluginException("Could not load plugin. No name provided", 1); - } // Determine the name of the plugin $pluginName = ucfirst($pluginName); @@ -222,38 +221,45 @@ class Plugins /** @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 + // @todo Find a more reliable method for determining header directory + $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')) { @@ -262,26 +268,11 @@ class Plugins return $this->plugins[$pluginName]; } - // Determine class name and file - // @todo Find a more reliable method for determining header directory - $headerReflection = new ReflectionClass( get_class($header) ); - $directory = dirname($headerReflection->getFileName()); - $classFile = (isset($header->classFile) ? $directory.DS.$header->classFile : $directory.DS.'plugin.'.strtolower($pluginName).".php"); - $className = (isset($header->className) ? $header->className : '\Application\Plugin\\'.$pluginName); - - // Try to access the file - if (!file_exists($classFile)) - { - throw new PluginException("Could not load plugin. Class file does not exist", 1); - } - // Attempt to load the plugin - require_once($classFile); - if (!class_exists($className, false)) - { + if (!class_exists($pluginClass, true)) throw new PluginException("Could not load plugin. Class does not exist", 1); - } - $this->plugins[$pluginName] = new $className($parameters); + + $this->plugins[$pluginName] = new $pluginClass($parameters); Logger::log('Loaded Plugin: \'' . $pluginName . '\''); // And return it diff --git a/src/FuzeWorks/iPluginHeader.php b/src/FuzeWorks/iPluginHeader.php index 8f3059e..df86e62 100644 --- a/src/FuzeWorks/iPluginHeader.php +++ b/src/FuzeWorks/iPluginHeader.php @@ -39,6 +39,50 @@ namespace FuzeWorks; interface iPluginHeader { + /** + * Should return the name of the plugin. + * + * This is the name used to access the plugin when using Plugins::get() + * @return string + */ public function getName(): string; + + /** + * Should return the namespace prefix of the classes of this plugin. + * + * Used to autoload classes of this plugin. + * @see https://www.php-fig.org/psr/psr-4/ + * + * Invoked upon `FuzeWorks\Plugins::get`. Autoloading plugin classes before that is not possible. + * + * @return string|null + */ + public function getClassesPrefix(): ?string; + + /** + * Should return the directory where the classes of this plugin can be found. + * + * Only the source directory within the plugin should be returned, e.g: + * If the source directory is 'src' within the plugin directory, return 'src' + * + * @return string|null + */ + public function getSourceDirectory(): ?string; + + /** + * Should return the className of the main class of this plugin + * + * Should only return null if the pluginHeader has the optional getPlugin() method. + * + * @return string|null + */ + public function getPluginClass(): ?string; + + /** + * Initializes the pluginHeader. This method allows the developer to hook into multiple systems of FuzeWorks + * upon FuzeWorks initialization. See the FuzeWorks boot process for more information. + * + * @return mixed + */ public function init(); } \ No newline at end of file diff --git a/src/Layout/layout.logger_file.php b/src/Layout/layout.logger_file.php index 5be9676..aeeb559 100644 --- a/src/Layout/layout.logger_file.php +++ b/src/Layout/layout.logger_file.php @@ -34,19 +34,16 @@ * @version Version 1.2.0 */ -$mask = "|%5s |%-95s |\n"; +$mask = "|%5s |%5s |%-90s |\n"; $id = 1; if (!empty($logs)) - printf($mask, $id, ' REQUEST ' . date('Y-m-d H:i') . '-'.substr(sha1(uniqid()), 0, 8).''); + printf($mask, $id, 'REQUEST', ' ' . date('Y-m-d H:i') . '-'.substr(sha1(uniqid()), 0, 8).''); foreach ($logs as $log) { $id++; - - $string = ''; - $string .= '[ERROR]' . ' - '; - $string .= $log['message']; + printf($mask, $id, $log['type'], $log['message']); } if (!empty($logs)) - printf($mask, $id, ''); \ No newline at end of file + printf($mask, $id, 'HALT', '---------------------'); \ No newline at end of file diff --git a/test/core/core_factoryTest.php b/test/core/core_factoryTest.php index 682ed74..680496a 100644 --- a/test/core/core_factoryTest.php +++ b/test/core/core_factoryTest.php @@ -54,6 +54,32 @@ class factoryTest extends CoreTestAbstract $this->assertInstanceOf('FuzeWorks\Factory', Factory::getInstance()); } + /** + * @covers ::getInstance + */ + public function testGetInstance() + { + // Add the mock + $mock = $this->getMockBuilder(MockFactory::class)->getMock(); + Factory::getInstance()->setInstance('Mock', $mock); + + // First test a global getInstance Factory + $this->assertInstanceOf('\FuzeWorks\Factory', Factory::getInstance()); + + // Second, test retrieving a component + $this->assertInstanceOf(get_class($mock), Factory::getInstance('Mock')); + } + + /** + * @depends testGetInstance + * @covers ::getInstance + * @expectedException \FuzeWorks\Exception\FactoryException + */ + public function testGetInstanceNotFound() + { + Factory::getInstance('NotFound'); + } + /** * @depends testCanLoadFactory * @covers ::getInstance @@ -70,11 +96,23 @@ class factoryTest extends CoreTestAbstract */ public function testLoadDifferentInstance() { + // Add the mock + $mock = $this->getMockBuilder(MockFactory::class)->getMock(); + Factory::getInstance()->setInstance('Mock', $mock); + // First a situation where one is the shared instance and one is a cloned instance - $this->assertNotSame(Factory::getInstance(), Factory::getInstance(true)); + $a = Factory::getInstance('Mock'); + $b = Factory::cloneInstance('Mock'); + $this->assertInstanceOf(get_class($mock), $a); + $this->assertInstanceOf(get_class($mock), $b); + $this->assertNotSame($a,$b); // And a situation where both are cloned instances - $this->assertNotSame(Factory::getInstance(true), Factory::getInstance(true)); + $a = Factory::cloneInstance('Mock'); + $b = Factory::cloneInstance('Mock'); + $this->assertInstanceOf(get_class($mock), $a); + $this->assertInstanceOf(get_class($mock), $b); + $this->assertNotSame($a,$b); } /** @@ -91,6 +129,8 @@ class factoryTest extends CoreTestAbstract $this->assertFalse(isset(Factory::getInstance()->mock)); // Same instance factories + /** @var Factory $factory1 */ + /** @var Factory $factory2 */ $factory1 = Factory::getInstance()->setInstance('Mock', $mock); $factory2 = Factory::getInstance()->setInstance('Mock', $mock); @@ -98,8 +138,8 @@ class factoryTest extends CoreTestAbstract $this->assertSame($factory1->mock, $factory2->mock); // Different instance factories - $factory3 = Factory::getInstance(true)->setInstance('Mock', $mock); - $factory4 = Factory::getInstance(true)->setInstance('Mock', $mock); + $factory3 = Factory::getInstance()->setInstance('Mock', $mock); + $factory4 = Factory::getInstance()->setInstance('Mock', $mock); // Return the mocks $this->assertSame($factory3->mock, $factory4->mock); @@ -121,23 +161,23 @@ class factoryTest extends CoreTestAbstract $factory2 = Factory::getInstance()->setInstance('Mock', $mock); // Clone the instance in factory2 - $factory2->cloneInstance('Mock'); + $factory2mock = $factory2->cloneInstance('Mock'); // Should be true, since both Factories use the same Mock instance - $this->assertSame($factory1->mock, $factory2->mock); + $this->assertSame($factory1->mock, $factory2mock); // Different instance factories - $factory3 = Factory::getInstance(true)->setInstance('Mock', $mock); - $factory4 = Factory::getInstance(true)->setInstance('Mock', $mock); + $factory3 = Factory::getInstance()->setInstance('Mock', $mock); + $factory4 = Factory::getInstance()->setInstance('Mock', $mock); // Should be same for now $this->assertSame($factory3->mock, $factory4->mock); // Clone the instance in factory4 - $factory4->cloneInstance('Mock'); + $factory4mock = $factory4->cloneInstance('Mock', true); // Should be false, since both Factories use a different Mock instance - $this->assertNotSame($factory3->mock, $factory4->mock); + $this->assertNotSame($factory3->mock, $factory4mock); } /** @@ -154,31 +194,6 @@ class factoryTest extends CoreTestAbstract $factory->cloneInstance('fake'); } - /** - * @depends testCanLoadFactory - * @covers ::enableCloneInstances - * @covers ::disableCloneInstances - * @covers ::cloneInstance - * @covers ::getInstance - */ - public function testGlobalCloneInstance() - { - // First test without global cloning - $this->assertSame(Factory::getInstance(), Factory::getInstance()); - - // Now enable global cloning - Factory::enableCloneInstances(); - - // Now test without global cloning - $this->assertNotSame(Factory::getInstance(), Factory::getInstance()); - - // Disable global cloning - Factory::disableCloneInstances(); - - // And test again without global cloning - $this->assertSame(Factory::getInstance(), Factory::getInstance()); - } - /** * @depends testCanLoadFactory * @covers ::getInstance @@ -295,13 +310,9 @@ class factoryTest extends CoreTestAbstract public function tearDown() { - Factory::disableCloneInstances(); - $factory = Factory::getInstance(); - if (isset($factory->Mock)) - { - $factory->removeInstance('Mock'); - } + if (isset($factory->mock)) + $factory->removeInstance('mock'); } } diff --git a/test/core/core_pluginsTest.php b/test/core/core_pluginsTest.php index 30f3e3f..1793f43 100644 --- a/test/core/core_pluginsTest.php +++ b/test/core/core_pluginsTest.php @@ -55,6 +55,7 @@ class pluginTest extends CoreTestAbstract public function setUp() { $this->plugins = new Plugins(); + $this->plugins->init(); $this->plugins->addComponentPath('test'.DS.'plugins'); $this->plugins->loadHeadersFromPluginPaths(); } @@ -73,7 +74,7 @@ class pluginTest extends CoreTestAbstract */ public function testLoadPlugin() { - $this->assertInstanceOf('\Application\Plugin\TestLoadPlugin', $this->plugins->get('testLoadPlugin')); + $this->assertInstanceOf('\FuzeWorks\UnitTest\Plugins\TestLoadPlugin\TestLoadPlugin', $this->plugins->get('testLoadPlugin')); } /** @@ -99,7 +100,7 @@ class pluginTest extends CoreTestAbstract $this->plugins->addPlugin($header); // Assert the plugin - $this->assertInstanceOf('Application\Plugin\Plug', $this->plugins->get('Plug')); + $this->assertInstanceOf('\FuzeWorks\UnitTest\Plugins\TestLoadHeader\Plug', $this->plugins->get('Plug')); } /** @@ -125,9 +126,9 @@ class pluginTest extends CoreTestAbstract * @depends testLoadPlugin * @covers ::get */ - public function testGetPluginWithClassFile() + public function testGetPluginWithClassMap() { - $this->assertInstanceOf('OtherPlug', $this->plugins->get('TestGetPluginWithClassFile')); + $this->assertInstanceOf('FuzeWorks\UnitTest\Plugins\TestGetPluginWithClassMap\OtherPlug', $this->plugins->get('TestGetPluginWithClassMap')); } /** diff --git a/test/plugins/TestDisabledPlugin/header.php b/test/plugins/TestDisabledPlugin/header.php index d8cd7b1..ae44470 100644 --- a/test/plugins/TestDisabledPlugin/header.php +++ b/test/plugins/TestDisabledPlugin/header.php @@ -47,4 +47,19 @@ class TestDisabledPluginHeader implements iPluginHeader public function init() { } + + public function getClassesPrefix(): ?string + { + return null; + } + + public function getSourceDirectory(): ?string + { + return null; + } + + public function getPluginClass(): ?string + { + return null; + } } \ No newline at end of file diff --git a/test/plugins/TestGetPluginMethod/header.php b/test/plugins/TestGetPluginMethod/header.php index fa35603..2b7b820 100644 --- a/test/plugins/TestGetPluginMethod/header.php +++ b/test/plugins/TestGetPluginMethod/header.php @@ -52,4 +52,19 @@ class TestGetPluginMethodHeader implements iPluginHeader { return 'test_string'; } + + public function getClassesPrefix(): ?string + { + return null; + } + + public function getSourceDirectory(): ?string + { + return null; + } + + public function getPluginClass(): ?string + { + return null; + } } \ No newline at end of file diff --git a/test/plugins/TestGetPluginWithClassFile/OtherPlugFile.php b/test/plugins/TestGetPluginWithClassMap/OtherPlug.php similarity index 95% rename from test/plugins/TestGetPluginWithClassFile/OtherPlugFile.php rename to test/plugins/TestGetPluginWithClassMap/OtherPlug.php index a15a806..51d45c0 100644 --- a/test/plugins/TestGetPluginWithClassFile/OtherPlugFile.php +++ b/test/plugins/TestGetPluginWithClassMap/OtherPlug.php @@ -34,6 +34,8 @@ * @version Version 1.2.0 */ +namespace FuzeWorks\UnitTest\Plugins\TestGetPluginWithClassMap; + class OtherPlug { diff --git a/test/plugins/TestGetPluginWithClassFile/header.php b/test/plugins/TestGetPluginWithClassMap/header.php similarity index 77% rename from test/plugins/TestGetPluginWithClassFile/header.php rename to test/plugins/TestGetPluginWithClassMap/header.php index 326a2e1..c8a7bb0 100644 --- a/test/plugins/TestGetPluginWithClassFile/header.php +++ b/test/plugins/TestGetPluginWithClassMap/header.php @@ -36,18 +36,31 @@ namespace Application\Plugin; use FuzeWorks\iPluginHeader; -class TestGetPluginWithClassFileHeader implements iPluginHeader +class TestGetPluginWithClassMapHeader implements iPluginHeader { - public $classFile = 'OtherPlugFile.php'; - public $className = 'OtherPlug'; - public function getName(): string { - return "TestGetPluginWithClassFile"; + return "TestGetPluginWithClassMap"; + } + + public function getClassesPrefix(): ?string + { + return '\FuzeWorks\UnitTest\Plugins\TestGetPluginWithClassMap'; + } + + public function getSourceDirectory(): ?string + { + return ''; + } + + public function getPluginClass(): ?string + { + return '\FuzeWorks\UnitTest\Plugins\TestGetPluginWithClassMap\OtherPlug'; } public function init() { } + } \ No newline at end of file diff --git a/test/plugins/TestInvalidClass/header.php b/test/plugins/TestInvalidClass/header.php index 1c98cc6..ed2e985 100644 --- a/test/plugins/TestInvalidClass/header.php +++ b/test/plugins/TestInvalidClass/header.php @@ -44,6 +44,21 @@ class TestInvalidClassHeader implements iPluginHeader return "TestInvalidClass"; } + public function getClassesPrefix(): ?string + { + return '\FuzeWorks\UnitTest\Plugins\TestInvalidClassHeader'; + } + + public function getSourceDirectory(): ?string + { + return ''; + } + + public function getPluginClass(): ?string + { + return '\FuzeWorks\UnitTest\Plugins\TestInvalidClassHeader\TestInvalidClass'; + } + public function init() { } diff --git a/test/plugins/TestLoadHeader/loadHeader/plugin.plug.php b/test/plugins/TestLoadHeader/loadHeader/Plug.php similarity index 96% rename from test/plugins/TestLoadHeader/loadHeader/plugin.plug.php rename to test/plugins/TestLoadHeader/loadHeader/Plug.php index 22d2109..29763c5 100644 --- a/test/plugins/TestLoadHeader/loadHeader/plugin.plug.php +++ b/test/plugins/TestLoadHeader/loadHeader/Plug.php @@ -33,7 +33,7 @@ * * @version Version 1.2.0 */ -namespace Application\Plugin; +namespace FuzeWorks\UnitTest\Plugins\TestLoadHeader; class Plug { diff --git a/test/plugins/TestLoadHeader/loadHeader/header.php b/test/plugins/TestLoadHeader/loadHeader/header.php index bc179b4..afb0ae8 100644 --- a/test/plugins/TestLoadHeader/loadHeader/header.php +++ b/test/plugins/TestLoadHeader/loadHeader/header.php @@ -44,6 +44,21 @@ class TestLoadHeaderHeader implements iPluginHeader return "Plug"; } + public function getClassesPrefix(): ?string + { + return '\FuzeWorks\UnitTest\Plugins\TestLoadHeader'; + } + + public function getSourceDirectory(): ?string + { + return ''; + } + + public function getPluginClass(): ?string + { + return '\FuzeWorks\UnitTest\Plugins\TestLoadHeader\Plug'; + } + public function init() { } diff --git a/test/plugins/TestLoadPlugin/plugin.testloadplugin.php b/test/plugins/TestLoadHeaderNotIPluginHeader/TestLoadPlugin.php similarity index 95% rename from test/plugins/TestLoadPlugin/plugin.testloadplugin.php rename to test/plugins/TestLoadHeaderNotIPluginHeader/TestLoadPlugin.php index c35872a..445fd32 100644 --- a/test/plugins/TestLoadPlugin/plugin.testloadplugin.php +++ b/test/plugins/TestLoadHeaderNotIPluginHeader/TestLoadPlugin.php @@ -33,7 +33,7 @@ * * @version Version 1.2.0 */ -namespace Application\Plugin; +namespace FuzeWorks\UnitTest\Plugins\TestLoadHeaderNotIPluginHeader; class TestLoadPlugin { diff --git a/test/plugins/TestLoadHeaderNotIPluginHeader/header.php b/test/plugins/TestLoadHeaderNotIPluginHeader/header.php index ccad9fe..bfe17ff 100644 --- a/test/plugins/TestLoadHeaderNotIPluginHeader/header.php +++ b/test/plugins/TestLoadHeaderNotIPluginHeader/header.php @@ -43,6 +43,21 @@ class TestLoadHeaderNotIPluginHeaderHeader return "TestLoadHeaderNotIPluginHeader"; } + public function getClassesPrefix(): ?string + { + return '\FuzeWorks\UnitTest\Plugins\TestLoadHeaderNotIPluginHeader'; + } + + public function getSourceDirectory(): ?string + { + return ''; + } + + public function getPluginClass(): ?string + { + return '\FuzeWorks\UnitTest\Plugins\TestLoadHeaderNotIPluginHeader\TestLoadPlugin'; + } + public function init() { } diff --git a/test/plugins/TestLoadHeaderNotIPluginHeader/plugin.testloadplugin.php b/test/plugins/TestLoadPlugin/TestLoadPlugin.php similarity index 96% rename from test/plugins/TestLoadHeaderNotIPluginHeader/plugin.testloadplugin.php rename to test/plugins/TestLoadPlugin/TestLoadPlugin.php index c35872a..33ec7a5 100644 --- a/test/plugins/TestLoadHeaderNotIPluginHeader/plugin.testloadplugin.php +++ b/test/plugins/TestLoadPlugin/TestLoadPlugin.php @@ -33,7 +33,7 @@ * * @version Version 1.2.0 */ -namespace Application\Plugin; +namespace FuzeWorks\UnitTest\Plugins\TestLoadPlugin; class TestLoadPlugin { diff --git a/test/plugins/TestLoadPlugin/header.php b/test/plugins/TestLoadPlugin/header.php index 70e8f43..9fba303 100644 --- a/test/plugins/TestLoadPlugin/header.php +++ b/test/plugins/TestLoadPlugin/header.php @@ -44,6 +44,21 @@ class TestLoadPluginHeader implements iPluginHeader return "TestLoadPlugin"; } + public function getClassesPrefix(): ?string + { + return '\FuzeWorks\UnitTest\Plugins\TestLoadPlugin'; + } + + public function getSourceDirectory(): ?string + { + return ''; + } + + public function getPluginClass(): ?string + { + return '\FuzeWorks\UnitTest\Plugins\TestLoadPlugin\TestLoadPlugin'; + } + public function init() { } diff --git a/test/plugins/TestMissingPlugin/header.php b/test/plugins/TestMissingPlugin/header.php index 0eb25d4..756a3e9 100644 --- a/test/plugins/TestMissingPlugin/header.php +++ b/test/plugins/TestMissingPlugin/header.php @@ -44,6 +44,21 @@ class TestMissingPluginHeader implements iPluginHeader return "TestMissingPlugin"; } + public function getClassesPrefix(): ?string + { + return '\FuzeWorks\UnitTest\Plugins\TestMissingPlugin'; + } + + public function getSourceDirectory(): ?string + { + return ''; + } + + public function getPluginClass(): ?string + { + return '\FuzeWorks\UnitTest\Plugins\TestMissingPluginHeader\TestMissingPlugin'; + } + public function init() { } diff --git a/test/plugins/TestReloadPlugin/plugin.testreloadplugin.php b/test/plugins/TestReloadPlugin/TestReloadPlugin.php similarity index 96% rename from test/plugins/TestReloadPlugin/plugin.testreloadplugin.php rename to test/plugins/TestReloadPlugin/TestReloadPlugin.php index eb5aa3e..e224e0c 100644 --- a/test/plugins/TestReloadPlugin/plugin.testreloadplugin.php +++ b/test/plugins/TestReloadPlugin/TestReloadPlugin.php @@ -33,7 +33,7 @@ * * @version Version 1.2.0 */ -namespace Application\Plugin; +namespace FuzeWorks\UnitTest\Plugins\TestReloadPlugin; class TestReloadPlugin { diff --git a/test/plugins/TestReloadPlugin/header.php b/test/plugins/TestReloadPlugin/header.php index 1d97683..ca4d4d0 100644 --- a/test/plugins/TestReloadPlugin/header.php +++ b/test/plugins/TestReloadPlugin/header.php @@ -44,6 +44,21 @@ class TestReloadPluginHeader implements iPluginHeader return "TestReloadPlugin"; } + public function getClassesPrefix(): ?string + { + return '\FuzeWorks\UnitTest\Plugins\TestReloadPlugin'; + } + + public function getSourceDirectory(): ?string + { + return ''; + } + + public function getPluginClass(): ?string + { + return '\FuzeWorks\UnitTest\Plugins\TestReloadPlugin\TestReloadPlugin'; + } + public function init() { } From 4e8bb7ede3bace77a683a3c9f8322e65c192109a Mon Sep 17 00:00:00 2001 From: Abel Hoogeveen Date: Sat, 27 Jul 2019 17:22:12 +0200 Subject: [PATCH 3/4] Imlemented the autoloader into libraries. Libraries can now provide classes to be added to the autoloader, although this is not required. --- src/FuzeWorks/Core.php | 20 +++++- src/FuzeWorks/Libraries.php | 24 ++++++- src/FuzeWorks/iLibrary.php | 64 +++++++++++++++++++ test/core/abstract.coreTestAbstract.php | 11 ++-- test/core/core_configuratorTest.php | 2 + test/core/core_factoryTest.php | 2 + test/core/core_libraryTest.php | 28 ++++++++ test/core/core_loggerTest.php | 2 + test/core/core_pluginsTest.php | 2 + .../TestAddBadAutoloader.php | 52 +++++++++++++++ .../TestAddLibraryClass.php | 11 +++- .../SomeExtraClass.php | 43 +++++++++++++ .../TestAddLibraryWithAutoloader.php | 52 +++++++++++++++ .../TestGetLibraryFromAltDirectory.php | 11 +++- .../TestGetLibraryFromDirectory.php | 12 +++- .../TestGetLibraryFromSubdirectory.php | 11 +++- .../TestGetLibraryParametersFromConfig.php | 13 +++- 17 files changed, 345 insertions(+), 15 deletions(-) create mode 100644 src/FuzeWorks/iLibrary.php create mode 100644 test/libraries/TestAddBadAutoloader/TestAddBadAutoloader.php create mode 100644 test/libraries/TestAddLibraryWithAutoloader/SomeExtraClass.php create mode 100644 test/libraries/TestAddLibraryWithAutoloader/TestAddLibraryWithAutoloader.php diff --git a/src/FuzeWorks/Core.php b/src/FuzeWorks/Core.php index 5aef261..f9e89ac 100644 --- a/src/FuzeWorks/Core.php +++ b/src/FuzeWorks/Core.php @@ -86,6 +86,13 @@ class Core */ protected static $errorHandlers = []; + /** + * Array of all classMaps which can be autoloaded. + * + * @var array + */ + protected static $autoloadMap = []; + /** * Initializes the core. * @@ -251,8 +258,6 @@ class Core } } - protected static $autoloadMap = []; - /** * @param string $nameSpacePrefix * @param string $filePath @@ -292,6 +297,17 @@ class Core } } + /** + * Clears the autoloader to its original state. + * + * Not intended for use by developer. Only for use during testing + * @internal + */ + public static function clearAutoloader() + { + self::$autoloadMap = []; + } + /** * Tests for file writability * diff --git a/src/FuzeWorks/Libraries.php b/src/FuzeWorks/Libraries.php index 52fccda..0420808 100644 --- a/src/FuzeWorks/Libraries.php +++ b/src/FuzeWorks/Libraries.php @@ -37,7 +37,10 @@ namespace FuzeWorks; use FuzeWorks\Exception\ConfigException; +use FuzeWorks\Exception\CoreException; use FuzeWorks\Exception\LibraryException; +use ReflectionClass; +use ReflectionException; class Libraries { @@ -196,12 +199,27 @@ class Libraries } // Load the class object + /** @var iLibrary $classObject */ $classObject = new $libraryClass($parameters); - // @todo Check if library has all required methods (iLibrary) - // @todo Implement autoloading for libraries + // If not instance of iLibrary, refuse the library + if (!$classObject instanceof iLibrary) + throw new LibraryException("Could not initiate library. Library is not instance of iLibrary."); - // Check if the address is already reserved, if it is, we can presume that a new instance is requested. + // Add the library files to the autoloader + try { + $headerReflection = new ReflectionClass(get_class($classObject)); + $filePath = dirname($headerReflection->getFileName()) . (!is_null($classObject->getSourceDirectory()) ? DS . $classObject->getSourceDirectory() : '' ); + $prefix = $classObject->getClassesPrefix(); + if (!is_null($filePath) && !is_null($prefix)) + Core::addAutoloadMap($prefix, $filePath); + } catch (ReflectionException $e) { + throw new LibraryException("Could not initiate library. ReflectionClass threw exception."); + } catch (CoreException $e) { + throw new LibraryException("Could not initiate library. Failed to add to autoloader."); + } + + // @todo Check if the address is already reserved, if it is, we can presume that a new instance is requested. // Otherwise this code would not be reached // Now load the class diff --git a/src/FuzeWorks/iLibrary.php b/src/FuzeWorks/iLibrary.php new file mode 100644 index 0000000..598c4f5 --- /dev/null +++ b/src/FuzeWorks/iLibrary.php @@ -0,0 +1,64 @@ +config->discardConfigFiles(); + Factory::getInstance('config')->discardConfigFiles(); // Re-enable events, in case they have been disabled Events::enable(); // Remove Config overrides Config::$configOverrides = []; + + // Remove autoloader + Core::clearAutoloader(); } } diff --git a/test/core/core_configuratorTest.php b/test/core/core_configuratorTest.php index 1b52349..3ec6063 100644 --- a/test/core/core_configuratorTest.php +++ b/test/core/core_configuratorTest.php @@ -64,6 +64,8 @@ class configuratorTest extends CoreTestAbstract public function tearDown() { + parent::tearDown(); + Core::$tempDir = dirname(__DIR__) . '/temp'; Core::$logDir = dirname(__DIR__) . '/temp'; } diff --git a/test/core/core_factoryTest.php b/test/core/core_factoryTest.php index 680496a..df25b51 100644 --- a/test/core/core_factoryTest.php +++ b/test/core/core_factoryTest.php @@ -310,6 +310,8 @@ class factoryTest extends CoreTestAbstract public function tearDown() { + parent::tearDown(); + $factory = Factory::getInstance(); if (isset($factory->mock)) $factory->removeInstance('mock'); diff --git a/test/core/core_libraryTest.php b/test/core/core_libraryTest.php index 256ed35..123513e 100644 --- a/test/core/core_libraryTest.php +++ b/test/core/core_libraryTest.php @@ -195,8 +195,36 @@ class libraryTest extends CoreTestAbstract $this->libraries->addLibraryClass('LibraryClassFail', '\Case\Not\Exist'); } + /** + * @depends testAddLibraryClass + * @covers ::initLibrary + */ + public function testAddLibraryWithAutoloader() + { + // First assert the extra class can't be autoloaded + $this->assertFalse(class_exists('FuzeWorks\Test\TestAddLibraryWithAutoloader\SomeExtraClass', true)); + + // Load the library and test the instance type + $this->assertInstanceOf('Application\Library\TestAddLibraryWithAutoloader', $this->libraries->get('TestAddLibraryWithAutoloader')); + + // Afterwards test if the loader has been correctly added + $this->assertTrue(class_exists('FuzeWorks\Test\TestAddLibraryWithAutoloader\SomeExtraClass', true)); + } + + /** + * @depends testAddLibraryWithAutoloader + * @covers ::initLibrary + * @expectedException \FuzeWorks\Exception\LibraryException + */ + public function testAddBadAutoloader() + { + $this->assertInstanceOf('Application\Library\TestAddBadAutoloader', $this->libraries->get('TestAddBadAutoloader')); + } + public function tearDown() { + parent::tearDown(); + Factory::getInstance()->config->getConfig('error')->revert(); } diff --git a/test/core/core_loggerTest.php b/test/core/core_loggerTest.php index 0a9bb51..69f9577 100644 --- a/test/core/core_loggerTest.php +++ b/test/core/core_loggerTest.php @@ -213,6 +213,8 @@ class loggerTest extends CoreTestAbstract public function tearDown() { + parent::tearDown(); + Logger::disable(); Logger::$logs = array(); } diff --git a/test/core/core_pluginsTest.php b/test/core/core_pluginsTest.php index 1793f43..29beb1a 100644 --- a/test/core/core_pluginsTest.php +++ b/test/core/core_pluginsTest.php @@ -198,6 +198,8 @@ class pluginTest extends CoreTestAbstract public function tearDown() { + parent::tearDown(); + $factory = Factory::getInstance(); $factory->config->plugins->revert(); } diff --git a/test/libraries/TestAddBadAutoloader/TestAddBadAutoloader.php b/test/libraries/TestAddBadAutoloader/TestAddBadAutoloader.php new file mode 100644 index 0000000..a647f52 --- /dev/null +++ b/test/libraries/TestAddBadAutoloader/TestAddBadAutoloader.php @@ -0,0 +1,52 @@ +parameters = $parameters; } + public function getClassesPrefix(): ?string + { + return null; + } + + public function getSourceDirectory(): ?string + { + return null; + } + } \ No newline at end of file From ccbf09aa76d479c6526a52c4106c3dd3b23c0d4c Mon Sep 17 00:00:00 2001 From: Abel Hoogeveen Date: Wed, 21 Aug 2019 17:50:06 +0200 Subject: [PATCH 4/4] Implemented last change before release 1.2.0 --- src/FuzeWorks/Core.php | 5 ----- src/FuzeWorks/Libraries.php | 2 +- src/FuzeWorks/Plugins.php | 5 ++--- 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/src/FuzeWorks/Core.php b/src/FuzeWorks/Core.php index f9e89ac..a7f7328 100644 --- a/src/FuzeWorks/Core.php +++ b/src/FuzeWorks/Core.php @@ -167,11 +167,6 @@ class Core return $_is_php[$version]; } - public static function isCli(): bool - { - return (PHP_SAPI === 'cli' OR defined('STDIN')); - } - public static function exceptionHandler() { for ($i = Priority::getHighestPriority(); $i <= Priority::getLowestPriority(); $i++) diff --git a/src/FuzeWorks/Libraries.php b/src/FuzeWorks/Libraries.php index 0420808..07ab638 100644 --- a/src/FuzeWorks/Libraries.php +++ b/src/FuzeWorks/Libraries.php @@ -184,7 +184,7 @@ class Libraries protected function initLibrary(string $libraryName, string $libraryClass, array $parameters = []) { // First check to see if the library is already loaded - if (!class_exists($libraryClass, false)) + if (!class_exists($libraryClass, true)) throw new LibraryException("Could not initiate library. Class not found", 1); // Determine what parameters to use diff --git a/src/FuzeWorks/Plugins.php b/src/FuzeWorks/Plugins.php index b65ea05..5516055 100644 --- a/src/FuzeWorks/Plugins.php +++ b/src/FuzeWorks/Plugins.php @@ -37,8 +37,8 @@ namespace FuzeWorks; use FuzeWorks\ConfigORM\ConfigORM; use FuzeWorks\Event\PluginGetEvent; -use FuzeWorks\Exception\ConfigException; use FuzeWorks\Exception\CoreException; +use FuzeWorks\Exception\FactoryException; use FuzeWorks\Exception\PluginException; use ReflectionClass; use ReflectionException; @@ -92,7 +92,7 @@ class Plugins /** * Called upon initialization of the Container * - * @throws ConfigException + * @throws FactoryException * @codeCoverageIgnore */ public function init() @@ -245,7 +245,6 @@ class Plugins $header = $this->headers[$pluginName]; // Add to autoloader - // @todo Find a more reliable method for determining header directory $headerReflection = new ReflectionClass( get_class($header) ); $prefix = $header->getClassesPrefix(); $filePath = dirname($headerReflection->getFileName()) . (!empty($header->getSourceDirectory()) ? DS . $header->getSourceDirectory() : '');