refactored some errors. UI Tests and cleanup.

pull/559/head
Austin Collier 10 months ago
parent dbf05ebf40
commit 1b6c5b942b

@ -19,11 +19,12 @@ require_once __DIR__ . '/autoload.php';
* @copyright Copyright (c) 2011, Mike Cao <mike@mikecao.com>
*
* # Core methods
* @method static void start() Starts the framework.
* @method static void path(string $path) Adds a path for autoloading classes.
* @method static void stop(?int $code = null) Stops the framework and sends a response.
* @method static void halt(int $code = 200, string $message = '', bool $actuallyExit = true)
* @method static void start() Starts the framework.
* @method static void path(string $path) Adds a path for autoloading classes.
* @method static void stop(?int $code = null) Stops the framework and sends a response.
* @method static void halt(int $code = 200, string $message = '', bool $actuallyExit = true)
* Stop the framework with an optional status code and message.
* @method static void registerContainerHandler(callable|object $containerHandler) Registers a container handler.
*
* # Routing
* @method static Route route(string $pattern, callable|string $callback, bool $pass_route = false, string $alias = '')

@ -26,8 +26,8 @@ class Dispatcher
public const FILTER_AFTER = '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 */
protected $container_exception = null;
/** @var mixed $containerException Exception message if thrown by setting the container as a callable method */
protected $containerException = null;
/** @var ?Engine $engine Engine instance */
protected ?Engine $engine = null;
@ -256,8 +256,6 @@ class Dispatcher
$callback = $this->parseStringClassAndMethod($callback);
}
$this->handleInvalidCallbackType($callback);
return $this->invokeCallable($callback, $params);
}
@ -325,10 +323,12 @@ class Dispatcher
{
// If this is a directly callable function, call it
if (is_array($func) === false) {
$this->verifyValidFunction($func);
return call_user_func_array($func, $params);
}
[$class, $method] = $func;
$resolvedClass = null;
// Only execute the container handler if it's not a Flight class
if (
@ -343,28 +343,12 @@ class Dispatcher
) {
$containerHandler = $this->containerHandler;
$resolvedClass = $this->resolveContainerClass($containerHandler, $class, $params);
if($resolvedClass !== null) {
if ($resolvedClass !== null) {
$class = $resolvedClass;
}
}
// Final check to make sure it's actually a class and a method, or throw an error
if (is_object($class) === false && class_exists($class) === false) {
$this->fixOutputBuffering();
throw new Exception("Class '$class' not found. Is it being correctly autoloaded with Flight::path()?");
}
// If this tried to resolve a class in a container and failed somehow, throw the exception
if (isset($resolvedClass) === false && $this->container_exception !== null) {
$this->fixOutputBuffering();
throw $this->container_exception;
}
// Class is there, but no method
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.");
}
$this->verifyValidClassCallable($class, $method, $resolvedClass);
// Class is a string, and method exists, create the object by hand and inject only the Engine
if (is_string($class) === true) {
@ -382,7 +366,7 @@ class Dispatcher
*
* @throws InvalidArgumentException If `$callback` is an invalid type
*/
protected function handleInvalidCallbackType($callback): void
protected function verifyValidFunction($callback): void
{
$isInvalidFunctionName = (
is_string($callback)
@ -394,6 +378,41 @@ class Dispatcher
}
}
/**
* Verifies if the provided class and method are valid callable.
*
* @param string|object $class The class name.
* @param string $method The method name.
* @param object|null $resolvedClass The resolved class.
*
* @throws Exception If the class or method is not found.
*
* @return void
*/
protected function verifyValidClassCallable($class, $method, $resolvedClass): void
{
$final_exception = null;
// Final check to make sure it's actually a class and a method, or throw an error
if (is_object($class) === false && class_exists($class) === false) {
$final_exception = new Exception("Class '$class' not found. Is it being correctly autoloaded with Flight::path()?");
// If this tried to resolve a class in a container and failed somehow, throw the exception
} elseif (isset($resolvedClass) === false && $this->containerException !== null) {
$final_exception = $this->containerException;
// Class is there, but no method
} elseif (is_object($class) === true && method_exists($class, $method) === false) {
$final_exception = new Exception("Class found, but method '" . get_class($class) . "::$method' not found.");
}
if ($final_exception !== null) {
$this->fixOutputBuffering();
throw $final_exception;
}
}
/**
* Resolves the container class.
*
@ -417,7 +436,6 @@ class Dispatcher
// Just a callable where you configure the behavior (Dice, PHP-DI, etc.)
} elseif (is_callable($container_handler) === true) {
// This is to catch all the error that could be thrown by whatever container you are using
try {
$class_object = call_user_func($container_handler, $class, $params);
@ -425,11 +443,12 @@ class Dispatcher
// could not resolve a class for some reason
$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
// 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.
$this->container_exception = $e;
// This is thrown in the verifyValidClassCallable method
$this->containerException = $e;
}
}
@ -444,7 +463,7 @@ class Dispatcher
protected function fixOutputBuffering(): void
{
// Cause PHPUnit has 1 level of output buffering by default
if(ob_get_level() > (getenv('PHPUNIT_TEST') ? 1 : 0)) {
if (ob_get_level() > (getenv('PHPUNIT_TEST') ? 1 : 0)) {
ob_end_clean();
}
}

@ -277,21 +277,21 @@ class DispatcherTest extends TestCase
public function testExecuteStringClassDefaultContainer(): void
{
$this->dispatcher->setEngine(new Engine);
$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);
$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);
$this->dispatcher->setEngine(new Engine());
$result = $this->dispatcher->execute([ ContainerDefault::class, 'testTheContainer' ]);
$this->assertSame('You got it boss!', $result);
}

@ -5,7 +5,6 @@ declare(strict_types=1);
namespace tests;
use Exception;
use flight\core\Dispatcher;
use flight\database\PdoWrapper;
use flight\Engine;
use flight\net\Request;

@ -5,7 +5,6 @@ declare(strict_types=1);
namespace tests;
use Exception;
use flight\core\Dispatcher;
use flight\Engine;
use tests\classes\Hello;
use PHPUnit\Framework\TestCase;

@ -26,7 +26,7 @@ class Container
public function testThePdoWrapper()
{
$value = (int) $this->pdoWrapper->fetchField('SELECT 5');
$value = intval($this->pdoWrapper->fetchField('SELECT 5'));
echo 'Yay! I injected a PdoWrapper, and it returned the number ' . $value . ' from the database!';
}
}

@ -8,7 +8,6 @@ use flight\Engine;
class ContainerDefault
{
protected Engine $app;
public function __construct(Engine $engine)
@ -22,4 +21,8 @@ class ContainerDefault
return $this->app->get('test_me_out');
}
public function testUi()
{
echo '<span id="infotext">Route text:</span> The container successfully injected a value into the engine! Engine class: <b>' . get_class($this->app) . '</b> test_me_out Value: <b>' . $this->app->get('test_me_out') . '</b>';
}
}

@ -81,6 +81,8 @@ class LayoutMiddleware
<li><a href="/redirect/before%2Fafter">Slash in Param</a></li>
<li><a href="/わたしはひとです">UTF8 URL</a></li>
<li><a href="/わたしはひとです/ええ">UTF8 URL w/ Param</a></li>
<li><a href="/dice">Dice Container</a></li>
<li><a href="/no-container">No Container Registered</a></li>
</ul>
HTML;
echo '<div id="container">';

@ -2,6 +2,10 @@
declare(strict_types=1);
use flight\database\PdoWrapper;
use tests\classes\Container;
use tests\classes\ContainerDefault;
/*
* This is the test file where we can open up a quick test server and make
* sure that the UI is really working the way we would expect it to.
@ -139,6 +143,9 @@ Flight::group('', function () {
Flight::route('/redirect/@id', function ($id) {
echo '<span id="infotext">Route text:</span> This route status is that it <span style="color:' . ($id === 'before/after' ? 'green' : 'red') . '; font-weight: bold;">' . ($id === 'before/after' ? 'succeeded' : 'failed') . ' URL Param: ' . $id . '</span>';
});
Flight::route('/no-container', ContainerDefault::class . '->testUi');
Flight::route('/dice', Container::class . '->testThePdoWrapper');
}, [ new LayoutMiddleware() ]);
// Test 9: JSON output (should not output any other html)
@ -167,4 +174,23 @@ Flight::map('notFound', function () {
echo "<a href='/'>Go back</a>";
});
Flight::map('start', function () {
if (Flight::request()->url === '/dice') {
$dice = new \Dice\Dice();
$dice = $dice->addRules([
PdoWrapper::class => [
'shared' => true,
'constructParams' => [ 'sqlite::memory:' ]
]
]);
Flight::registerContainerHandler(function ($class, $params) use ($dice) {
return $dice->create($class, $params);
});
}
// Default start behavior now
Flight::_start();
});
Flight::start();

Loading…
Cancel
Save