From dbf05ebf4044b8b778b7073e00f0da2b9ef953ff Mon Sep 17 00:00:00 2001 From: Austin Collier Date: Wed, 20 Mar 2024 08:42:56 -0600 Subject: [PATCH] Lots more tests. Now will dynamically create object with engine instance. --- flight/Engine.php | 3 ++ flight/core/Dispatcher.php | 27 +++++++++----- tests/DispatcherTest.php | 57 +++++++++++++++++++++++++++++- tests/EngineTest.php | 5 --- tests/MapTest.php | 1 - tests/classes/ContainerDefault.php | 25 +++++++++++++ 6 files changed, 103 insertions(+), 15 deletions(-) create mode 100644 tests/classes/ContainerDefault.php diff --git a/flight/Engine.php b/flight/Engine.php index 7f45daa..62342b6 100644 --- a/flight/Engine.php +++ b/flight/Engine.php @@ -138,6 +138,9 @@ class Engine $this->dispatcher->reset(); } + // Add this class to Dispatcher + $this->dispatcher->setEngine($this); + // Register default components $this->loader->register('request', Request::class); $this->loader->register('response', Response::class); diff --git a/flight/core/Dispatcher.php b/flight/core/Dispatcher.php index e36644c..672cb82 100644 --- a/flight/core/Dispatcher.php +++ b/flight/core/Dispatcher.php @@ -6,6 +6,7 @@ namespace flight\core; use Closure; use Exception; +use flight\Engine; use InvalidArgumentException; use ReflectionClass; use TypeError; @@ -26,7 +27,10 @@ class Dispatcher private const FILTER_TYPES = [self::FILTER_BEFORE, self::FILTER_AFTER]; /** @var mixed $container_exception Exception message if thrown by setting the container as a callable method */ - public static $container_exception = null; + protected $container_exception = null; + + /** @var ?Engine $engine Engine instance */ + protected ?Engine $engine = null; /** @var array Mapped events. */ protected array $events = []; @@ -57,6 +61,11 @@ class Dispatcher $this->containerHandler = $containerHandler; } + public function setEngine(Engine $engine): void + { + $this->engine = $engine; + } + /** * Dispatches an event. * @@ -346,20 +355,22 @@ class Dispatcher } // If this tried to resolve a class in a container and failed somehow, throw the exception - if (isset($resolvedClass) === false && self::$container_exception !== null) { + if (isset($resolvedClass) === false && $this->container_exception !== null) { $this->fixOutputBuffering(); - throw self::$container_exception; + throw $this->container_exception; } - // If we made it this far, we can forget the container exception and move on... - self::$container_exception = null; - // Class is there, but no method - if (method_exists($class, $method) === false) { + if (is_object($class) === true && method_exists($class, $method) === false) { $this->fixOutputBuffering(); throw new Exception("Class found, but method '".get_class($class)."::$method' not found."); } + // Class is a string, and method exists, create the object by hand and inject only the Engine + if (is_string($class) === true) { + $class = new $class($this->engine); + } + return call_user_func_array([ $class, $method ], $params); } @@ -418,7 +429,7 @@ class Dispatcher // and store it somewhere. If we just let it throw itself, it // doesn't properly close the output buffers and can cause other // issues. - self::$container_exception = $e; + $this->container_exception = $e; } } diff --git a/tests/DispatcherTest.php b/tests/DispatcherTest.php index 5fb902c..a75f18f 100644 --- a/tests/DispatcherTest.php +++ b/tests/DispatcherTest.php @@ -4,13 +4,16 @@ declare(strict_types=1); namespace tests; +use ArgumentCountError; use Closure; use Exception; use flight\core\Dispatcher; +use flight\Engine; use InvalidArgumentException; use PharIo\Manifest\InvalidEmailException; use tests\classes\Hello; use PHPUnit\Framework\TestCase; +use tests\classes\ContainerDefault; use tests\classes\TesterClass; use TypeError; @@ -21,7 +24,6 @@ class DispatcherTest extends TestCase protected function setUp(): void { $this->dispatcher = new Dispatcher(); - Dispatcher::$container_exception = null; } public function testClosureMapping(): void @@ -247,4 +249,57 @@ class DispatcherTest extends TestCase $this->assertSame('param1', $class->param2); } + + public function testExecuteStringClassBadConstructParams(): void + { + $this->expectException(ArgumentCountError::class); + $this->expectExceptionMessageMatches('#Too few arguments to function tests\\\\classes\\\\TesterClass::__construct\(\), 1 passed .+ and exactly 6 expected#'); + $this->dispatcher->execute(TesterClass::class . '->instanceMethod'); + } + + public function testExecuteStringClassNoConstruct(): void + { + $result = $this->dispatcher->execute(Hello::class . '->sayHi'); + $this->assertSame('hello', $result); + } + + public function testExecuteStringClassNoConstructDoubleColon(): void + { + $result = $this->dispatcher->execute(Hello::class . '::sayHi'); + $this->assertSame('hello', $result); + } + + public function testExecuteStringClassNoConstructArraySyntax(): void + { + $result = $this->dispatcher->execute([ Hello::class, 'sayHi' ]); + $this->assertSame('hello', $result); + } + + public function testExecuteStringClassDefaultContainer(): void + { + $this->dispatcher->setEngine(new Engine); + $result = $this->dispatcher->execute(ContainerDefault::class . '->testTheContainer'); + $this->assertSame('You got it boss!', $result); + } + + public function testExecuteStringClassDefaultContainerDoubleColon(): void + { + $this->dispatcher->setEngine(new Engine); + $result = $this->dispatcher->execute(ContainerDefault::class . '::testTheContainer'); + $this->assertSame('You got it boss!', $result); + } + + public function testExecuteStringClassDefaultContainerArraySyntax(): void + { + $this->dispatcher->setEngine(new Engine); + $result = $this->dispatcher->execute([ ContainerDefault::class, 'testTheContainer' ]); + $this->assertSame('You got it boss!', $result); + } + + public function testExecuteStringClassDefaultContainerButForgotInjectingEngine(): void + { + $this->expectException(TypeError::class); + $this->expectExceptionMessageMatches('#Argument 1 passed to tests\\\\classes\\\\ContainerDefault::__construct\(\) must be an instance of flight\\\\Engine, null given#'); + $result = $this->dispatcher->execute([ ContainerDefault::class, 'testTheContainer' ]); + } } diff --git a/tests/EngineTest.php b/tests/EngineTest.php index ba8739b..64c8911 100644 --- a/tests/EngineTest.php +++ b/tests/EngineTest.php @@ -21,7 +21,6 @@ class EngineTest extends TestCase public function setUp(): void { $_SERVER = []; - Dispatcher::$container_exception = null; } public function tearDown(): void @@ -751,8 +750,6 @@ class EngineTest extends TestCase $this->expectExceptionMessage("Class 'BadClass' not found. Is it being correctly autoloaded with Flight::path()?"); $engine->start(); - - $this->assertEquals('Class BadClass not found', Dispatcher::$container_exception->getMessage()); } public function testContainerDiceBadMethod() { @@ -775,8 +772,6 @@ class EngineTest extends TestCase $this->expectExceptionMessage("Class found, but method 'tests\classes\Container::badMethod' not found."); $engine->start(); - - $this->assertNull(Dispatcher::$container_exception); } public function testContainerPsr11() { diff --git a/tests/MapTest.php b/tests/MapTest.php index 79c39dc..bf276fb 100644 --- a/tests/MapTest.php +++ b/tests/MapTest.php @@ -16,7 +16,6 @@ class MapTest extends TestCase protected function setUp(): void { - Dispatcher::$container_exception = null; $this->app = new Engine(); } diff --git a/tests/classes/ContainerDefault.php b/tests/classes/ContainerDefault.php new file mode 100644 index 0000000..28abf4a --- /dev/null +++ b/tests/classes/ContainerDefault.php @@ -0,0 +1,25 @@ +set('test_me_out', 'You got it boss!'); + $this->app = $engine; + } + + public function testTheContainer() + { + return $this->app->get('test_me_out'); + } + +}