Merge pull request #685 from flightphp/fix-phpcs-errors

Fix phpcs errors
master
n0nag0n 7 hours ago committed by GitHub
commit 3941d44c70
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -38,12 +38,15 @@
"psr-4": {
"Tests\\PHP8\\": [
"tests/named-arguments"
]
],
"Tests\\Server\\": "tests/server",
"Tests\\ServerV2\\": "tests/server-v2",
"tests\\groupcompactsyntax\\": "tests/groupcompactsyntax"
}
},
"require-dev": {
"ext-pdo_sqlite": "*",
"flightphp/container": "^1.0",
"flightphp/container": "^1.3",
"flightphp/runway": "^1.2",
"league/container": "^4.2",
"level-2/dice": "^4.0",
@ -51,7 +54,7 @@
"phpstan/phpstan": "^2.1",
"phpunit/phpunit": "^9.6",
"rregeer/phpunit-coverage-check": "^0.3.1",
"squizlabs/php_codesniffer": "^3.11"
"squizlabs/php_codesniffer": "^4.0"
},
"config": {
"allow-plugins": {
@ -84,7 +87,7 @@
],
"lint": "phpstan --no-progress --memory-limit=256M",
"beautify": "phpcbf --standard=phpcs.xml",
"phpcs": "phpcs --standard=phpcs.xml -n",
"phpcs": "phpcs",
"post-install-cmd": [
"php -r \"if (!file_exists('phpcs.xml')) copy('phpcs.xml.dist', 'phpcs.xml');\"",
"php -r \"if (!file_exists('phpstan.neon')) copy('phpstan.dist.neon', 'phpstan.neon');\"",

@ -267,7 +267,8 @@ class Engine
/**
* Registers the container handler
*
* @param ContainerInterface|callable(class-string<T> $id, array<int|string, mixed> $params): ?T $containerHandler Callback function or PSR-11 Container object that sets the container and how it will inject classes
* @param ContainerInterface|callable(class-string<T> $id, array<int|string, mixed> $params): ?T $containerHandler
* Callback function or PSR-11 Container object that sets the container and how it will inject classes
*
* @template T of object
*/
@ -488,7 +489,14 @@ class Engine
// Which loosely translates to $class->$method($params)
$start = microtime(true);
$middlewareResult = $middlewareObject($params);
$this->triggerEvent('flight.middleware.executed', $route, $middleware, $eventName, microtime(true) - $start);
$this->triggerEvent(
'flight.middleware.executed',
$route,
$middleware,
$eventName,
microtime(true) - $start
);
if ($useV3OutputBuffering === true) {
$this->response()->write(ob_get_clean());
@ -860,7 +868,12 @@ class Engine
public function _methodNotFound(Route $route): void
{
$this->response()->setHeader('Allow', implode(', ', $route->methods));
$this->halt(405, 'Method Not Allowed. Allowed Methods are: ' . implode(', ', $route->methods), empty(getenv('PHPUNIT_TEST')));
$this->halt(
405,
'Method Not Allowed. Allowed Methods are: ' . implode(', ', $route->methods),
empty(getenv('PHPUNIT_TEST'))
);
}
/**

@ -80,8 +80,10 @@ require_once __DIR__ . '/autoload.php';
* @phpstan-method static void jsonHalt(mixed $data, int $code = 200, bool $encode = true, string $charset = 'utf-8', int $option = 0)
* @phpstan-method static void jsonp(mixed $data, string $param = 'jsonp', int $code = 200, bool $encode = true, string $charset = "utf8", int $encodeOption = 0, int $encodeDepth = 512)
*
* Note: IDEs will use standard @method tags for autocompletion, while PHPStan will use @phpstan-* tags for advanced type checking.
* Note: IDEs will use standard @method tags for autocompletion,
* while PHPStan will use @phpstan-* tags for advanced type checking.
*/
// phpcs:ignore PSR1.Classes.ClassDeclaration.MissingNamespace
class Flight
{
/**

@ -20,7 +20,13 @@ class AiGenerateInstructionsCommand extends AbstractBaseCommand
public function __construct(array $config)
{
parent::__construct('ai:generate-instructions', 'Generate project-specific AI coding instructions', $config);
$this->option('--config-file', 'Path to .runway-config.json file (deprecated, use config.php instead)', null, '');
$this->option(
'--config-file',
'Path to .runway-config.json file (deprecated, use config.php instead)',
null,
''
);
}
/**
@ -40,7 +46,12 @@ class AiGenerateInstructionsCommand extends AbstractBaseCommand
if (empty($this->config['runway'])) {
$configFile = $this->configFile;
$io = $this->app()->io();
$io->warn('The --config-file option is deprecated. Move your config values to the \'runway\' key in the config.php file for configuration.', true);
$io->warn(
'The --config-file option is deprecated. '
. 'Move your config values to the \'runway\' key in the config.php file for configuration.',
true
);
$runwayConfig = json_decode(file_get_contents($configFile), true) ?? [];
} else {
$runwayConfig = $this->config['runway'];
@ -56,12 +67,30 @@ class AiGenerateInstructionsCommand extends AbstractBaseCommand
// Ask questions
$projectDesc = $io->prompt('Please describe what your project is for?');
$database = $io->prompt('What database are you planning on using? (e.g. MySQL, SQLite, PostgreSQL, none)', 'none');
$templating = $io->prompt('What HTML templating engine will you plan on using (if any)? (recommend latte)', 'latte');
$database = $io->prompt(
'What database are you planning on using? (e.g. MySQL, SQLite, PostgreSQL, none)',
'none'
);
$templating = $io->prompt(
'What HTML templating engine will you plan on using (if any)? (recommend latte)',
'latte'
);
$security = $io->confirm('Is security an important element of this project?', 'y');
$performance = $io->confirm('Is performance and speed an important part of this project?', 'y');
$composerLibs = $io->prompt('What major composer libraries will you be using if you know them right now?', 'none');
$envSetup = $io->prompt('How will you set up your development environment? (e.g. Docker, Vagrant, PHP dev server, other)', 'Docker');
$composerLibs = $io->prompt(
'What major composer libraries will you be using if you know them right now?',
'none'
);
$envSetup = $io->prompt(
'How will you set up your development environment? (e.g. Docker, Vagrant, PHP dev server, other)',
'Docker'
);
$teamSize = $io->prompt('How many developers will be working on this project?', '1');
$api = $io->confirm('Will this project expose an API?', 'n');
$other = $io->prompt('Any other important requirements or context? (optional)', 'no');
@ -107,7 +136,13 @@ class AiGenerateInstructionsCommand extends AbstractBaseCommand
$data = [
'model' => $model,
'messages' => [
['role' => 'system', 'content' => 'You are a helpful AI coding assistant focused on the Flight Framework for PHP. You are up to date with all your knowledge from https://docs.flightphp.com. As an expert into the programming language PHP, you are top notch at architecting out proper instructions for FlightPHP projects.'],
[
'role' => 'system',
'content' => 'You are a helpful AI coding assistant focused on the Flight Framework for PHP. '
. 'You are up to date with all your knowledge from https://docs.flightphp.com. '
. 'As an expert into the programming language PHP, '
. 'you are top notch at architecting out proper instructions for FlightPHP projects.'
],
['role' => 'user', 'content' => $prompt],
],
'temperature' => 0.2,
@ -129,7 +164,11 @@ class AiGenerateInstructionsCommand extends AbstractBaseCommand
}
// Write to files
$io->info('Updating .github/copilot-instructions.md, .cursor/rules/project-overview.mdc, .gemini/GEMINI.md and .windsurfrules...', true);
$io->info(
'Updating .github/copilot-instructions.md, .cursor/rules/project-overview.mdc, .gemini/GEMINI.md and .windsurfrules...',
true
);
if (!is_dir($this->projectRoot . '.github')) {
mkdir($this->projectRoot . '.github', 0755, true);
}

@ -79,7 +79,11 @@ class AiInitCommand extends AbstractBaseCommand
$defaultModel = 'claude-sonnet-4-5';
break;
}
$model = trim($io->prompt('Enter the model name you want to use (e.g. gpt-5, claude-sonnet-4-5, etc)', $defaultModel));
$model = trim($io->prompt(
'Enter the model name you want to use (e.g. gpt-5, claude-sonnet-4-5, etc)',
$defaultModel
));
$runwayAiConfig = [
'provider' => $api,

@ -31,8 +31,16 @@ class ControllerCommand extends AbstractBaseCommand
$io = $this->app()->io();
if (empty($this->config['runway'])) {
$io->warn('Using a .runway-config.json file is deprecated. Move your config values to app/config/config.php with `php runway config:migrate`.', true); // @codeCoverageIgnore
$runwayConfig = json_decode(file_get_contents($this->projectRoot . '/.runway-config.json'), true); // @codeCoverageIgnore
$io->warn(
'Using a .runway-config.json file is deprecated. '
. 'Move your config values to app/config/config.php with `php runway config:migrate`.',
true
); // @codeCoverageIgnore
$runwayConfig = json_decode(
file_get_contents($this->projectRoot . '/.runway-config.json'),
true
); // @codeCoverageIgnore
} else {
$runwayConfig = $this->config['runway'];
}
@ -95,6 +103,9 @@ class ControllerCommand extends AbstractBaseCommand
protected function persistClass(string $controllerName, PhpFile $file, string $appRoot)
{
$printer = new \Nette\PhpGenerator\PsrPrinter();
file_put_contents($this->projectRoot . '/' . $appRoot . 'controllers/' . $controllerName . '.php', $printer->printFile($file));
file_put_contents(
$this->projectRoot . '/' . $appRoot . 'controllers/' . $controllerName . '.php',
$printer->printFile($file)
);
}
}

@ -118,13 +118,7 @@ class RouteCommand extends AbstractBaseCommand
$methods = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'];
foreach ($methods as $method) {
$lowercaseMethod = strtolower($method);
if (
$this->{$lowercaseMethod} === true &&
(
$route->methods[0] === '*' ||
in_array($method, $route->methods, true) === true
)
) {
if ($this->{$lowercaseMethod} === true && ($route->methods[0] === '*' || in_array($method, $route->methods, true) === true)) {
$boolval = true;
break;
}

@ -58,16 +58,14 @@ class Dispatcher
*
* @template T of object
*
* @throws InvalidArgumentException If $containerHandler is not a `callable` or instance of `Psr\Container\ContainerInterface`.
* @throws InvalidArgumentException
* If $containerHandler is not a `callable` or instance of `Psr\Container\ContainerInterface`.
*/
public function setContainerHandler($containerHandler): void
{
$containerInterfaceNS = '\Psr\Container\ContainerInterface';
if (
is_a($containerHandler, $containerInterfaceNS)
|| is_callable($containerHandler)
) {
if (is_a($containerHandler, $containerInterfaceNS) || is_callable($containerHandler)) {
$this->containerHandler = $containerHandler;
return;
@ -289,10 +287,7 @@ class Dispatcher
*/
public function execute($callback, array &$params = [])
{
if (
is_string($callback) === true
&& (strpos($callback, '->') !== false || strpos($callback, '::') !== false)
) {
if (is_string($callback) === true && (strpos($callback, '->') !== false || strpos($callback, '::') !== false)) {
$callback = $this->parseStringClassAndMethod($callback);
}
@ -419,7 +414,10 @@ class Dispatcher
// 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) {
$exception = new Exception("Class '$class' not found. Is it being correctly autoloaded with Flight::path()?");
$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 (!$resolvedClass && $this->containerException !== null) {

@ -439,15 +439,13 @@ class Request
*/
public static function getScheme(): string
{
if (
(strtolower(self::getVar('HTTPS')) === 'on')
||
(self::getVar('HTTP_X_FORWARDED_PROTO') === 'https')
||
(self::getVar('HTTP_FRONT_END_HTTPS') === 'on')
||
(self::getVar('REQUEST_SCHEME') === 'https')
) {
if (strtolower(self::getVar('HTTPS')) === 'on') {
return 'https';
} elseif (self::getVar('HTTP_X_FORWARDED_PROTO') === 'https') {
return 'https';
} elseif (self::getVar('HTTP_FRONT_END_HTTPS') === 'on') {
return 'https';
} elseif (self::getVar('REQUEST_SCHEME') === 'https') {
return 'https';
}
@ -478,7 +476,8 @@ class Request
/**
* Retrieves the array of uploaded files.
*
* @return array<string, UploadedFile|array<int, UploadedFile>> Key is field name; value is either a single UploadedFile or an array of UploadedFile when multiple were uploaded.
* @return array<string, UploadedFile|array<int, UploadedFile>>
* Key is field name; value is either a single UploadedFile or an array of UploadedFile when multiple were uploaded.
*/
public function getUploadedFiles(): array
{

@ -484,7 +484,8 @@ class Response
* Downloads a file.
*
* @param string $filePath The path to the file to be downloaded.
* @param string $fileName The name the downloaded file should have. If not provided or is an empty string, the name of the file on disk will be used.
* @param string $fileName The name the downloaded file should have.
* If not provided or is an empty string, the name of the file on disk will be used.
*
* @throws Exception If the file cannot be found.
*

@ -48,8 +48,14 @@ class UploadedFile
* @param int $error The error code associated with the uploaded file.
* @param bool|null $isPostUploadedFile Indicates if the file was uploaded via POST method.
*/
public function __construct(string $name, string $mimeType, int $size, string $tmpName, int $error, ?bool $isPostUploadedFile = null)
{
public function __construct(
string $name,
string $mimeType,
int $size,
string $tmpName,
int $error,
?bool $isPostUploadedFile = null
) {
$this->name = $name;
$this->mimeType = $mimeType;
$this->size = $size;
@ -154,7 +160,7 @@ class UploadedFile
$uploadFunctionToCall = $isUploadedFile === true ?
// Standard POST upload - use move_uploaded_file for security
'move_uploaded_file' :
// Handle non-POST uploads (PATCH, PUT, DELETE) or other valid temp files
// Handle non-POST uploads (PATCH, PUT, DELETE) or other valid temp files
'rename';
$result = $uploadFunctionToCall($this->tmpName, $targetPath);

@ -3,6 +3,7 @@
declare(strict_types=1);
// This file is only here so that the PHP8 attribute for doesn't throw an error in files
// phpcs:ignore PSR1.Classes.ClassDeclaration.MissingNamespace
class ReturnTypeWillChange
{
//

@ -1,54 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<ruleset
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="./vendor/squizlabs/php_codesniffer/phpcs.xsd"
name="pcsg-generated-ruleset">
<description>
Created with the PHP Coding Standard Generator.
http://edorian.github.io/php-coding-standard-generator/
</description>
xsi:noNamespaceSchemaLocation="vendor/squizlabs/php_codesniffer/phpcs.xsd">
<!-- <arg name="report" value="source" /> -->
<!-- <arg name="report" value="summary" /> -->
<!-- <arg name="report" value="diff" /> -->
<arg name="report" value="full" />
<arg name="report-width" value="80" />
<arg name="colors" />
<arg name="encoding" value="utf-8" />
<arg name="tab-width" value="4" />
<rule ref="PSR1">
<exclude name="PSR1.Classes.ClassDeclaration.MissingNamespace" />
</rule>
<rule ref="PSR12" />
<rule ref="Generic">
<exclude name="Generic.PHP.ClosingPHPTag.NotFound" />
<exclude name="Generic.PHP.UpperCaseConstant.Found" />
<exclude name="Generic.Arrays.DisallowShortArraySyntax.Found" />
<exclude name="Generic.Files.EndFileNoNewline.Found" />
<exclude name="Generic.Files.LowercasedFilename.NotFound" />
<exclude name="Generic.Commenting.DocComment.TagValueIndent" />
<exclude name="Generic.Classes.OpeningBraceSameLine.BraceOnNewLine" />
<exclude name="Generic.WhiteSpace.DisallowSpaceIndent.SpacesUsed" />
<exclude name="Generic.Commenting.DocComment.ContentAfterOpen" />
<exclude name="Generic.Commenting.DocComment.ContentBeforeClose" />
<exclude name="Generic.Commenting.DocComment.MissingShort" />
<exclude name="Generic.Commenting.DocComment.SpacingBeforeShort" />
<exclude name="Generic.Formatting.NoSpaceAfterCast.SpaceFound" />
<exclude name="Generic.Functions.OpeningFunctionBraceKernighanRitchie.BraceOnNewLine" />
<exclude name="Generic.Formatting.MultipleStatementAlignment.NotSameWarning" />
<exclude name="Generic.Functions.OpeningFunctionBraceBsdAllman.BraceOnSameLine" />
<exclude name="Generic.PHP.DisallowRequestSuperglobal.Found" />
<exclude name="Generic.WhiteSpace.DisallowSpaceIndent.SpacesUsedHeredocCloser" />
<rule ref="PSR2">
</rule>
<rule ref="Generic.Files.LineLength">
<properties>
<property name="ignoreComments" value="true" />
</properties>
<rule ref="PSR12">
<exclude name="PSR12.Classes.AnonClassDeclaration.SpaceAfterKeyword"/>
</rule>
<rule ref="Generic.Formatting.SpaceAfterNot">
<properties>
<property name="spacing" value="0" />
</properties>
</rule>
<rule ref="Generic.WhiteSpace.ArbitraryParenthesesSpacing">
<properties>
<property name="ignoreNewlines" value="true" />
</properties>
</rule>
<file>flight/</file>
<file>tests/</file>
<exclude-pattern>tests/views/*</exclude-pattern>
<file>index.php</file>
<file>flight</file>
<file>tests</file>
</ruleset>

@ -38,7 +38,7 @@ class DispatcherTest extends TestCase
public function testFunctionMapping(): void
{
$this->dispatcher->set('map2', fn (): string => 'hello');
$this->dispatcher->set('map2', fn(): string => 'hello');
$this->assertSame('hello', $this->dispatcher->run('map2'));
}
@ -46,6 +46,7 @@ class DispatcherTest extends TestCase
public function testHasEvent(): void
{
$this->dispatcher->set('map-event', function (): void {
//
});
$this->assertTrue($this->dispatcher->has('map-event'));
@ -54,6 +55,7 @@ class DispatcherTest extends TestCase
public function testClearAllRegisteredEvents(): void
{
$customFunction = $anotherFunction = function (): void {
//
};
$this->dispatcher
@ -72,6 +74,7 @@ class DispatcherTest extends TestCase
public function testClearDeclaredRegisteredEvent(): void
{
$customFunction = $anotherFunction = function (): void {
//
};
$this->dispatcher
@ -110,7 +113,7 @@ class DispatcherTest extends TestCase
public function testBeforeAndAfter(): void
{
$this->dispatcher->set('hello', fn (string $name): string => "Hello, $name!");
$this->dispatcher->set('hello', fn(string $name): string => "Hello, $name!");
$this->dispatcher
->hook('hello', Dispatcher::FILTER_BEFORE, function (array &$params): void {
@ -129,7 +132,7 @@ class DispatcherTest extends TestCase
public function testBeforeAndAfterWithShortAfterFilterSyntax(): void
{
$this->dispatcher->set('hello', fn (string $name): string => "Hello, $name!");
$this->dispatcher->set('hello', fn(string $name): string => "Hello, $name!");
$this->dispatcher
->hook('hello', Dispatcher::FILTER_BEFORE, function (array &$params): void {
@ -149,7 +152,11 @@ class DispatcherTest extends TestCase
public function testInvalidCallback(): void
{
$this->expectException(Exception::class);
$this->expectExceptionMessage("Class 'NonExistentClass' not found. Is it being correctly autoloaded with Flight::path()?");
$this->expectExceptionMessage(
"Class 'NonExistentClass' not found. "
. "Is it being correctly autoloaded with Flight::path()?"
);
$this->dispatcher->execute(['NonExistentClass', 'nonExistentMethod']);
}
@ -247,6 +254,7 @@ class DispatcherTest extends TestCase
$invalidCallable = 'invalidGlobalFunction';
$validCallable = function (): void {
//
};
$this->dispatcher->filter([$validCallable, $invalidCallable], $params, $output);
@ -275,7 +283,12 @@ class DispatcherTest extends TestCase
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->expectExceptionMessageMatches(
'#Too few arguments to function tests\\\\classes\\\\TesterClass::__construct\(\), 1 passed'
. ' .+ and exactly 6 expected#'
);
$this->dispatcher->execute(TesterClass::class . '->instanceMethod');
}
@ -327,8 +340,12 @@ class DispatcherTest extends TestCase
public function testExecuteStringClassDefaultContainerButForgotInjectingEngine(): void
{
$this->expectException(TypeError::class);
$this->expectExceptionMessageMatches('#tests\\\\classes\\\\ContainerDefault::__construct\(\).+flight\\\\Engine, null given#');
$result = $this->dispatcher->execute([ContainerDefault::class, 'testTheContainer']);
$this->expectExceptionMessageMatches(
'#tests\\\\classes\\\\ContainerDefault::__construct\(\).+flight\\\\Engine, null given#'
);
$this->dispatcher->execute([ContainerDefault::class, 'testTheContainer']);
}
public function testContainerDicePdoWrapperTestBadParams(): void
@ -341,6 +358,6 @@ class DispatcherTest extends TestCase
$this->expectException(Exception::class);
$this->expectExceptionMessage('This is an exception in the constructor');
$this->dispatcher->invokeCallable([ ClassWithExceptionInConstruct::class, '__construct' ]);
$this->dispatcher->invokeCallable([ClassWithExceptionInConstruct::class, '__construct']);
}
}

@ -40,16 +40,16 @@ class EngineTest extends TestCase
};
$this->assertTrue($engine->getInitializedVar());
// we need to setup a dummy route
$engine->route('/someRoute', function () { });
$engine->request()->url = '/someRoute';
// we need to setup a dummy route
$engine->route('/someRoute', function () {});
$engine->request()->url = '/someRoute';
$engine->start();
$this->assertFalse($engine->router()->caseSensitive);
$this->assertTrue($engine->response()->content_length);
}
public function testInitBeforeStartV2OutputBuffering(): void
public function testInitBeforeStartV2OutputBuffering(): void
{
$engine = new class extends Engine {
public function getInitializedVar(): bool
@ -57,12 +57,12 @@ class EngineTest extends TestCase
return $this->initialized;
}
};
$engine->set('flight.v2.output_buffering', true);
$engine->set('flight.v2.output_buffering', true);
$this->assertTrue($engine->getInitializedVar());
$engine->start();
// This is a necessary evil because of how the v2 output buffer works.
ob_end_clean();
// This is a necessary evil because of how the v2 output buffer works.
ob_end_clean();
$this->assertFalse($engine->router()->caseSensitive);
$this->assertTrue($engine->response()->content_length);
@ -96,8 +96,7 @@ class EngineTest extends TestCase
$engine = new Engine();
$this->expectException(Exception::class);
$this->expectExceptionMessage('Cannot override an existing framework method.');
$engine->map('_start', function () {
});
$engine->map('_start', function () {});
}
public function testRegisterExistingMethod(): void
@ -111,7 +110,7 @@ class EngineTest extends TestCase
public function testSetArrayOfValues(): void
{
$engine = new Engine();
$engine->set([ 'key1' => 'value1', 'key2' => 'value2']);
$engine->set(['key1' => 'value1', 'key2' => 'value2']);
$this->assertEquals('value1', $engine->get('key1'));
$this->assertEquals('value2', $engine->get('key2'));
}
@ -153,8 +152,8 @@ class EngineTest extends TestCase
$this->expectOutputString('<h1>404 Not Found</h1><h3>The page you have requested could not be found.</h3>');
$engine->start();
}
public function testStartWithRouteButReturnedValueThrows404V2OutputBuffering(): void
public function testStartWithRouteButReturnedValueThrows404V2OutputBuffering(): void
{
$_SERVER['REQUEST_METHOD'] = 'GET';
$_SERVER['REQUEST_URI'] = '/someRoute';
@ -165,7 +164,7 @@ class EngineTest extends TestCase
return $this->initialized;
}
};
$engine->set('flight.v2.output_buffering', true);
$engine->set('flight.v2.output_buffering', true);
$engine->route('/someRoute', function () {
echo 'i ran';
return true;
@ -185,18 +184,18 @@ class EngineTest extends TestCase
return $this->initialized;
}
};
// First route that returns true (should continue routing)
$engine->route('/someRoute', function () {
echo 'first route ran, ';
return true;
}, true);
// Second route that should be found and executed
$engine->route('/someRoute', function () {
echo 'second route executed!';
}, true);
$this->expectOutputString('first route ran, second route executed!');
$engine->start();
}
@ -211,13 +210,13 @@ class EngineTest extends TestCase
{
return $this->initialized;
}
public function getLoader()
{
return $this->loader;
}
};
// Mock response to prevent actual headers
$engine->getLoader()->register('response', function () {
return new class extends Response {
@ -227,23 +226,23 @@ class EngineTest extends TestCase
}
};
});
// First route that returns true and matches POST
$engine->route('POST /someRoute', function () {
echo 'first POST route ran, ';
return true;
}, true);
// Second route that matches URL but wrong method (GET) - should be captured for 405
$engine->route('GET /someRoute', function () {
echo 'should not execute';
}, true);
// Third route that matches POST and should execute
$engine->route('POST /someRoute', function () {
echo 'second POST route executed!';
}, true);
$this->expectOutputString('first POST route ran, second POST route executed!');
$engine->start();
}
@ -259,46 +258,46 @@ class EngineTest extends TestCase
return $this->initialized;
}
};
// Route that returns true (continues iteration)
$engine->route('/someRoute', function () {
echo 'first route ran, ';
return true;
}, true);
// Route with different URL that won't match
$engine->route('/differentRoute', function () {
echo 'should not execute';
}, true);
// No more matching routes - should reach end of iterator and return 404
$this->expectOutputString('<h1>404 Not Found</h1><h3>The page you have requested could not be found.</h3>');
$engine->start();
}
public function testDoubleStart(): void
public function testDoubleStart(): void
{
$engine = new Engine();
$engine->route('/someRoute', function () {
echo 'i ran';
}, true);
$engine->request()->url = '/someRoute';
$engine->start();
$engine = new Engine();
$engine->route('/someRoute', function () {
echo 'i ran';
}, true);
$engine->request()->url = '/someRoute';
$engine->start();
$request = $engine->request();
$response = $engine->response();
$request = $engine->request();
$response = $engine->response();
// This is pretending like this is embodied in a platform like swoole where
// another request comes in while still holding all the same state.
$_SERVER['REQUEST_METHOD'] = 'GET';
$_SERVER['REQUEST_URI'] = '/someRoute';
$engine->start();
// This is pretending like this is embodied in a platform like swoole where
// another request comes in while still holding all the same state.
$_SERVER['REQUEST_METHOD'] = 'GET';
$_SERVER['REQUEST_URI'] = '/someRoute';
$engine->start();
$this->assertFalse($request === $engine->request());
$this->assertFalse($response === $engine->response());
$this->assertFalse($request === $engine->request());
$this->assertFalse($response === $engine->response());
$this->expectOutputString('i rani ran');
}
$this->expectOutputString('i rani ran');
}
public function testStopWithCode(): void
{
@ -323,7 +322,7 @@ class EngineTest extends TestCase
$this->assertEquals(500, $engine->response()->status());
}
public function testStopWithCodeV2OutputBuffering(): void
public function testStopWithCodeV2OutputBuffering(): void
{
$engine = new class extends Engine {
public function getLoader()
@ -340,13 +339,13 @@ class EngineTest extends TestCase
}
};
});
$engine->set('flight.v2.output_buffering', true);
$engine->route('/testRoute', function () use ($engine) {
echo 'I am a teapot';
$engine->stop(500);
});
$engine->request()->url = '/testRoute';
$engine->start();
$engine->set('flight.v2.output_buffering', true);
$engine->route('/testRoute', function () use ($engine) {
echo 'I am a teapot';
$engine->stop(500);
});
$engine->request()->url = '/testRoute';
$engine->start();
$this->expectOutputString('I am a teapot');
$this->assertEquals(500, $engine->response()->status());
}
@ -409,7 +408,7 @@ class EngineTest extends TestCase
$this->expectOutputString('');
}
public function testOptionsRoute(): void
public function testOptionsRoute(): void
{
$engine = new Engine();
$engine->route('GET /someRoute', function () {
@ -421,7 +420,7 @@ class EngineTest extends TestCase
// No body should be sent
$this->expectOutputString('');
$this->assertEquals('GET, HEAD, OPTIONS', $engine->response()->headers()['Allow']);
$this->assertEquals('GET, HEAD, OPTIONS', $engine->response()->headers()['Allow']);
}
public function testHalt(): void
@ -498,46 +497,46 @@ class EngineTest extends TestCase
$engine->json(['key1' => 'value1', 'key2' => 'value2']);
$this->assertEquals('application/json', $engine->response()->headers()['Content-Type']);
$this->assertEquals(200, $engine->response()->status());
$this->assertEquals('{"key1":"value1","key2":"value2"}', $engine->response()->getBody());
}
public function testJsonWithDuplicateDefaultFlags()
{
$engine = new Engine();
$flags = JSON_HEX_TAG | JSON_HEX_TAG | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE;
// utf8 emoji
$engine->json(['key1' => 'value1', 'key2' => 'value2', 'utf8_emoji' => '😀'], 201, true, '', $flags);
$this->assertEquals('application/json', $engine->response()->headers()['Content-Type']);
$this->assertEquals(201, $engine->response()->status());
$this->assertEquals('{"key1":"value1","key2":"value2","utf8_emoji":"😀"}', $engine->response()->getBody());
}
public function testJsonThrowOnErrorByDefault(): void
{
$engine = new Engine();
$this->expectException(Exception::class);
$this->expectExceptionMessage('Malformed UTF-8 characters, possibly incorrectly encoded');
$engine->json(['key1' => 'value1', 'key2' => 'value2', 'utf8_emoji' => "\xB1\x31"]);
}
public function testJsonV2OutputBuffering(): void
$this->assertEquals('{"key1":"value1","key2":"value2"}', $engine->response()->getBody());
}
public function testJsonWithDuplicateDefaultFlags()
{
$engine = new Engine();
$engine->response()->v2_output_buffering = true;
$flags = JSON_HEX_TAG | JSON_HEX_TAG | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE;
// utf8 emoji
$engine->json(['key1' => 'value1', 'key2' => 'value2', 'utf8_emoji' => '😀'], 201, true, '', $flags);
$this->assertEquals('application/json', $engine->response()->headers()['Content-Type']);
$this->assertEquals(201, $engine->response()->status());
$this->assertEquals('{"key1":"value1","key2":"value2","utf8_emoji":"😀"}', $engine->response()->getBody());
}
public function testJsonThrowOnErrorByDefault(): void
{
$engine = new Engine();
$this->expectException(Exception::class);
$this->expectExceptionMessage('Malformed UTF-8 characters, possibly incorrectly encoded');
$engine->json(['key1' => 'value1', 'key2' => 'value2', 'utf8_emoji' => "\xB1\x31"]);
}
public function testJsonV2OutputBuffering(): void
{
$engine = new Engine();
$engine->response()->v2_output_buffering = true;
$engine->json(['key1' => 'value1', 'key2' => 'value2']);
$this->expectOutputString('{"key1":"value1","key2":"value2"}');
$this->assertEquals('application/json', $engine->response()->headers()['Content-Type']);
$this->assertEquals(200, $engine->response()->status());
}
public function testJsonHalt(): void
public function testJsonHalt(): void
{
$engine = new Engine();
$this->expectOutputString('{"key1":"value1","key2":"value2"}');
$this->expectOutputString('{"key1":"value1","key2":"value2"}');
$engine->jsonHalt(['key1' => 'value1', 'key2' => 'value2']);
$this->assertEquals('application/json', $engine->response()->headers()['Content-Type']);
$this->assertEquals(200, $engine->response()->status());
$this->assertEquals('{"key1":"value1","key2":"value2"}', $engine->response()->getBody());
$this->assertEquals('{"key1":"value1","key2":"value2"}', $engine->response()->getBody());
}
public function testJsonP(): void
@ -547,13 +546,13 @@ class EngineTest extends TestCase
$engine->jsonp(['key1' => 'value1', 'key2' => 'value2']);
$this->assertEquals('application/javascript; charset=utf-8', $engine->response()->headers()['Content-Type']);
$this->assertEquals(200, $engine->response()->status());
$this->assertEquals('whatever({"key1":"value1","key2":"value2"});', $engine->response()->getBody());
$this->assertEquals('whatever({"key1":"value1","key2":"value2"});', $engine->response()->getBody());
}
public function testJsonPV2OutputBuffering(): void
public function testJsonPV2OutputBuffering(): void
{
$engine = new Engine();
$engine->response()->v2_output_buffering = true;
$engine->response()->v2_output_buffering = true;
$engine->request()->query['jsonp'] = 'whatever';
$engine->jsonp(['key1' => 'value1', 'key2' => 'value2']);
$this->expectOutputString('whatever({"key1":"value1","key2":"value2"});');
@ -570,10 +569,10 @@ class EngineTest extends TestCase
$this->assertEquals(200, $engine->response()->status());
}
public function testJsonpBadParamV2OutputBuffering(): void
public function testJsonpBadParamV2OutputBuffering(): void
{
$engine = new Engine();
$engine->response()->v2_output_buffering = true;
$engine->response()->v2_output_buffering = true;
$engine->jsonp(['key1' => 'value1', 'key2' => 'value2']);
$this->expectOutputString('({"key1":"value1","key2":"value2"});');
$this->assertEquals('application/javascript; charset=utf-8', $engine->response()->headers()['Content-Type']);
@ -608,7 +607,7 @@ class EngineTest extends TestCase
$engine = new Engine;
$_SERVER['HTTP_IF_MODIFIED_SINCE'] = 'Fri, 13 Feb 2009 23:31:30 GMT';
$engine->lastModified(1234567890);
$this->assertTrue(empty($engine->response()->headers()['Last-Modified']));
$this->assertTrue(empty($engine->response()->headers()['Last-Modified']));
$this->assertEquals(304, $engine->response()->status());
}
@ -618,7 +617,7 @@ class EngineTest extends TestCase
$engine->route('/path1/@param:[0-9]{3}', function () {
echo 'I win';
}, false, 'path1');
$url = $engine->getUrl('path1', [ 'param' => 123 ]);
$url = $engine->getUrl('path1', ['param' => 123]);
$this->assertEquals('/path1/123', $url);
}
@ -628,7 +627,7 @@ class EngineTest extends TestCase
$engine->route('/item/@item_param:[a-z0-9]{16}/by-status/@token:[a-z0-9]{16}', function () {
echo 'I win';
}, false, 'path_item_1');
$url = $engine->getUrl('path_item_1', [ 'item_param' => 1234567890123456, 'token' => 6543210987654321 ]);
$url = $engine->getUrl('path_item_1', ['item_param' => 1234567890123456, 'token' => 6543210987654321]);
$this->assertEquals('/item/1234567890123456/by-status/6543210987654321', $url);
}
@ -666,8 +665,7 @@ class EngineTest extends TestCase
public function testMiddlewareCallableFunctionReturnFalse(): void
{
$engine = new class extends Engine {
};
$engine = new class extends Engine {};
$engine->route('/path1/@id', function ($id) {
echo 'OK' . $id;
})
@ -742,7 +740,7 @@ class EngineTest extends TestCase
$this->expectOutputString('OK123after123');
}
public function testMiddlewareClassStringNoContainer(): void
public function testMiddlewareClassStringNoContainer(): void
{
$middleware = new class {
public function after($params)
@ -761,10 +759,10 @@ class EngineTest extends TestCase
$this->expectOutputString('OK123after123');
}
public function testMiddlewareClassStringWithContainer(): void
public function testMiddlewareClassStringWithContainer(): void
{
$engine = new Engine();
$engine = new Engine();
$dice = new \Dice\Dice();
$dice = $dice->addRule('*', [
'substitutions' => [
@ -774,7 +772,7 @@ class EngineTest extends TestCase
$engine->registerContainerHandler(function ($class, $params) use ($dice) {
return $dice->create($class, $params);
});
$engine->route('/path1/@id', function ($id) {
echo 'OK' . $id;
@ -794,8 +792,7 @@ class EngineTest extends TestCase
return false;
}
};
$engine = new class extends Engine {
};
$engine = new class extends Engine {};
$engine->route('/path1/@id', function ($id) {
echo 'OK' . $id;
@ -850,7 +847,7 @@ class EngineTest extends TestCase
$engine = new Engine();
$engine->route('/path1/@id/subpath1/@another_id', function () {
echo 'OK';
})->addMiddleware([ $middleware, $middleware2 ]);
})->addMiddleware([$middleware, $middleware2]);
$engine->request()->url = '/path1/123/subpath1/456';
$engine->start();
@ -887,14 +884,15 @@ class EngineTest extends TestCase
$router->map('/@cool_id', function () {
echo 'OK';
});
}, [ $middleware, $middleware2 ]);
}, [$middleware, $middleware2]);
$engine->request()->url = '/path1/123/subpath1/456';
$engine->start();
$this->expectOutputString('before456before123OKafter123456after123');
}
public function testContainerBadClass() {
public function testContainerBadClass()
{
$engine = new Engine();
$this->expectException(InvalidArgumentException::class);
@ -902,47 +900,50 @@ class EngineTest extends TestCase
$engine->registerContainerHandler('BadClass');
}
public function testContainerDice() {
public function testContainerDice()
{
$engine = new Engine();
$dice = new \Dice\Dice();
$dice = $dice->addRules([
PdoWrapper::class => [
'shared' => true,
'constructParams' => [ 'sqlite::memory:' ]
'constructParams' => ['sqlite::memory:']
]
]);
$engine->registerContainerHandler(function ($class, $params) use ($dice) {
return $dice->create($class, $params);
});
$engine->route('/container', Container::class.'->testTheContainer');
$engine->route('/container', Container::class . '->testTheContainer');
$engine->request()->url = '/container';
$engine->start();
$this->expectOutputString('yay! I injected a collection, and it has 1 items');
}
public function testContainerDicePdoWrapperTest() {
public function testContainerDicePdoWrapperTest()
{
$engine = new Engine();
$dice = new \Dice\Dice();
$dice = $dice->addRules([
PdoWrapper::class => [
'shared' => true,
'constructParams' => [ 'sqlite::memory:' ]
'constructParams' => ['sqlite::memory:']
]
]);
$engine->registerContainerHandler(function ($class, $params) use ($dice) {
return $dice->create($class, $params);
});
$engine->route('/container', Container::class.'->testThePdoWrapper');
$engine->route('/container', Container::class . '->testThePdoWrapper');
$engine->request()->url = '/container';
$engine->start();
$this->expectOutputString('Yay! I injected a PdoWrapper, and it returned the number 5 from the database!');
}
public function testContainerDiceFlightEngine() {
public function testContainerDiceFlightEngine()
{
$engine = new Engine();
$engine->set('test_me_out', 'You got it boss!');
$dice = new \Dice\Dice();
@ -954,44 +955,46 @@ class EngineTest extends TestCase
$engine->registerContainerHandler(function ($class, $params) use ($dice) {
return $dice->create($class, $params);
});
$engine->route('/container', ContainerDefault::class.'->echoTheContainer');
$engine->route('/container', ContainerDefault::class . '->echoTheContainer');
$engine->request()->url = '/container';
$engine->start();
$this->expectOutputString('You got it boss!');
}
public function testContainerDiceBadClass() {
public function testContainerDiceBadClass()
{
$engine = new Engine();
$dice = new \Dice\Dice();
$engine->registerContainerHandler(function ($class, $params) use ($dice) {
return $dice->create($class, $params);
});
$engine->route('/container', 'BadClass->testTheContainer');
$engine->request()->url = '/container';
$this->expectException(Exception::class);
$this->expectExceptionMessage("Class 'BadClass' not found. Is it being correctly autoloaded with Flight::path()?");
$engine->start();
}
public function testContainerDiceBadMethod() {
public function testContainerDiceBadMethod()
{
$engine = new Engine();
$dice = new \Dice\Dice();
$dice = $dice->addRules([
PdoWrapper::class => [
'shared' => true,
'constructParams' => [ 'sqlite::memory:' ]
'constructParams' => ['sqlite::memory:']
]
]);
$engine->registerContainerHandler(function ($class, $params) use ($dice) {
return $dice->create($class, $params);
});
$engine->route('/container', Container::class.'->badMethod');
$engine->route('/container', Container::class . '->badMethod');
$engine->request()->url = '/container';
$this->expectException(Exception::class);
@ -1000,46 +1003,49 @@ class EngineTest extends TestCase
$engine->start();
}
public function testContainerPsr11(): void {
public function testContainerPsr11(): void
{
$engine = new Engine();
$container = new \League\Container\Container();
$container->add(Container::class)->addArgument(Collection::class)->addArgument(PdoWrapper::class);
$container->add(Collection::class);
$container->add(PdoWrapper::class)->addArgument('sqlite::memory:');
$engine->registerContainerHandler($container);
$engine->route('/container', Container::class.'->testTheContainer');
$engine->route('/container', Container::class . '->testTheContainer');
$engine->request()->url = '/container';
$engine->start();
$this->expectOutputString('yay! I injected a collection, and it has 1 items');
}
public function testContainerPsr11ClassNotFound() {
public function testContainerPsr11ClassNotFound()
{
$engine = new Engine();
$container = new \League\Container\Container();
$container->add(Container::class)->addArgument(Collection::class);
$container->add(Collection::class);
$engine->registerContainerHandler($container);
$engine->route('/container', 'BadClass->testTheContainer');
$engine->request()->url = '/container';
$this->expectException(Exception::class);
$this->expectExceptionMessage("Class 'BadClass' not found. Is it being correctly autoloaded with Flight::path()?");
$engine->start();
}
public function testContainerPsr11MethodNotFound(): void {
public function testContainerPsr11MethodNotFound(): void
{
$engine = new Engine();
$container = new \League\Container\Container();
$container->add(Container::class)->addArgument(Collection::class)->addArgument(PdoWrapper::class);
$container->add(Collection::class);
$container->add(PdoWrapper::class)->addArgument('sqlite::memory:');
$engine->registerContainerHandler($container);
$engine->route('/container', Container::class.'->badMethod');
$engine->route('/container', Container::class . '->badMethod');
$engine->request()->url = '/container';
$this->expectException(Exception::class);
@ -1048,7 +1054,8 @@ class EngineTest extends TestCase
$engine->start();
}
public function testRouteFoundButBadMethod(): void {
public function testRouteFoundButBadMethod(): void
{
$engine = new class extends Engine {
public function getLoader()
{
@ -1068,30 +1075,30 @@ class EngineTest extends TestCase
};
});
$engine->route('POST /path1/@id', function ($id) {
echo 'OK' . $id;
});
$engine->route('POST /path1/@id', function ($id) {
echo 'OK' . $id;
});
$engine->route('GET /path2/@id', function ($id) {
echo 'OK' . $id;
});
$engine->route('GET /path2/@id', function ($id) {
echo 'OK' . $id;
});
$engine->route('PATCH /path3/@id', function ($id) {
echo 'OK' . $id;
});
$engine->route('PATCH /path3/@id', function ($id) {
echo 'OK' . $id;
});
$engine->request()->url = '/path1/123';
$engine->request()->method = 'GET';
$engine->request()->url = '/path1/123';
$engine->request()->method = 'GET';
$engine->start();
$this->expectOutputString('Method Not Allowed. Allowed Methods are: POST, OPTIONS');
$this->expectOutputString('Method Not Allowed. Allowed Methods are: POST, OPTIONS');
$this->assertEquals(405, $engine->response()->status());
$this->assertEquals('Method Not Allowed. Allowed Methods are: POST, OPTIONS', $engine->response()->getBody());
$this->assertEquals('POST, OPTIONS', $engine->response()->headers()['Allow']);
}
$this->assertEquals('Method Not Allowed. Allowed Methods are: POST, OPTIONS', $engine->response()->getBody());
$this->assertEquals('POST, OPTIONS', $engine->response()->headers()['Allow']);
}
public function testDownload(): void
public function testDownload(): void
{
$engine = new class extends Engine {
public function getLoader()
@ -1102,26 +1109,26 @@ class EngineTest extends TestCase
// doing this so we can overwrite some parts of the response
$engine->getLoader()->register('response', function () {
return new class extends Response {
public $headersSent = [];
public $headersSent = [];
public function setRealHeader(
string $header_string,
bool $replace = true,
int $response_code = 0
): self {
$this->headersSent[] = $header_string;
$this->headersSent[] = $header_string;
return $this;
}
};
});
$tmpfile = tmpfile();
fwrite($tmpfile, 'I am a teapot');
$streamPath = stream_get_meta_data($tmpfile)['uri'];
$this->expectOutputString('I am a teapot');
$tmpfile = tmpfile();
fwrite($tmpfile, 'I am a teapot');
$streamPath = stream_get_meta_data($tmpfile)['uri'];
$this->expectOutputString('I am a teapot');
$engine->download($streamPath);
$this->assertContains('Content-Disposition: attachment; filename="'.basename($streamPath).'"', $engine->response()->headersSent);
$this->assertContains('Content-Disposition: attachment; filename="' . basename($streamPath) . '"', $engine->response()->headersSent);
}
public function testDownloadWithDefaultFileName(): void
public function testDownloadWithDefaultFileName(): void
{
$engine = new class extends Engine {
public function getLoader()
@ -1132,30 +1139,30 @@ class EngineTest extends TestCase
// doing this so we can overwrite some parts of the response
$engine->getLoader()->register('response', function () {
return new class extends Response {
public $headersSent = [];
public $headersSent = [];
public function setRealHeader(
string $header_string,
bool $replace = true,
int $response_code = 0
): self {
$this->headersSent[] = $header_string;
$this->headersSent[] = $header_string;
return $this;
}
};
});
$tmpfile = tmpfile();
fwrite($tmpfile, 'I am a teapot');
$streamPath = stream_get_meta_data($tmpfile)['uri'];
$this->expectOutputString('I am a teapot');
$tmpfile = tmpfile();
fwrite($tmpfile, 'I am a teapot');
$streamPath = stream_get_meta_data($tmpfile)['uri'];
$this->expectOutputString('I am a teapot');
$engine->download($streamPath, 'something.txt');
$this->assertContains('Content-Disposition: attachment; filename="something.txt"', $engine->response()->headersSent);
$this->assertContains('Content-Disposition: attachment; filename="something.txt"', $engine->response()->headersSent);
}
public function testDownloadBadPath() {
$engine = new Engine();
$this->expectException(Exception::class);
$this->expectExceptionMessage("/path/to/nowhere cannot be found.");
$engine->download('/path/to/nowhere');
}
public function testDownloadBadPath()
{
$engine = new Engine();
$this->expectException(Exception::class);
$this->expectExceptionMessage("/path/to/nowhere cannot be found.");
$engine->download('/path/to/nowhere');
}
}

@ -123,6 +123,7 @@ class EventSystemTest extends TestCase
$called = true;
});
Flight::onEvent('test.event', function () {
//
});
$this->assertTrue($called, 'Overridden onEvent method should be called.');
}
@ -231,12 +232,19 @@ class EventSystemTest extends TestCase
*/
public function testHasListeners(): void
{
$this->assertFalse(Flight::eventDispatcher()->hasListeners('test.event'), 'Event should not have listeners before registration');
$this->assertFalse(
Flight::eventDispatcher()->hasListeners('test.event'),
'Event should not have listeners before registration'
);
Flight::onEvent('test.event', function () {
//
});
$this->assertTrue(Flight::eventDispatcher()->hasListeners('test.event'), 'Event should have listeners after registration');
$this->assertTrue(
Flight::eventDispatcher()->hasListeners('test.event'),
'Event should have listeners after registration'
);
}
/**
@ -245,11 +253,16 @@ class EventSystemTest extends TestCase
public function testGetListeners(): void
{
$callback1 = function () {
//
};
$callback2 = function () {
//
};
$this->assertEmpty(Flight::eventDispatcher()->getListeners('test.event'), 'Event should have no listeners before registration');
$this->assertEmpty(
Flight::eventDispatcher()->getListeners('test.event'),
'Event should have no listeners before registration'
);
Flight::onEvent('test.event', $callback1);
Flight::onEvent('test.event', $callback2);
@ -275,11 +288,16 @@ class EventSystemTest extends TestCase
*/
public function testGetAllRegisteredEvents(): void
{
$this->assertEmpty(Flight::eventDispatcher()->getAllRegisteredEvents(), 'No events should be registered initially');
$this->assertEmpty(
Flight::eventDispatcher()->getAllRegisteredEvents(),
'No events should be registered initially'
);
Flight::onEvent('test.event1', function () {
//
});
Flight::onEvent('test.event2', function () {
//
});
$events = Flight::eventDispatcher()->getAllRegisteredEvents();
@ -303,7 +321,11 @@ class EventSystemTest extends TestCase
Flight::onEvent('test.event', $callback1);
Flight::onEvent('test.event', $callback2);
$this->assertCount(2, Flight::eventDispatcher()->getListeners('test.event'), 'Event should have two listeners initially');
$this->assertCount(
2,
Flight::eventDispatcher()->getListeners('test.event'),
'Event should have two listeners initially'
);
Flight::eventDispatcher()->removeListener('test.event', $callback1);
@ -318,19 +340,36 @@ class EventSystemTest extends TestCase
public function testRemoveAllListeners(): void
{
Flight::onEvent('test.event', function () {
//
});
Flight::onEvent('test.event', function () {
//
});
Flight::onEvent('another.event', function () {
//
});
$this->assertTrue(Flight::eventDispatcher()->hasListeners('test.event'), 'Event should have listeners before removal');
$this->assertTrue(Flight::eventDispatcher()->hasListeners('another.event'), 'Another event should have listeners');
$this->assertTrue(
Flight::eventDispatcher()->hasListeners('test.event'),
'Event should have listeners before removal'
);
$this->assertTrue(
Flight::eventDispatcher()->hasListeners('another.event'),
'Another event should have listeners'
);
Flight::eventDispatcher()->removeAllListeners('test.event');
$this->assertFalse(Flight::eventDispatcher()->hasListeners('test.event'), 'Event should have no listeners after removal');
$this->assertTrue(Flight::eventDispatcher()->hasListeners('another.event'), 'Another event should still have listeners');
$this->assertFalse(
Flight::eventDispatcher()->hasListeners('test.event'),
'Event should have no listeners after removal'
);
$this->assertTrue(
Flight::eventDispatcher()->hasListeners('another.event'),
'Another event should still have listeners'
);
}
/**
@ -340,9 +379,13 @@ class EventSystemTest extends TestCase
{
// Should not throw any errors
Flight::eventDispatcher()->removeListener('nonexistent.event', function () {
//
});
Flight::eventDispatcher()->removeAllListeners('nonexistent.event');
$this->assertTrue(true, 'Removing listeners for nonexistent events should not throw errors');
$this->assertTrue(
true,
'Removing listeners for nonexistent events should not throw errors'
);
}
}

@ -121,7 +121,7 @@ class LoaderTest extends TestCase
{
$this->loader->register('g', User::class);
$current_class = $this->loader->get('g');
$this->assertEquals([ User::class, [], null ], $current_class);
$this->assertEquals([User::class, [], null], $current_class);
$this->loader->unregister('g');
$unregistered_class_result = $this->loader->get('g');
$this->assertNull($unregistered_class_result);
@ -129,7 +129,7 @@ class LoaderTest extends TestCase
public function testNewInstance6Params(): void
{
$TesterClass = $this->loader->newInstance(TesterClass::class, ['Bob','Fred', 'Joe', 'Jane', 'Sally', 'Suzie']);
$TesterClass = $this->loader->newInstance(TesterClass::class, ['Bob', 'Fred', 'Joe', 'Jane', 'Sally', 'Suzie']);
$this->assertEquals('Bob', $TesterClass->param1);
$this->assertEquals('Fred', $TesterClass->param2);
$this->assertEquals('Joe', $TesterClass->param3);

@ -99,7 +99,7 @@ class PdoWrapperTest extends TestCase
public function testFetchAllWithNamedParams(): void
{
$rows = $this->pdo_wrapper->fetchAll('SELECT * FROM test WHERE name = :name', [ 'name' => 'two']);
$rows = $this->pdo_wrapper->fetchAll('SELECT * FROM test WHERE name = :name', ['name' => 'two']);
$this->assertCount(1, $rows);
$this->assertEquals(2, $rows[0]['id']);
$this->assertEquals('two', $rows[0]['name']);
@ -107,19 +107,19 @@ class PdoWrapperTest extends TestCase
public function testFetchAllWithInInt(): void
{
$rows = $this->pdo_wrapper->fetchAll('SELECT id FROM test WHERE id IN(? )', [ [1,2 ]]);
$rows = $this->pdo_wrapper->fetchAll('SELECT id FROM test WHERE id IN(? )', [[1, 2]]);
$this->assertEquals(2, count($rows));
}
public function testFetchAllWithInString(): void
{
$rows = $this->pdo_wrapper->fetchAll('SELECT id FROM test WHERE name IN(?)', [ ['one','two' ]]);
$rows = $this->pdo_wrapper->fetchAll('SELECT id FROM test WHERE name IN(?)', [['one', 'two']]);
$this->assertEquals(2, count($rows));
}
public function testFetchAllWithInStringCommas(): void
{
$rows = $this->pdo_wrapper->fetchAll('SELECT id FROM test WHERE id > ? AND name IN( ?) ', [ 0, 'one,two' ]);
$rows = $this->pdo_wrapper->fetchAll('SELECT id FROM test WHERE id > ? AND name IN( ?) ', [0, 'one,two']);
$this->assertEquals(2, count($rows));
}
@ -178,7 +178,15 @@ class PdoWrapperTest extends TestCase
$queriesData = null;
$dispatcher = EventDispatcher::getInstance();
$dispatcher->on('flight.db.queries', function ($conn, $queries) use (&$eventTriggered, &$connectionData, &$queriesData) {
$dispatcher->on('flight.db.queries', function (
$conn,
$queries
) use (
&$eventTriggered,
&$connectionData,
&$queriesData
) {
$eventTriggered = true;
$connectionData = $conn;
$queriesData = $queries;

@ -31,6 +31,6 @@ class RenderTest extends TestCase
$this->app->render('hello', ['name' => 'Bob'], 'content');
$this->app->render('layouts/layout');
$this->expectOutputString('<html>Hello, Bob!</html>');
$this->expectOutputString("<body>Hello, Bob!</body>\n");
}
}

@ -266,13 +266,13 @@ class RequestBodyParserTest extends TestCase
// Use PHP CLI with -d to set upload_max_filesize (ini_set can't change this setting in many SAPIs)
$cases = [
// No unit yields default branch which returns 0 in current implementation
['1' , 0], // no unit and number too small
['1K' , 1024],
['2M' , 2 * 1024 * 1024],
['1G' , 1024 * 1024 * 1024],
['1T' , 1024 * 1024 * 1024 * 1024],
['1Z' , 0 ], // Unknown unit and number too small
[ '1024', 1024 ]
['1', 0], // no unit and number too small
['1K', 1024],
['2M', 2 * 1024 * 1024],
['1G', 1024 * 1024 * 1024],
['1T', 1024 * 1024 * 1024 * 1024],
['1Z', 0], // Unknown unit and number too small
['1024', 1024]
];
foreach ($cases as [$iniVal, $expected]) {
@ -321,7 +321,9 @@ class RequestBodyParserTest extends TestCase
$parts[] = "Content-Type: text/plain\r\n\r\nignoredvalue";
// C: header too long (>16384) => skipped
$longHeader = 'Content-Disposition: form-data; name="toolong"; filename="toolong.txt"; ' . str_repeat('x', 16500);
$longHeader = 'Content-Disposition: form-data; name="toolong"; filename="toolong.txt"; '
. str_repeat('x', 16500);
$parts[] = $longHeader . "\r\n\r\nlongvalue";
// D: header line without colon gets skipped but rest processed; becomes non-file field
@ -334,14 +336,24 @@ class RequestBodyParserTest extends TestCase
$parts[] = "Content-Disposition: form-data; name=\"\"; filename=\"empty.txt\"\r\n\r\nemptyNameValue";
// G: invalid filename triggers sanitized fallback
$parts[] = "Content-Disposition: form-data; name=\"filebad\"; filename=\"a*b?.txt\"\r\nContent-Type: text/plain\r\n\r\nFILEBAD";
$parts[] = "Content-Disposition: form-data; "
. "name=\"filebad\"; "
. "filename=\"a*b?.txt\"\r\nContent-Type: text/plain\r\n\r\nFILEBAD";
// H1 & H2: two files same key for aggregation logic (arrays)
$parts[] = "Content-Disposition: form-data; name=\"filemulti\"; filename=\"one.txt\"\r\nContent-Type: text/plain\r\n\r\nONE";
$parts[] = "Content-Disposition: form-data; name=\"filemulti\"; filename=\"two.txt\"\r\nContent-Type: text/plain\r\n\r\nTWO";
$parts[] = "Content-Disposition: form-data; "
. "name=\"filemulti\"; "
. "filename=\"one.txt\"\r\nContent-Type: text/plain\r\n\r\nONE";
$parts[] = "Content-Disposition: form-data; "
. "name=\"filemulti\"; "
. "filename=\"two.txt\"\r\nContent-Type: text/plain\r\n\r\nTWO";
// I: file exceeding total bytes triggers UPLOAD_ERR_INI_SIZE
$parts[] = "Content-Disposition: form-data; name=\"filebig\"; filename=\"big.txt\"\r\nContent-Type: text/plain\r\n\r\n" . str_repeat('A', 10);
$parts[] = "Content-Disposition: form-data; "
. "name=\"filebig\"; "
. "filename=\"big.txt\"\r\nContent-Type: text/plain\r\n\r\n"
. str_repeat('A', 10);
// Build full body
$body = '';
@ -394,11 +406,31 @@ class RequestBodyParserTest extends TestCase
public function testMultipartEmptyArrayNameStripped(): void
{
// Covers line where keyName becomes empty after removing [] (name="[]") and header param extraction (preg_match_all)
// Covers line where keyName becomes empty after removing [] (name="[]")
// and header param extraction (preg_match_all)
$boundary = 'BOUNDARYEMPTY';
$validFilePart = "Content-Disposition: form-data; name=\"fileok\"; filename=\"ok.txt\"\r\nContent-Type: text/plain\r\n\r\nOK";
$emptyNameFilePart = "Content-Disposition: form-data; name=\"[]\"; filename=\"empty.txt\"\r\nContent-Type: text/plain\r\n\r\nSHOULD_SKIP";
$body = '--' . $boundary . "\r\n" . $validFilePart . "\r\n" . '--' . $boundary . "\r\n" . $emptyNameFilePart . "\r\n" . '--' . $boundary . "--\r\n";
$validFilePart = "Content-Disposition: form-data; "
. "name=\"fileok\"; "
. "filename=\"ok.txt\"\r\nContent-Type: text/plain\r\n\r\nOK";
$emptyNameFilePart = "Content-Disposition: form-data; "
. "name=\"[]\"; "
. "filename=\"empty.txt\"\r\nContent-Type: text/plain\r\n\r\nSHOULD_SKIP";
$body = '--'
. $boundary
. "\r\n"
. $validFilePart
. "\r\n"
. '--'
. $boundary
. "\r\n"
. $emptyNameFilePart
. "\r\n"
. '--'
. $boundary
. "--\r\n";
$tmp = tmpfile();
$path = stream_get_meta_data($tmp)['uri'];

@ -197,7 +197,7 @@ class RequestTest extends TestCase
'stream_path' => $stream_path,
'method' => 'POST'
]);
$this->assertEquals([ 'foo' => 'bar' ], $request->data->getData());
$this->assertEquals(['foo' => 'bar'], $request->data->getData());
$this->assertEquals('{"foo":"bar"}', $request->getBody());
}
@ -430,7 +430,12 @@ class RequestTest extends TestCase
// Find best match first
$_SERVER['HTTP_ACCEPT'] = 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8';
$request = new Request();
$this->assertEquals('application/xml', $request->negotiateContentType(['application/xml', 'application/json', 'text/html']));
$this->assertEquals('application/xml', $request->negotiateContentType([
'application/xml',
'application/json',
'text/html'
]));
// Find the first match
$_SERVER['HTTP_ACCEPT'] = 'application/json,text/html';

@ -412,6 +412,7 @@ class RouterTest extends TestCase
public function testRouteBeingReturned(): void
{
$route = $this->router->map('/hi', function () {
//
});
$route_in_router = $this->router->getRoutes()[0];
$this->assertSame($route, $route_in_router);
@ -420,6 +421,7 @@ class RouterTest extends TestCase
public function testRouteSetAlias(): void
{
$route = $this->router->map('/hi', function () {
//
});
$route->setAlias('hello');
$this->assertEquals('hello', $route->alias);
@ -773,7 +775,7 @@ class RouterTest extends TestCase
public function testStripMultipleSlashesFromUrlAndStillMatch(): void
{
$this->router->get('/', [ $this, 'ok' ]);
$this->router->get('/', [$this, 'ok']);
$this->request->url = '///';
$this->request->method = 'GET';
$this->check('OK');

@ -88,7 +88,13 @@ class SimplePdoTest extends TestCase
public function testRunQueryWithoutParamsWithMaxQueryMetrics(): void
{
$db = new class ('sqlite::memory:', null, null, null, ['maxQueryMetrics' => 2, 'trackApmQueries' => true]) extends SimplePdo {
$db = new class(
'sqlite::memory:',
null,
null,
null,
['maxQueryMetrics' => 2, 'trackApmQueries' => true]
) extends SimplePdo {
public function getQueryMetrics(): array
{
return $this->queryMetrics;

@ -41,14 +41,17 @@ class UploadedFileTest extends TestCase
public function getFileErrorMessageTests(): array
{
return [
[ UPLOAD_ERR_INI_SIZE, 'The uploaded file exceeds the upload_max_filesize directive in php.ini.', ],
[ UPLOAD_ERR_FORM_SIZE, 'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.', ],
[ UPLOAD_ERR_PARTIAL, 'The uploaded file was only partially uploaded.', ],
[ UPLOAD_ERR_NO_FILE, 'No file was uploaded.', ],
[ UPLOAD_ERR_NO_TMP_DIR, 'Missing a temporary folder.', ],
[ UPLOAD_ERR_CANT_WRITE, 'Failed to write file to disk.', ],
[ UPLOAD_ERR_EXTENSION, 'A PHP extension stopped the file upload.', ],
[ -1, 'An unknown error occurred. Error code: -1' ]
[UPLOAD_ERR_INI_SIZE, 'The uploaded file exceeds the upload_max_filesize directive in php.ini.',],
[
UPLOAD_ERR_FORM_SIZE,
'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.',
],
[UPLOAD_ERR_PARTIAL, 'The uploaded file was only partially uploaded.',],
[UPLOAD_ERR_NO_FILE, 'No file was uploaded.',],
[UPLOAD_ERR_NO_TMP_DIR, 'Missing a temporary folder.',],
[UPLOAD_ERR_CANT_WRITE, 'Failed to write file to disk.',],
[UPLOAD_ERR_EXTENSION, 'A PHP extension stopped the file upload.',],
[-1, 'An unknown error occurred. Error code: -1']
];
}

@ -104,7 +104,7 @@ class ViewTest extends TestCase
$this->view->render('world');
$this->expectOutputString('Hello world, Bob!');
$this->expectOutputString("Hello world, Bob!\n");
}
public function testGetTemplateAbsolutePath(): void

@ -33,6 +33,11 @@ class ContainerDefault
public function testUi(): void
{
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>';
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>';
}
}

@ -9,6 +9,7 @@ class Factory
// Cannot be instantiated
private function __construct()
{
//
}
public static function create(): self

@ -21,7 +21,13 @@ class AiGenerateInstructionsCommandTest extends TestCase
self::$ou = __DIR__ . DIRECTORY_SEPARATOR . 'output.test' . uniqid('', true) . '.txt';
file_put_contents(self::$in, '');
file_put_contents(self::$ou, '');
$this->baseDir = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'flightphp-test-basedir-' . uniqid('', true) . DIRECTORY_SEPARATOR;
$this->baseDir = sys_get_temp_dir()
. DIRECTORY_SEPARATOR
. 'flightphp-test-basedir-'
. uniqid('', true)
. DIRECTORY_SEPARATOR;
if (!is_dir($this->baseDir)) {
mkdir($this->baseDir, 0777, true);
}

@ -95,7 +95,10 @@ PHP;
$app->add(new RouteCommand(['runway' => ['something' => 'else']]));
@$app->handle(['runway', 'routes']);
$this->assertStringContainsString('index_root not set in app/config/config.php', file_get_contents(static::$ou));
$this->assertStringContainsString(
'index_root not set in app/config/config.php',
file_get_contents(static::$ou)
);
}
public function testGetRoutes(): void

@ -2,14 +2,14 @@
declare(strict_types=1);
namespace tests\groupcompactsyntax;
use Flight;
use PHPUnit\Framework\TestCase;
use tests\groupcompactsyntax\PostsController;
use tests\groupcompactsyntax\TodosController;
use tests\groupcompactsyntax\UsersController;
require_once __DIR__ . '/UsersController.php';
require_once __DIR__ . '/PostsController.php';
final class FlightRouteCompactSyntaxTest extends TestCase
{
public function setUp(): void
@ -38,7 +38,7 @@ final class FlightRouteCompactSyntaxTest extends TestCase
public function testOptionsOnly(): void
{
Flight::resource('/users', UsersController::class, [
'only' => [ 'index', 'destroy' ]
'only' => ['index', 'destroy']
]);
$routes = Flight::router()->getRoutes();
@ -100,7 +100,7 @@ final class FlightRouteCompactSyntaxTest extends TestCase
public function testOptionsExcept(): void
{
Flight::resource('/todos', TodosController::class, [
'except' => [ 'create', 'store', 'update', 'destroy', 'edit' ]
'except' => ['create', 'store', 'update', 'destroy', 'edit']
]);
$routes = Flight::router()->getRoutes();
@ -119,7 +119,7 @@ final class FlightRouteCompactSyntaxTest extends TestCase
public function testOptionsMiddlewareAndAliasBase(): void
{
Flight::resource('/todos', TodosController::class, [
'middleware' => [ 'auth' ],
'middleware' => ['auth'],
'alias_base' => 'nothanks'
]);

@ -8,29 +8,36 @@ final class PostsController
{
public function index(): void
{
//
}
public function show(string $id): void
{
//
}
public function create(): void
{
//
}
public function store(): void
{
//
}
public function edit(string $id): void
{
//
}
public function update(string $id): void
{
//
}
public function destroy(string $id): void
{
//
}
}

@ -8,9 +8,11 @@ final class TodosController
{
public function index(): void
{
//
}
public function show(string $id): void
{
//
}
}

@ -2,6 +2,7 @@
declare(strict_types=1);
// phpcs:ignore PSR1.Classes.ClassDeclaration.MissingNamespace
class ExampleClass
{
//

@ -75,7 +75,7 @@ final class FlightTest extends TestCase
{
$dateTime = new DateTimeImmutable();
$controller = new class ($dateTime) {
$controller = new class($dateTime) {
public function __construct(private DateTimeImmutable $dateTime)
{
//

@ -69,4 +69,4 @@ echo "----------------------------------------"
echo "Getting post-test memory reading..."
final_memory_response=$(curl -s "${URL}?memory=1")
final_memory=$(echo "$final_memory_response" | grep "Memory:" | awk '{print $2}')
echo "Final memory usage: ${final_memory} KB"
echo "Final memory usage: ${final_memory} KB"

@ -23,4 +23,4 @@ for ((i = 0; i < count; i++)); do
echo " ${php_versions[$i]} vendor/bin/phpcs --standard=phpcs.xml -n"
${php_versions[$i]} vendor/bin/phpcs --standard=phpcs.xml -n
fi
done
done

@ -9,60 +9,82 @@ declare(strict_types=1);
* @author Kristaps Muižnieks https://github.com/krmu
*/
require file_exists(__DIR__ . '/../../vendor/autoload.php') ? __DIR__ . '/../../vendor/autoload.php' : __DIR__ . '/../../flight/autoload.php';
Flight::set('flight.content_length', false);
Flight::set('flight.views.path', './');
Flight::set('flight.views.extension', '.phtml');
// This enables the old functionality of Flight output buffering
Flight::set('flight.v2.output_buffering', true);
// Test 1: Root route
Flight::route('/', function () {
echo '<span id="infotext">Route text:</span> Root route works!';
if (Flight::request()->query->redirected) {
echo '<br>Redirected from /redirect route successfully!';
namespace Tests\ServerV2 {
class AuthCheck
{
public function before(): void
{
if (!isset($_COOKIE['user'])) {
echo '<span id="infotext">Middleware text:</span> You are not authorized to access this route!';
}
}
}
});
Flight::route('/querytestpath', function () {
echo '<span id="infotext">Route text:</span> This ir query route<br>';
echo "I got such query parameters:<pre>";
print_r(Flight::request()->query);
echo "</pre>";
}, false, "querytestpath");
// Test 2: Simple route
Flight::route('/test', function () {
echo '<span id="infotext">Route text:</span> Test route works!';
});
// Test 3: Route with parameter
Flight::route('/user/@name', function ($name) {
echo "<span id='infotext'>Route text:</span> Hello, $name!";
});
Flight::route('POST /postpage', function () {
echo '<span id="infotext">Route text:</span> THIS IS POST METHOD PAGE';
}, false, "postpage");
// Test 4: Grouped routes
Flight::group('/group', function () {
}
namespace {
use Tests\ServerV2\AuthCheck;
require_once __DIR__ . '/../phpunit_autoload.php';
Flight::set('flight.content_length', false);
Flight::set('flight.views.path', './');
Flight::set('flight.views.extension', '.phtml');
// This enables the old functionality of Flight output buffering
Flight::set('flight.v2.output_buffering', true);
// Test 1: Root route
Flight::route('/', function () {
echo '<span id="infotext">Route text:</span> Root route works!';
if (Flight::request()->query['redirected']) {
echo '<br>Redirected from /redirect route successfully!';
}
});
Flight::route('/querytestpath', function () {
echo '<span id="infotext">Route text:</span> This ir query route<br>';
echo 'I got such query parameters:<pre>';
print_r(Flight::request()->query);
echo '</pre>';
}, false, 'querytestpath');
// Test 2: Simple route
Flight::route('/test', function () {
echo '<span id="infotext">Route text:</span> Group test route works!';
echo '<span id="infotext">Route text:</span> Test route works!';
});
// Test 3: Route with parameter
Flight::route('/user/@name', function ($name) {
echo "<span id='infotext'>Route text:</span> There is variable called name and it is $name";
echo "<span id='infotext'>Route text:</span> Hello, $name!";
});
Flight::group('/group1', function () {
Flight::group('/group2', function () {
Flight::group('/group3', function () {
Flight::group('/group4', function () {
Flight::group('/group5', function () {
Flight::group('/group6', function () {
Flight::group('/group7', function () {
Flight::group('/group8', function () {
Flight::route('/final_group', function () {
echo 'Mega Group test route works!';
}, false, "final_group");
Flight::route('POST /postpage', function () {
echo '<span id="infotext">Route text:</span> THIS IS POST METHOD PAGE';
}, false, 'postpage');
// Test 4: Grouped routes
Flight::group('/group', function () {
Flight::route('/test', function () {
echo '<span id="infotext">Route text:</span> Group test route works!';
});
Flight::route('/user/@name', function ($name) {
echo "<span id='infotext'>Route text:</span> There is variable called name and it is $name";
});
Flight::group('/group1', function () {
Flight::group('/group2', function () {
Flight::group('/group3', function () {
Flight::group('/group4', function () {
Flight::group('/group5', function () {
Flight::group('/group6', function () {
Flight::group('/group7', function () {
Flight::group('/group8', function () {
Flight::route('/final_group', function () {
echo 'Mega Group test route works!';
}, false, 'final_group');
});
});
});
});
@ -71,152 +93,151 @@ Flight::group('/group', function () {
});
});
});
});
// Test 5: Route alias
Flight::route('/alias', function () {
echo '<span id="infotext">Route text:</span> Alias route works!';
}, false, 'aliasroute');
class AuthCheck
{
public function before(): void
{
if (!isset($_COOKIE['user'])) {
echo '<span id="infotext">Middleware text:</span> You are not authorized to access this route!';
// Test 5: Route alias
Flight::route('/alias', function () {
echo '<span id="infotext">Route text:</span> Alias route works!';
}, false, 'aliasroute');
$middle = new AuthCheck();
// Test 6: Route with middleware
Flight::route('/protected', function () {
echo '<span id="infotext">Route text:</span> Protected route works!';
})->addMiddleware([$middle]);
// Test 7: Route with template
Flight::route('/template/@name', function ($name) {
Flight::render('template.phtml', ['name' => $name]);
});
// Test 8: Throw an error
Flight::route('/error', function () {
trigger_error('This is a successful error');
});
// Test 9: JSON output (should not output any other html)
Flight::route('/json', function () {
echo "\n\n\n\n\n";
Flight::json(['message' => 'JSON renders successfully!']);
echo "\n\n\n\n\n";
});
// Test 13: JSONP output (should not output any other html)
Flight::route('/jsonp', function () {
echo "\n\n\n\n\n";
Flight::jsonp(['message' => 'JSONP renders successfully!'], 'jsonp');
echo "\n\n\n\n\n";
});
Flight::route('/json-halt', function () {
Flight::jsonHalt(['message' => 'JSON rendered and halted successfully with no other body content!']);
});
// Test 10: Halt
Flight::route('/halt', function () {
Flight::halt(400, 'Halt worked successfully');
});
// Test 11: Redirect
Flight::route('/redirect', function () {
Flight::redirect('/?redirected=1');
});
Flight::set('flight.views.path', './');
Flight::map('error', function (Throwable $error) {
echo "<h1> An error occurred, mapped error method worked, error below </h1>";
echo '<pre style="border: 2px solid red; padding: 21px; background: lightgray; font-weight: bold;">';
echo str_replace(getenv('PWD'), "***CLASSIFIED*****", $error->getTraceAsString());
echo "</pre>";
echo "<a href='/'>Go back</a>";
});
Flight::map('notFound', function () {
echo '<span id="infotext">Route text:</span> The requested URL was not found';
echo "<a href='/'>Go back</a>";
});
echo '
<style>
ul {
list-style-type: none;
margin: 0;
padding: 0;
overflow: hidden;
background-color: #333;
}
}
}
$middle = new AuthCheck();
// Test 6: Route with middleware
Flight::route('/protected', function () {
echo '<span id="infotext">Route text:</span> Protected route works!';
})->addMiddleware([$middle]);
// Test 7: Route with template
Flight::route('/template/@name', function ($name) {
Flight::render('template.phtml', ['name' => $name]);
});
// Test 8: Throw an error
Flight::route('/error', function () {
trigger_error('This is a successful error');
});
// Test 9: JSON output (should not output any other html)
Flight::route('/json', function () {
echo "\n\n\n\n\n";
Flight::json(['message' => 'JSON renders successfully!']);
echo "\n\n\n\n\n";
});
// Test 13: JSONP output (should not output any other html)
Flight::route('/jsonp', function () {
echo "\n\n\n\n\n";
Flight::jsonp(['message' => 'JSONP renders successfully!'], 'jsonp');
echo "\n\n\n\n\n";
});
Flight::route('/json-halt', function () {
Flight::jsonHalt(['message' => 'JSON rendered and halted successfully with no other body content!']);
});
// Test 10: Halt
Flight::route('/halt', function () {
Flight::halt(400, 'Halt worked successfully');
});
// Test 11: Redirect
Flight::route('/redirect', function () {
Flight::redirect('/?redirected=1');
});
Flight::set('flight.views.path', './');
Flight::map('error', function (Throwable $error) {
echo "<h1> An error occurred, mapped error method worked, error below </h1>";
echo '<pre style="border: 2px solid red; padding: 21px; background: lightgray; font-weight: bold;">';
echo str_replace(getenv('PWD'), "***CLASSIFIED*****", $error->getTraceAsString());
echo "</pre>";
echo "<a href='/'>Go back</a>";
});
Flight::map('notFound', function () {
echo '<span id="infotext">Route text:</span> The requested URL was not found';
echo "<a href='/'>Go back</a>";
});
echo '
<style>
ul {
list-style-type: none;
margin: 0;
padding: 0;
overflow: hidden;
background-color: #333;
}
li {
float: left;
}
#infotext {
font-weight: bold;
color: blueviolet;
li {
float: left;
}
#infotext {
font-weight: bold;
color: blueviolet;
}
li a {
display: block;
color: white;
text-align: center;
padding: 14px 16px;
text-decoration: none;
}
li a {
display: block;
color: white;
text-align: center;
padding: 14px 16px;
text-decoration: none;
}
li a:hover {
background-color: #111;
}
#container {
color: #333;
font-size: 16px;
line-height: 1.5;
margin: 20px 0;
padding: 10px;
border: 1px solid #ddd;
background-color: #f9f9f9;
}
#debugrequest {
color: #333;
font-size: 16px;
line-height: 1.5;
margin: 20px 0;
padding: 10px;
border: 1px solid #ddd;
background-color: #f9f9f9;
}
</style>
<ul>
<li><a href="/">Root Route</a></li>
<li><a href="/test">Test Route</a></li>
<li><a href="/user/John">User Route with Parameter (John)</a></li>
<li><a href="/group/test">Grouped Test Route</a></li>
<li><a href="/group/user/Jane">Grouped User Route with Parameter (Jane)</a></li>
<li><a href="/alias">Alias Route</a></li>
<li><a href="/protected">Protected path</a></li>
<li><a href="/template/templatevariable">Template path</a></li>
<li><a href="/querytestpath?test=1&variable2=uuid&variable3=tester">Query path</a></li>
<li><a href="/postpage">Post method test page - should be 404</a></li>
<li><a href="' . Flight::getUrl('final_group') . '">Mega group</a></li>
<li><a href="/error">Error</a></li>
<li><a href="/json">JSON</a></li>
<li><a href="/jsonp?jsonp=myjson">JSONP</a></li>
<li><a href="/json-halt">JSON Halt</a></li>
<li><a href="/halt">Halt</a></li>
<li><a href="/redirect">Redirect</a></li>
</ul>';
Flight::before('start', function ($params) {
echo '<div id="container">';
});
Flight::after('start', function ($params) {
echo '</div>';
echo '<div id="debugrequest">';
echo "Request information<pre>";
print_r(Flight::request());
echo "</pre>";
echo "</div>";
});
Flight::start();
li a:hover {
background-color: #111;
}
#container {
color: #333;
font-size: 16px;
line-height: 1.5;
margin: 20px 0;
padding: 10px;
border: 1px solid #ddd;
background-color: #f9f9f9;
}
#debugrequest {
color: #333;
font-size: 16px;
line-height: 1.5;
margin: 20px 0;
padding: 10px;
border: 1px solid #ddd;
background-color: #f9f9f9;
}
</style>
<ul>
<li><a href="/">Root Route</a></li>
<li><a href="/test">Test Route</a></li>
<li><a href="/user/John">User Route with Parameter (John)</a></li>
<li><a href="/group/test">Grouped Test Route</a></li>
<li><a href="/group/user/Jane">Grouped User Route with Parameter (Jane)</a></li>
<li><a href="/alias">Alias Route</a></li>
<li><a href="/protected">Protected path</a></li>
<li><a href="/template/templatevariable">Template path</a></li>
<li><a href="/querytestpath?test=1&variable2=uuid&variable3=tester">Query path</a></li>
<li><a href="/postpage">Post method test page - should be 404</a></li>
<li><a href="' . Flight::getUrl('final_group') . '">Mega group</a></li>
<li><a href="/error">Error</a></li>
<li><a href="/json">JSON</a></li>
<li><a href="/jsonp?jsonp=myjson">JSONP</a></li>
<li><a href="/json-halt">JSON Halt</a></li>
<li><a href="/halt">Halt</a></li>
<li><a href="/redirect">Redirect</a></li>
</ul>';
Flight::before('start', function () {
echo '<div id="container">';
});
Flight::after('start', function () {
echo '</div>';
echo '<div id="debugrequest">';
echo "Request information<pre>";
print_r(Flight::request());
echo "</pre>";
echo "</div>";
});
Flight::start();
}

@ -1 +1 @@
<span id="infotext">Route text:</span> Template <?=$name?> works!
<span id="infotext">Route text:</span> Template <?= $name ?> works!

@ -2,6 +2,8 @@
declare(strict_types=1);
namespace Tests\Server;
class AuthCheck
{
public function before(): void

@ -2,6 +2,10 @@
declare(strict_types=1);
namespace Tests\Server;
use Flight;
class LayoutMiddleware
{
public function before(): void
@ -9,50 +13,50 @@ class LayoutMiddleware
$final_route = Flight::getUrl('final_group');
echo <<<HTML
<style>
ul {
list-style-type: none;
margin: 0;
padding: 0;
overflow: hidden;
background-color: #333;
}
ul {
list-style-type: none;
margin: 0;
padding: 0;
overflow: hidden;
background-color: #333;
}
li {
float: left;
}
#infotext {
font-weight: bold;
color: blueviolet;
}
li a {
display: block;
color: white;
text-align: center;
padding: 14px 16px;
text-decoration: none;
}
li {
float: left;
}
#infotext {
font-weight: bold;
color: blueviolet;
}
li a {
display: block;
color: white;
text-align: center;
padding: 14px 16px;
text-decoration: none;
}
li a:hover {
background-color: #111;
}
#container {
color: #333;
font-size: 16px;
line-height: 1.5;
margin: 20px 0;
padding: 10px;
border: 1px solid #ddd;
background-color: #f9f9f9;
}
#debugrequest {
color: #333;
font-size: 16px;
line-height: 1.5;
margin: 20px 0;
padding: 10px;
border: 1px solid #ddd;
background-color: #f9f9f9;
}
li a:hover {
background-color: #111;
}
#container {
color: #333;
font-size: 16px;
line-height: 1.5;
margin: 20px 0;
padding: 10px;
border: 1px solid #ddd;
background-color: #f9f9f9;
}
#debugrequest {
color: #333;
font-size: 16px;
line-height: 1.5;
margin: 20px 0;
padding: 10px;
border: 1px solid #ddd;
background-color: #f9f9f9;
}
</style>
<ul>
<li><a href="/">Root Route</a></li>

@ -2,11 +2,20 @@
declare(strict_types=1);
namespace Tests\Server;
use Flight;
class OverwriteBodyMiddleware
{
public function after(): void
{
$response = Flight::response();
$response->write(str_replace('<span style="color:red; font-weight: bold;">failed</span>', '<span style="color:green; font-weight: bold;">successfully works!</span>', $response->getBody()), true);
$response->write(str_replace(
'<span style="color:red; font-weight: bold;">failed</span>',
'<span style="color:green; font-weight: bold;">successfully works!</span>',
$response->getBody()
), true);
}
}

@ -2,6 +2,8 @@
declare(strict_types=1);
namespace Tests\Server;
class Pascal_Snake_Case // phpcs:ignore
{
public function doILoad() // phpcs:ignore

@ -2,10 +2,15 @@
declare(strict_types=1);
use Dice\Dice;
use flight\core\Loader;
use flight\database\PdoWrapper;
use tests\classes\Container;
use tests\classes\ContainerDefault;
use Tests\Server\AuthCheck;
use Tests\Server\LayoutMiddleware;
use Tests\Server\OverwriteBodyMiddleware;
use Tests\Server\Pascal_Snake_Case;
/*
* This is the test file where we can open up a quick test server and make
@ -14,7 +19,7 @@ use tests\classes\ContainerDefault;
* @author Kristaps Muižnieks https://github.com/krmu
*/
require file_exists(__DIR__ . '/../../vendor/autoload.php') ? __DIR__ . '/../../vendor/autoload.php' : __DIR__ . '/../../flight/autoload.php';
require_once __DIR__ . '/../phpunit_autoload.php';
Flight::set('flight.content_length', false);
Flight::set('flight.views.path', './');
@ -23,20 +28,20 @@ Loader::setV2ClassLoading(false);
Flight::path(__DIR__);
Flight::group('', function () {
// Test 1: Root route
Flight::route('/', function () {
echo '<span id="infotext">Route text:</span> Root route works!';
if (Flight::request()->query->redirected) {
if (Flight::request()->query['redirected']) {
echo '<br>Redirected from /redirect route successfully!';
}
});
Flight::route('/querytestpath', function () {
echo '<span id="infotext">Route text:</span> This is query route<br>';
echo "Query parameters:<pre>";
echo 'Query parameters:<pre>';
print_r(Flight::request()->query);
echo "</pre>";
}, false, "querytestpath");
echo '</pre>';
}, false, 'querytestpath');
// Test 2: Simple route
Flight::route('/test', function () {
@ -47,18 +52,21 @@ Flight::group('', function () {
Flight::route('/user/@name', function ($name) {
echo "<span id='infotext'>Route text:</span> Hello, $name!";
});
Flight::route('POST /postpage', function () {
echo '<span id="infotext">Route text:</span> THIS IS POST METHOD PAGE';
}, false, "postpage");
}, false, 'postpage');
// Test 4: Grouped routes
Flight::group('/group', function () {
Flight::route('/test', function () {
echo '<span id="infotext">Route text:</span> Group test route works!';
});
Flight::route('/user/@name', function ($name) {
echo "<span id='infotext'>Route text:</span> There is variable called name and it is $name";
});
Flight::group('/group1', function () {
Flight::group('/group2', function () {
Flight::group('/group3', function () {
@ -69,7 +77,7 @@ Flight::group('', function () {
Flight::group('/group8', function () {
Flight::route('/final_group', function () {
echo 'Mega Group test route works!';
}, false, "final_group");
}, false, 'final_group');
});
});
});
@ -86,8 +94,8 @@ Flight::group('', function () {
}, false, 'aliasroute');
/** Middleware test */
include_once 'AuthCheck.php';
$middle = new AuthCheck();
// Test 6: Route with middleware
Flight::route('/protected', function () {
echo '<span id="infotext">Route text:</span> Protected route works!';
@ -119,51 +127,75 @@ Flight::group('', function () {
// Test 12: Redirect with status code
Flight::route('/streamResponse', function () {
echo "Streaming a response";
echo 'Streaming a response';
for ($i = 1; $i <= 50; $i++) {
echo ".";
usleep(50000);
ob_flush();
}
echo "is successful!!";
echo 'is successful!!';
})->stream();
// Test 12: Redirect with status code
Flight::route('/streamWithHeaders', function () {
echo "Streaming a response";
echo 'Streaming a response';
for ($i = 1; $i <= 50; $i++) {
echo ".";
usleep(50000);
ob_flush();
}
echo "is successful!!";
})->streamWithHeaders(['Content-Type' => 'text/html', 'status' => 200 ]);
echo 'is successful!!';
})->streamWithHeaders(['Content-Type' => 'text/html', 'status' => 200]);
// Test 14: Overwrite the body with a middleware
Flight::route('/overwrite', function () {
echo '<span id="infotext">Route text:</span> This route status is that it <span style="color:red; font-weight: bold;">failed</span>';
echo <<<'html'
<span id="infotext">Route text:</span>
This route status is that it
<span style="color:red; font-weight: bold;">failed</span>
html;
})->addMiddleware([new OverwriteBodyMiddleware()]);
// Test 15: UTF8 Chars in url
Flight::route('/わたしはひとです', function () {
echo '<span id="infotext">Route text:</span> This route status is that it <span style="color:green; font-weight: bold;">succeeded はい!!!</span>';
echo <<<'html'
<span id="infotext">Route text:</span>
This route status is that it
<span style="color:green; font-weight: bold;">succeeded はい!!!</span>
html;
});
// Test 16: UTF8 Chars in url with utf8 params
Flight::route('/わたしはひとです/@name', function ($name) {
echo '<span id="infotext">Route text:</span> This route status is that it <span style="color:' . ($name === 'ええ' ? 'green' : 'red') . '; font-weight: bold;">' . ($name === 'ええ' ? 'succeeded' : 'failed') . ' URL Param: ' . $name . '</span>';
echo '<span id="infotext">Route text:</span> This route status is that it <span style="color:'
. ($name === 'ええ' ? 'green' : 'red')
. '; font-weight: bold;">'
. ($name === 'ええ' ? 'succeeded' : 'failed')
. ' URL Param: '
. $name
. '</span>';
});
// Test 17: Slash in param
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>';
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::set('test_me_out', 'You got it boss!'); // used in /no-container route
Flight::route('/no-container', ContainerDefault::class . '->testUi');
Flight::route('/dice', Container::class . '->testThePdoWrapper');
Flight::route('/Pascal_Snake_Case', Pascal_Snake_Case::class . '->doILoad');
}, [ new LayoutMiddleware() ]);
}, [new LayoutMiddleware()]);
// Test 9: JSON output (should not output any other html)
Flight::route('/json', function () {
@ -195,23 +227,25 @@ Flight::map('error', function (Throwable $e) {
$e->getCode(),
str_replace(getenv('PWD'), '***CONFIDENTIAL***', $e->getTraceAsString())
);
echo "<br><a href='/'>Go back</a>";
});
Flight::map('notFound', function () {
echo '<span id="infotext">Route text:</span> The requested URL was not found<br>';
echo "<a href='/'>Go back</a>";
});
Flight::map('start', function () {
if (Flight::request()->url === '/dice') {
$dice = new \Dice\Dice();
$dice = new Dice();
$dice = $dice->addRules([
PdoWrapper::class => [
'shared' => true,
'constructParams' => [ 'sqlite::memory:' ]
'constructParams' => ['sqlite::memory:']
]
]);
Flight::registerContainerHandler(function ($class, $params) use ($dice) {
return $dice->create($class, $params);
});

@ -1,5 +1,5 @@
<?php if(isset($name)): ?>
<span id="infotext">Route text:</span> Template <?=$name?> works!
<?php elseif(isset($data)): ?>
<span id="infotext">Route text:</span> Template with variable name "data" works! See: <?=$data?>
<?php if (isset($name)): ?>
<span id="infotext">Route text:</span> Template <?= $name ?> works!
<?php elseif (isset($data)): ?>
<span id="infotext">Route text:</span> Template with variable name "data" works! See: <?= $data ?>
<?php endif; ?>

@ -1 +1 @@
This file downloaded successfully!
This file downloaded successfully!

@ -1 +1 @@
<html><?php echo $content; ?></html>
<body><?= $content ?></body>

@ -1 +1 @@
Hello world, <?php echo $name; ?>!
Hello world, <?php echo $name; ?>!

Loading…
Cancel
Save