Added more tests. Captured callable exception thrown

pull/559/head
Austin Collier 10 months ago
parent f610adfc14
commit 3f887e305f

@ -220,13 +220,13 @@ class Engine
/** /**
* Registers the container handler * Registers the container handler
* *
* @param callable $callback Callback function that sets the container and how it will inject classes * @param callable|object $containerHandler Callback function or PSR-11 Container object that sets the container and how it will inject classes
* *
* @return void * @return void
*/ */
public function registerContainerHandler($callback): void public function registerContainerHandler($containerHandler): void
{ {
$this->dispatcher->setContainerHandler($callback); $this->dispatcher->setContainerHandler($containerHandler);
} }
/** /**

@ -25,6 +25,9 @@ class Dispatcher
public const FILTER_AFTER = 'after'; public const FILTER_AFTER = 'after';
private const FILTER_TYPES = [self::FILTER_BEFORE, self::FILTER_AFTER]; 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;
/** @var array<string, Closure(): (void|mixed)> Mapped events. */ /** @var array<string, Closure(): (void|mixed)> Mapped events. */
protected array $events = []; protected array $events = [];
@ -40,18 +43,18 @@ class Dispatcher
* *
* @var callable|object|null * @var callable|object|null
*/ */
protected $container_handler = null; protected $containerHandler = null;
/** /**
* Sets the dependency injection container handler. * Sets the dependency injection container handler.
* *
* @param callable|object $container_handler Dependency injection container * @param callable|object $containerHandler Dependency injection container
* *
* @return void * @return void
*/ */
public function setContainerHandler($container_handler): void public function setContainerHandler($containerHandler): void
{ {
$this->container_handler = $container_handler; $this->containerHandler = $containerHandler;
} }
/** /**
@ -246,11 +249,7 @@ class Dispatcher
$this->handleInvalidCallbackType($callback); $this->handleInvalidCallbackType($callback);
if (is_array($callback) === true) { return $this->invokeCallable($callback, $params);
return $this->invokeMethod($callback, $params);
}
return $this->callFunction($callback, $params);
} }
/** /**
@ -311,6 +310,7 @@ class Dispatcher
* @return mixed Function results * @return mixed Function results
* @throws TypeError For nonexistent class name. * @throws TypeError For nonexistent class name.
* @throws InvalidArgumentException If the constructor requires parameters * @throws InvalidArgumentException If the constructor requires parameters
* @version 3.7.0
*/ */
public function invokeCallable($func, array &$params = []) public function invokeCallable($func, array &$params = [])
{ {
@ -321,9 +321,9 @@ class Dispatcher
[$class, $method] = $func; [$class, $method] = $func;
// Only execute this if it's not a Flight class // Only execute the container handler if it's not a Flight class
if ( if (
$this->container_handler !== null && $this->containerHandler !== null &&
( (
( (
is_object($class) === true && is_object($class) === true &&
@ -332,13 +332,15 @@ class Dispatcher
is_string($class) === true is_string($class) === true
) )
) { ) {
$container_handler = $this->container_handler; $containerHandler = $this->containerHandler;
$resolved_class = $this->resolveContainerClass($container_handler, $class, $params); $resolvedClass = $this->resolveContainerClass($containerHandler, $class, $params);
if($resolved_class !== null) { if($resolvedClass !== null) {
$class = $resolved_class; $class = $resolvedClass;
} }
} }
// If there's no container handler set, you can use [ 'className', 'methodName' ]
// to call a method dynamically. Nothing is injected into the class though.
if (is_string($class) && class_exists($class)) { if (is_string($class) && class_exists($class)) {
$constructor = (new ReflectionClass($class))->getConstructor(); $constructor = (new ReflectionClass($class))->getConstructor();
$constructorParamsNumber = 0; $constructorParamsNumber = 0;
@ -362,17 +364,20 @@ class Dispatcher
// Final check to make sure it's actually a class and a method, or throw an error // Final check to make sure it's actually a class and a method, or throw an error
if (is_object($class) === false) { if (is_object($class) === false) {
if(ob_get_level() > 1) { // Cause PHPUnit has 1 level of output buffering by default
if(ob_get_level() > (getenv('PHPUNIT_TEST') ? 1 : 0)) {
ob_end_clean(); ob_end_clean();
} }
throw new Exception("Class '$class' not found. Is it being correctly autoloaded with Flight::path()?"); throw new Exception("Class '$class' not found. Is it being correctly autoloaded with Flight::path()?");
} }
// Class is there, but no method
if (method_exists($class, $method) === false) { if (method_exists($class, $method) === false) {
if(ob_get_level() > 1) { // Cause PHPUnit has 1 level of output buffering by default
if(ob_get_level() > (getenv('PHPUNIT_TEST') ? 1 : 0)) {
ob_end_clean(); ob_end_clean();
} }
throw new Exception("Method '".get_class($class)."::$method' not found."); throw new Exception("Class found, but method '".get_class($class)."::$method' not found.");
} }
return call_user_func_array([ $class, $method ], $params); return call_user_func_array([ $class, $method ], $params);
@ -428,6 +433,12 @@ class Dispatcher
} catch (Exception $e) { } catch (Exception $e) {
// could not resolve a class for some reason // could not resolve a class for some reason
$class_object = null; $class_object = null;
// If the container throws an exception, we need to catch it
// 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;
} }
} }

@ -192,7 +192,7 @@ class DispatcherTest extends TestCase
{ {
set_error_handler(function (int $errno, string $errstr): void { set_error_handler(function (int $errno, string $errstr): void {
$this->assertSame(E_USER_NOTICE, $errno); $this->assertSame(E_USER_NOTICE, $errno);
$this->assertSame("Event 'myMethod' has been overriden!", $errstr); $this->assertSame("Event 'myMethod' has been overridden!", $errstr);
}); });
$this->dispatcher->set('myMethod', function (): string { $this->dispatcher->set('myMethod', function (): string {
@ -200,10 +200,10 @@ class DispatcherTest extends TestCase
}); });
$this->dispatcher->set('myMethod', function (): string { $this->dispatcher->set('myMethod', function (): string {
return 'Overriden'; return 'Overridden';
}); });
$this->assertSame('Overriden', $this->dispatcher->run('myMethod')); $this->assertSame('Overridden', $this->dispatcher->run('myMethod'));
restore_error_handler(); restore_error_handler();
} }
@ -219,7 +219,7 @@ class DispatcherTest extends TestCase
return 'Original'; return 'Original';
}) })
->hook('myMethod', 'invalid', function (array &$params, &$output): void { ->hook('myMethod', 'invalid', function (array &$params, &$output): void {
$output = 'Overriden'; $output = 'Overridden';
}); });
$this->assertSame('Original', $this->dispatcher->run('myMethod')); $this->assertSame('Original', $this->dispatcher->run('myMethod'));

@ -6,6 +6,7 @@ namespace tests;
use Exception; use Exception;
use Flight; use Flight;
use flight\core\Dispatcher;
use flight\Engine; use flight\Engine;
use flight\net\Request; use flight\net\Request;
use flight\net\Response; use flight\net\Response;
@ -706,6 +707,8 @@ class EngineTest extends TestCase
$this->expectExceptionMessage("Class 'BadClass' not found. Is it being correctly autoloaded with Flight::path()?"); $this->expectExceptionMessage("Class 'BadClass' not found. Is it being correctly autoloaded with Flight::path()?");
$engine->start(); $engine->start();
$this->assertEquals('Class BadClass not found', Dispatcher::$container_exception->getMessage());
} }
public function testContainerDiceBadMethod() { public function testContainerDiceBadMethod() {
@ -719,9 +722,11 @@ class EngineTest extends TestCase
$engine->request()->url = '/container'; $engine->request()->url = '/container';
$this->expectException(Exception::class); $this->expectException(Exception::class);
$this->expectExceptionMessage("Method 'tests\classes\Container::badMethod' not found."); $this->expectExceptionMessage("Class found, but method 'tests\classes\Container::badMethod' not found.");
$engine->start(); $engine->start();
$this->assertNull(Dispatcher::$container_exception);
} }
public function testContainerPsr11() { public function testContainerPsr11() {
@ -765,7 +770,7 @@ class EngineTest extends TestCase
$engine->request()->url = '/container'; $engine->request()->url = '/container';
$this->expectException(Exception::class); $this->expectException(Exception::class);
$this->expectExceptionMessage("Method 'tests\classes\Container::badMethod' not found."); $this->expectExceptionMessage("Class found, but method 'tests\classes\Container::badMethod' not found.");
$engine->start(); $engine->start();
} }

Loading…
Cancel
Save