Merge branch 'master' into dispatcher-rework

dispatcher-rework
fadrian06 3 days ago
commit 1a8af5c96b

@ -16,11 +16,11 @@ This is the main FlightPHP core library for building fast, simple, and extensibl
- Run tests: `composer test` (uses phpunit/phpunit and spatie/phpunit-watcher)
- Run test server: `composer test-server` or `composer test-server-v2`
- Lint code & Check code style: `composer lint` (uses phpstan/phpstan, level 6)
- Beautify code: `composer format` (uses squizlabs/php_codesniffer, PSR1)
- Beautify code: `composer format` (uses squizlabs/php_codesniffer, PSR12)
- Test coverage: `composer test-coverage`
## Coding Standards
- Follow PSR1 coding standards (enforced by PHPCS)
- Follow PSR12 coding standards (enforced by PHPCS)
- Use strict comparisons (`===`, `!==`)
- PHPStan level 6 compliance
- Focus on PHP 7.4 compatibility (avoid PHP 8+ only features)

@ -16,11 +16,11 @@ This is the main FlightPHP core library for building fast, simple, and extensibl
- Run tests: `composer test` (uses phpunit/phpunit and spatie/phpunit-watcher)
- Run test server: `composer test-server` or `composer test-server-v2`
- Lint code & Check code style: `composer lint` (uses phpstan/phpstan, level 6)
- Beautify code: `composer format` (uses squizlabs/php_codesniffer, PSR1)
- Beautify code: `composer format` (uses squizlabs/php_codesniffer, PSR12)
- Test coverage: `composer test-coverage`
## Coding Standards
- Follow PSR1 coding standards (enforced by PHPCS)
- Follow PSR12 coding standards (enforced by PHPCS)
- Use strict comparisons (`===`, `!==`)
- PHPStan level 6 compliance
- Focus on PHP 7.4 compatibility (avoid PHP 8+ only features)

@ -16,11 +16,11 @@ This is the main FlightPHP core library for building fast, simple, and extensibl
- Run tests: `composer test` (uses phpunit/phpunit and spatie/phpunit-watcher)
- Run test server: `composer test-server` or `composer test-server-v2`
- Lint code & Check code style: `composer lint` (uses phpstan/phpstan, level 6)
- Beautify code: `composer format` (uses squizlabs/php_codesniffer, PSR1)
- Beautify code: `composer format` (uses squizlabs/php_codesniffer, PSR12)
- Test coverage: `composer test-coverage`
## Coding Standards
- Follow PSR1 coding standards (enforced by PHPCS)
- Follow PSR12 coding standards (enforced by PHPCS)
- Use strict comparisons (`===`, `!==`)
- PHPStan level 6 compliance
- Focus on PHP 7.4 compatibility (avoid PHP 8+ only features)

@ -9,19 +9,19 @@ Flight aims to be simple and fast. Anything that compromises either of those two
* **Dependencies** - We strive to be dependency free in Flight. Yes even polyfills, yes even `Interface` only repos like `psr/container`. The fewer dependencies, the fewer your exposed attack vectors.
* **Coding Standards** - We use PSR1 coding standards enforced by PHPCS. Some standards that either need additional configuration or need to be manually done are:
* **Coding Standards** - We use PSR-12 coding standards enforced by PHPCS. Some standards that either need additional configuration or need to be manually done are:
* PHPStan is at level 6.
* `===` instead of truthy or falsey statements like `==` or `!is_array()`.
* `===` instead of truthy or falsy statements like `==`.
* **PHP 7.4 Focused** - We do not make PHP 8+ focused enhancements on the framework as the focus is maintaining PHP 7.4.
* **Core functionality vs Plugin** - Have a conversation with us in the [chatroom](https://matrix.to/#/!cTfwPXhpkTXPXwVmxY:matrix.org?via=matrix.org&via=leitstelle511.net&via=integrations.ems.host) to know if your idea is worth makes sense in the framework or in a plugin.
* **Testing** - Until automated testing is put into place, any PRs must pass unit testing in PHP 7.4 and PHP 8.2+. Additionally you need to run `composer test-server` and `composer test-server-v2` and ensure all the header links work correctly.
* **Testing** - PRs must pass unit tests on PHP 7.4 through PHP 8.5+. Additionally you need to run `composer test-server` and `composer test-server-v2` and ensure all the header links work correctly.
#### **Did you find a bug?**
* **Do not open up a GitHub issue if the bug is a security vulnerability**. Instead contact maintainers directly via email to safely pass in the information related to the security vuln.
* **Do not open up a GitHub issue if the bug is a security vulnerability**. Instead contact maintainers directly via email to safely share details about the security vulnerability.
* **Ensure the bug was not already reported** by searching on GitHub under [Issues](https://github.com/flightphp/core/issues).

@ -22,13 +22,10 @@ composer require flightphp/core
or you can download a zip of this repo. Then you would have a basic `index.php` file like the following:
```php
// if installed with composer
require 'vendor/autoload.php';
// or if installed manually by zip file
// require 'flight/Flight.php';
require 'flight/autoload.php';
Flight::route('/', function () {
echo 'hello world!';
echo 'hello world!';
});
Flight::start();
@ -40,17 +37,20 @@ Yes! Flight is fast. It is one of the fastest PHP frameworks available. You can
See the benchmark below with some other popular PHP frameworks. This is measured in requests processed within the same timeframe.
| Framework | Plaintext Requests| JSON Requests|
| --------- | ------------ | ------------ |
| Flight | 190,421 | 182,491 |
| Yii | 145,749 | 131,434 |
| Fat-Free | 139,238 | 133,952 |
| Slim | 89,588 | 87,348 |
| Phalcon | 95,911 | 87,675 |
| Symfony | 65,053 | 63,237 |
| Lumen | 40,572 | 39,700 |
| Laravel | 26,657 | 26,901 |
| CodeIgniter | 20,628 | 19,901 |
```mermaid
---
config:
xyChart:
showDataLabel: true
---
xychart-beta
title "PHP Framework Performance (TechEmpower, requests/sec)"
x-axis "Framework" ["Flight", "Yii", "Fat-Free", "Slim", "Phalcon", "Symfony", "Lumen", "Laravel", "CodeIgniter"]
y-axis "Requests/sec (x1000)" 0 --> 200
%% Series 1: Plaintext, Series 2: JSON
bar [190.4, 145.7, 139.2, 89.6, 95.9, 65.1, 40.6, 26.7, 20.6]
bar [182.5, 131.4, 134.0, 87.3, 87.7, 63.2, 39.7, 26.9, 19.9]
```
## Skeleton App
@ -75,9 +75,10 @@ If you have a current project on v2, you should be able to upgrade to v3 with no
> [!IMPORTANT]
> Flight requires `PHP 7.4` or greater.
**Note:** PHP 7.4 is supported because at the current time of writing (2024) PHP 7.4 is the default version for some LTS Linux distributions. Forcing a move to PHP >8 would cause a lot of heartburn for those users.
The framework also supports PHP >8.
> [!NOTE]
> PHP 7.4 is supported because at the current time of writing (2024) PHP 7.4 is the default version for some LTS Linux distributions.
> Forcing a move to PHP 8 would cause a lot of heartburn for those users.
> The framework also supports PHP 8.
# Roadmap

@ -24,8 +24,7 @@ use Psr\Container\ContainerInterface;
* It is responsible for loading an HTTP request, running the assigned services,
* and generating an HTTP response.
*
* @license MIT, https://docs.flightphp.com/license
* @copyright Copyright (c) 2011-2025, Mike Cao <mike@mikecao.com>, n0nag0n <n0nag0n@sky-9.com>
* @copyright 2011-2026, Mike Cao https://mikecao.com, n0nag0n <n0nag0n@sky-9.com>
*
* @method void start()
* @method void stop()
@ -74,10 +73,7 @@ use Psr\Container\ContainerInterface;
* @phpstan-method void json(mixed $data, int $code = 200, bool $encode = true, string $charset = "utf8", int $encodeOption = 0, int $encodeDepth = 512)
* @phpstan-method void jsonHalt(mixed $data, int $code = 200, bool $encode = true, string $charset = 'utf-8', int $option = 0)
* @phpstan-method 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.
*
* phpcs:disable PSR2.Methods.MethodDeclaration.Underscore
* @license https://docs.flightphp.com/license MIT
*/
class Engine
{

@ -15,7 +15,7 @@ use Psr\Container\ContainerInterface;
* The Flight class is a static representation of the framework.
*
* @license MIT, https://docs.flightphp.com/license
* @copyright Copyright (c) 2011-2025, Mike Cao <mike@mikecao.com>, n0nag0n <n0nag0n@sky-9.com>
* @copyright 2011-2026, Mike Cao https://mikecao.com, n0nag0n <n0nag0n@sky-9.com>
*
* @method static void start()
* @method static void path(string $dir)
@ -77,11 +77,8 @@ use Psr\Container\ContainerInterface;
* @phpstan-method static void json(mixed $data, int $code = 200, bool $encode = true, string $charset = "utf8", int $encodeOption = 0, int $encodeDepth = 512)
* @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.
* @license https://docs.flightphp.com/license MIT
*/
// phpcs:ignore PSR1.Classes.ClassDeclaration.MissingNamespace
class Flight
{
/**

@ -6,134 +6,83 @@ namespace flight\core;
class EventDispatcher
{
/** @var self|null Singleton instance of the EventDispatcher */
private static ?self $instance = null;
/** @var array<string, array<int, callable>> */
/** @var array<string, callable[]> */
protected array $listeners = [];
/**
* Singleton instance of the EventDispatcher.
*
* @return self
*/
public static function getInstance(): self
{
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Register a callback for an event.
*
* @param string $event Event name
* @param callable $callback Callback function
*/
public function on(string $event, callable $callback): void
{
if (isset($this->listeners[$event]) === false) {
$this->listeners[$event] = [];
}
$this->listeners[$event] ??= [];
$this->listeners[$event][] = $callback;
}
/**
* Trigger an event with optional arguments.
*
* @param string $event Event name
* @param mixed ...$args Arguments to pass to the callbacks
*
* @param mixed ...$args Arguments to pass to the listeners.
* @return mixed
*/
public function trigger(string $event, ...$args)
{
$result = null;
if (isset($this->listeners[$event]) === true) {
foreach ($this->listeners[$event] as $callback) {
$result = call_user_func_array($callback, $args);
// If you return false, it will break the loop and stop the other event listeners.
if ($result === false) {
break; // Stop executing further listeners
}
$listenerReturnValue = null;
foreach ($this->getListeners($event) as $listener) {
$listenerReturnValue = $listener(...$args);
if ($listenerReturnValue === false) {
break;
}
}
return $result;
return $listenerReturnValue;
}
/**
* Check if an event has any registered listeners.
*
* @param string $event Event name
*
* @return bool True if the event has listeners, false otherwise
*/
public function hasListeners(string $event): bool
{
return isset($this->listeners[$event]) === true && count($this->listeners[$event]) > 0;
return (
isset($this->listeners[$event])
&& is_array($this->listeners[$event])
&& count($this->listeners[$event])
);
}
/**
* Get all listeners registered for a specific event.
*
* @param string $event Event name
*
* @return array<int, callable> Array of callbacks registered for the event
*/
/** @return callable[] */
public function getListeners(string $event): array
{
return $this->listeners[$event] ?? [];
}
/**
* Get a list of all events that have registered listeners.
*
* @return array<int, string> Array of event names
*/
/** @return string[] */
public function getAllRegisteredEvents(): array
{
return array_keys($this->listeners);
}
/**
* Remove a specific listener for an event.
*
* @param string $event the event name
* @param callable $callback the exact callback to remove
*
* @return void
*/
public function removeListener(string $event, callable $callback): void
{
if (isset($this->listeners[$event]) === true && count($this->listeners[$event]) > 0) {
$this->listeners[$event] = array_filter($this->listeners[$event], function ($listener) use ($callback) {
return $listener !== $callback;
});
$this->listeners[$event] = array_values($this->listeners[$event]); // Re-index the array
if (!$this->hasListeners($event)) {
return;
}
$this->listeners[$event] = array_values(array_filter(
$this->getListeners($event),
static fn(callable $listener): bool => $listener !== $callback,
));
}
/**
* Remove all listeners for a specific event.
*
* @param string $event the event name
*
* @return void
*/
public function removeAllListeners(string $event): void
{
if (isset($this->listeners[$event]) === true) {
unset($this->listeners[$event]);
}
unset($this->listeners[$event]);
}
/**
* Remove the current singleton instance of the EventDispatcher.
*
* @return void
*/
public static function resetInstance(): void
{
self::$instance = null;

@ -13,8 +13,8 @@ use Exception;
* instances with custom initialization parameters. It also performs
* class autoloading.
*
* @license MIT, http://flightphp.com/license
* @copyright Copyright (c) 2011, Mike Cao <mike@mikecao.com>
* @copyright 2011 Mike Cao https://mikecao.com
* @license https://docs.flightphp.com/license MIT
*/
class Loader
{

@ -11,8 +11,8 @@ use flight\util\Collection;
* all the super globals $_GET, $_POST, $_COOKIE, and $_FILES
* are stored and accessible via the Request object.
*
* @license MIT, http://flightphp.com/license
* @copyright Copyright (c) 2011, Mike Cao <mike@mikecao.com>
* @copyright 2011 Mike Cao https://mikecao.com
* @license https://docs.flightphp.com/license MIT
*
* The default request properties are:
*

@ -12,8 +12,8 @@ use flight\core\EventDispatcher;
* contains the response headers, HTTP status code, and response
* body.
*
* @license MIT, http://flightphp.com/license
* @copyright Copyright (c) 2011, Mike Cao <mike@mikecao.com>
* @copyright 2011 Mike Cao https://mikecao.com
* @license https://docs.flightphp.com/license MIT
*/
class Response
{

@ -9,8 +9,8 @@ namespace flight\net;
* an assigned callback function. The Router tries to match the
* requested URL against a series of URL patterns.
*
* @license MIT, http://flightphp.com/license
* @copyright Copyright (c) 2011, Mike Cao <mike@mikecao.com>
* @copyright 2011 Mike Cao https://mikecao.com
* @license https://docs.flightphp.com/license MIT
*/
class Route
{

@ -12,8 +12,8 @@ use flight\net\Route;
* an assigned callback function. The Router tries to match the
* requested URL against a series of URL patterns.
*
* @license MIT, http://flightphp.com/license
* @copyright Copyright (c) 2011, Mike Cao <mike@mikecao.com>
* @copyright 2011 Mike Cao https://mikecao.com
* @license https://docs.flightphp.com/license MIT
*/
class Router
{

@ -9,8 +9,8 @@ namespace flight\template;
* methods for managing view data and inserts the data into
* view templates upon rendering.
*
* @license MIT, http://flightphp.com/license
* @copyright Copyright (c) 2011, Mike Cao <mike@mikecao.com>
* @copyright 2011 Mike Cao https://mikecao.com
* @license https://docs.flightphp.com/license MIT
*/
class View
{

@ -13,10 +13,10 @@ use JsonSerializable;
* The Collection class allows you to access a set of data
* using both array and object notation.
*
* @license MIT, http://flightphp.com/license
* @copyright Copyright (c) 2011, Mike Cao <mike@mikecao.com>
* @copyright 2011 Mike Cao https://mikecao.com
* @implements ArrayAccess<string, mixed>
* @implements Iterator<string, mixed>
* @license https://docs.flightphp.com/license MIT
*/
class Collection implements ArrayAccess, Iterator, Countable, JsonSerializable
{

Loading…
Cancel
Save