From 6c5d9de3ca6aade8e40e6a17fcdffa701701fa8b Mon Sep 17 00:00:00 2001 From: fadrian06 Date: Fri, 14 Aug 2026 02:15:03 -0400 Subject: [PATCH 01/35] remove Engine @template --- flight/Engine.php | 1 - flight/core/Dispatcher.php | 9 --------- 2 files changed, 10 deletions(-) diff --git a/flight/Engine.php b/flight/Engine.php index 880decff..372f9523 100644 --- a/flight/Engine.php +++ b/flight/Engine.php @@ -57,7 +57,6 @@ * @method void lastModified(int $time) * @method void download(string $filePath) * - * @phpstan-template EngineTemplate of object * @phpstan-method void registerContainerHandler(ContainerInterface|callable(class-string $id, array $params): ?EngineTemplate $containerHandler) * @phpstan-method Route route(string $pattern, callable|string|array{0: class-string, 1: string} $callback, bool $pass_route = false, string $alias = '') * @phpstan-method void group(string $pattern, callable $callback, (class-string|callable|array{0: class-string, 1: string})[] $group_middlewares = []) diff --git a/flight/core/Dispatcher.php b/flight/core/Dispatcher.php index 62ff7f95..97ea22ea 100644 --- a/flight/core/Dispatcher.php +++ b/flight/core/Dispatcher.php @@ -20,7 +20,6 @@ * * @license MIT, http://flightphp.com/license * @copyright Copyright (c) 2011, Mike Cao - * @phpstan-template EngineTemplate of object */ class Dispatcher { @@ -30,7 +29,6 @@ class Dispatcher /** Exception message if thrown by setting the container as a callable method. */ protected ?Throwable $containerException = null; - /** @var ?Engine $engine Engine instance. */ protected ?Engine $engine = null; /** @var array Mapped events. */ @@ -76,13 +74,6 @@ public function setContainerHandler($containerHandler): void ); } - /** - * Sets the engine instance - * - * @param Engine $engine Flight instance - * - * @return void - */ public function setEngine(Engine $engine): void { $this->engine = $engine; From 12607d95002b1929a2637fbd3b2d8094466b228c Mon Sep 17 00:00:00 2001 From: fadrian06 Date: Fri, 14 Aug 2026 02:46:25 -0400 Subject: [PATCH 02/35] rename Psr\Container\ContainerInterface -> Container --- flight/core/Dispatcher.php | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/flight/core/Dispatcher.php b/flight/core/Dispatcher.php index 97ea22ea..54a99d81 100644 --- a/flight/core/Dispatcher.php +++ b/flight/core/Dispatcher.php @@ -7,7 +7,7 @@ use Exception; use flight\Engine; use InvalidArgumentException; -use Psr\Container\ContainerInterface; +use Psr\Container\ContainerInterface as Container; use ReflectionFunction; use Throwable; use TypeError; @@ -41,21 +41,11 @@ class Dispatcher */ protected array $filters = []; - /** - * This is a container for the dependency injection. - * - * @var null|ContainerInterface|(callable(string $classString, array $params): (null|object)) - */ + /** @var null|Container|(callable(class-string $classString, mixed[] $params): ?object) */ protected $containerHandler = null; /** - * Sets the dependency injection container handler. - * - * @param ContainerInterface|(callable(class-string $classString, array $params): ?T) $containerHandler - * Dependency injection container. - * - * @template T of object - * + * @param Container|(callable(class-string $classString, mixed[] $params): ?object) $containerHandler * @throws InvalidArgumentException * If $containerHandler is not a `callable` or instance of `Psr\Container\ContainerInterface`. */ From ae735c18450ffcce0e520fe37c1f262dd260e5a1 Mon Sep 17 00:00:00 2001 From: fadrian06 Date: Fri, 14 Aug 2026 02:47:56 -0400 Subject: [PATCH 03/35] remove Dispatcher properties redundants explanations --- flight/core/Dispatcher.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/flight/core/Dispatcher.php b/flight/core/Dispatcher.php index 54a99d81..0ac26e05 100644 --- a/flight/core/Dispatcher.php +++ b/flight/core/Dispatcher.php @@ -26,12 +26,10 @@ class Dispatcher public const FILTER_BEFORE = 'before'; public const FILTER_AFTER = 'after'; - /** Exception message if thrown by setting the container as a callable method. */ protected ?Throwable $containerException = null; - protected ?Engine $engine = null; - /** @var array Mapped events. */ + /** @var array */ protected array $events = []; /** From 8a4818cf8787e873d26256fa666779a9090ffaab Mon Sep 17 00:00:00 2001 From: fadrian06 Date: Fri, 14 Aug 2026 02:49:13 -0400 Subject: [PATCH 04/35] improve typehint of keys before and after inside all events in the Dispatcher $filters property --- flight/core/Dispatcher.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/flight/core/Dispatcher.php b/flight/core/Dispatcher.php index 0ac26e05..830af52b 100644 --- a/flight/core/Dispatcher.php +++ b/flight/core/Dispatcher.php @@ -33,9 +33,10 @@ class Dispatcher protected array $events = []; /** - * Method filters. - * - * @var array &$params, mixed &$output): (void|false)>>> + * @var array */ protected array $filters = []; From 0b0737f533a23b9bec83c60f1e9bc90d8224848f Mon Sep 17 00:00:00 2001 From: fadrian06 Date: Fri, 14 Aug 2026 02:50:07 -0400 Subject: [PATCH 05/35] remove $this of return type, it's incompatible with self return type --- flight/core/Dispatcher.php | 8 -------- 1 file changed, 8 deletions(-) diff --git a/flight/core/Dispatcher.php b/flight/core/Dispatcher.php index 830af52b..f0de6c79 100644 --- a/flight/core/Dispatcher.php +++ b/flight/core/Dispatcher.php @@ -90,8 +90,6 @@ public function run(string $name, array $params = []) * @param array &$params * * @throws Exception - * - * @return $this */ protected function runPreFilters(string $eventName, array &$params): self { @@ -145,8 +143,6 @@ protected function runPostFilters(string $eventName, &$output) * * @param string $name Event name. * @param callable(): (void|mixed) $callback Callback function. - * - * @return $this */ public function set(string $name, callable $callback): self { @@ -202,8 +198,6 @@ public function clear(?string $name = null): void * @param string $name Event name * @param 'before'|'after' $type Filter type. * @param callable(array &$params, mixed &$output): (void|false)|callable(mixed &$output): (void|false) $callback - * - * @return $this */ public function hook(string $name, string $type, callable $callback): self { @@ -481,8 +475,6 @@ protected function fixOutputBuffering(): void /** * Resets the object to the initial state. - * - * @return $this */ public function reset(): self { From 988c64ed228d6a6cbd1327f603572eb3d40a5ce7 Mon Sep 17 00:00:00 2001 From: fadrian06 Date: Fri, 14 Aug 2026 02:51:06 -0400 Subject: [PATCH 06/35] rename $containerInterfaceNS -> $containerFQCN and extract InvalidArgumentException $message argument --- flight/core/Dispatcher.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/flight/core/Dispatcher.php b/flight/core/Dispatcher.php index f0de6c79..8cad2c5b 100644 --- a/flight/core/Dispatcher.php +++ b/flight/core/Dispatcher.php @@ -50,17 +50,17 @@ class Dispatcher */ public function setContainerHandler($containerHandler): void { - $containerInterfaceNS = '\Psr\Container\ContainerInterface'; + $containerFQCN = '\\' . Container::class; - if (is_a($containerHandler, $containerInterfaceNS) || is_callable($containerHandler)) { + if (is_a($containerHandler, $containerFQCN) || is_callable($containerHandler)) { $this->containerHandler = $containerHandler; return; } - throw new InvalidArgumentException( - "\$containerHandler must be of type callable or instance $containerInterfaceNS" - ); + $message = "\$containerHandler must be of type callable or instance $containerFQCN"; + + throw new InvalidArgumentException($message); } public function setEngine(Engine $engine): void From 5c89341428e73442bf3ef6068df232fe5bf564f3 Mon Sep 17 00:00:00 2001 From: fadrian06 Date: Fri, 14 Aug 2026 02:51:34 -0400 Subject: [PATCH 07/35] clarify Dispatcher@run parameters and allows to throw errors inside events or filters --- flight/core/Dispatcher.php | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/flight/core/Dispatcher.php b/flight/core/Dispatcher.php index 8cad2c5b..6d427a04 100644 --- a/flight/core/Dispatcher.php +++ b/flight/core/Dispatcher.php @@ -72,11 +72,9 @@ public function setEngine(Engine $engine): void * Dispatches an event. * * @param string $name Event name. - * @param array $params Callback parameters. - * - * @throws Exception If event name isn't found or if event throws an `Exception`. - * - * @return mixed Output of callback + * @param mixed[] $params Event callable parameters. + * @return mixed Output of event callable. + * @throws Throwable If event name isn't found or if event throws an `Throwable`. */ public function run(string $name, array $params = []) { From 188f147c7dc0126c91a2de57933171a61dec30af Mon Sep 17 00:00:00 2001 From: fadrian06 Date: Fri, 14 Aug 2026 02:55:57 -0400 Subject: [PATCH 08/35] invert Dispatcher@setContainerHandler conditional and remove $containerFQCN --- flight/core/Dispatcher.php | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/flight/core/Dispatcher.php b/flight/core/Dispatcher.php index 6d427a04..22f59bfd 100644 --- a/flight/core/Dispatcher.php +++ b/flight/core/Dispatcher.php @@ -50,17 +50,13 @@ class Dispatcher */ public function setContainerHandler($containerHandler): void { - $containerFQCN = '\\' . Container::class; + if (!$containerHandler instanceof Container && !is_callable($containerHandler)) { + $message = "\$containerHandler must be of type callable or instance \\" . Container::class; - if (is_a($containerHandler, $containerFQCN) || is_callable($containerHandler)) { - $this->containerHandler = $containerHandler; - - return; + throw new InvalidArgumentException($message); } - $message = "\$containerHandler must be of type callable or instance $containerFQCN"; - - throw new InvalidArgumentException($message); + $this->containerHandler = $containerHandler; } public function setEngine(Engine $engine): void From 7beabae36da313230e54fe4f0cbd78031e513fd0 Mon Sep 17 00:00:00 2001 From: fadrian06 Date: Sat, 15 Aug 2026 09:26:47 -0400 Subject: [PATCH 09/35] improve Dispatcher description to reflect its responsibility for dispatching named callables --- flight/core/Dispatcher.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/flight/core/Dispatcher.php b/flight/core/Dispatcher.php index 22f59bfd..f986f077 100644 --- a/flight/core/Dispatcher.php +++ b/flight/core/Dispatcher.php @@ -13,10 +13,10 @@ use TypeError; /** - * The Dispatcher class is responsible for dispatching events. Events - * are simply aliases for class methods or functions. The Dispatcher - * allows you to hook other functions to an event that can modify the - * input parameters and/or the output. + * Responsible for dispatching named callables. + * + * The Dispatcher allows you to hook others callables that can modify the named + * callable arguments and/or its return value. * * @license MIT, http://flightphp.com/license * @copyright Copyright (c) 2011, Mike Cao From 1ae2bafeee292f1793b51d80dfe4d5acb733aaa8 Mon Sep 17 00:00:00 2001 From: fadrian06 Date: Sat, 15 Aug 2026 14:22:07 -0400 Subject: [PATCH 10/35] deprecate Dispatcher events property --- flight/core/Dispatcher.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/flight/core/Dispatcher.php b/flight/core/Dispatcher.php index f986f077..ed218f74 100644 --- a/flight/core/Dispatcher.php +++ b/flight/core/Dispatcher.php @@ -29,7 +29,10 @@ class Dispatcher protected ?Throwable $containerException = null; protected ?Engine $engine = null; - /** @var array */ + /** + * @deprecated Don't use this property directly, use `set()`, `get()` and `has()` instead. + * @var array + */ protected array $events = []; /** From 318852b19b1d02d26dad84e47111760d5d839e07 Mon Sep 17 00:00:00 2001 From: fadrian06 Date: Sat, 15 Aug 2026 14:22:31 -0400 Subject: [PATCH 11/35] deprecate Dispatcher filters property --- flight/core/Dispatcher.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/flight/core/Dispatcher.php b/flight/core/Dispatcher.php index ed218f74..c433cedd 100644 --- a/flight/core/Dispatcher.php +++ b/flight/core/Dispatcher.php @@ -36,9 +36,10 @@ class Dispatcher protected array $events = []; /** + * @deprecated Don't use this property, use `hook()` instead. * @var array */ protected array $filters = []; From 7379be00ac22bff9f929b36cb237d6aa5674e69c Mon Sep 17 00:00:00 2001 From: fadrian06 Date: Sat, 15 Aug 2026 14:23:49 -0400 Subject: [PATCH 12/35] simplify Dispatcher events values typehint --- flight/core/Dispatcher.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flight/core/Dispatcher.php b/flight/core/Dispatcher.php index c433cedd..fd7ac3d0 100644 --- a/flight/core/Dispatcher.php +++ b/flight/core/Dispatcher.php @@ -31,7 +31,7 @@ class Dispatcher /** * @deprecated Don't use this property directly, use `set()`, `get()` and `has()` instead. - * @var array + * @var array */ protected array $events = []; From bf3f86d6194dcf2299f276845f0c8239227a0cd8 Mon Sep 17 00:00:00 2001 From: fadrian06 Date: Sat, 15 Aug 2026 14:26:03 -0400 Subject: [PATCH 13/35] reuse Dispatcher@get in Dispatcher@has --- flight/core/Dispatcher.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flight/core/Dispatcher.php b/flight/core/Dispatcher.php index fd7ac3d0..eb176b7d 100644 --- a/flight/core/Dispatcher.php +++ b/flight/core/Dispatcher.php @@ -170,7 +170,7 @@ public function get(string $name): ?callable */ public function has(string $name): bool { - return isset($this->events[$name]); + return $this->get($name) !== null; } /** From bb6932e92ec41a8f48fd4233865ae6534700e8e5 Mon Sep 17 00:00:00 2001 From: fadrian06 Date: Sat, 15 Aug 2026 14:34:38 -0400 Subject: [PATCH 14/35] throw OutOfBoundsException instead of Exception when event name hasn't been set --- flight/core/Dispatcher.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/flight/core/Dispatcher.php b/flight/core/Dispatcher.php index eb176b7d..e60b0155 100644 --- a/flight/core/Dispatcher.php +++ b/flight/core/Dispatcher.php @@ -7,6 +7,7 @@ use Exception; use flight\Engine; use InvalidArgumentException; +use OutOfBoundsException; use Psr\Container\ContainerInterface as Container; use ReflectionFunction; use Throwable; @@ -74,7 +75,8 @@ public function setEngine(Engine $engine): void * @param string $name Event name. * @param mixed[] $params Event callable parameters. * @return mixed Output of event callable. - * @throws Throwable If event name isn't found or if event throws an `Throwable`. + * @throws Throwable If the callable or its filters throw an `Throwable`. + * @throws OutOfBoundsException If callable name is not found. */ public function run(string $name, array $params = []) { @@ -104,14 +106,15 @@ protected function runPreFilters(string $eventName, array &$params): self * @param array &$params * * @return void|mixed - * @throws Exception + * @throws Throwable If the callable or its filters throw an `Throwable`. + * @throws OutOfBoundsException If callable name is not found. */ protected function runEvent(string $eventName, array &$params) { $requestedMethod = $this->get($eventName); if ($requestedMethod === null) { - throw new Exception("Event '$eventName' isn't found."); + throw new OutOfBoundsException("Event '$eventName' isn't found."); } return $this->execute($requestedMethod, $params); From 2682e3298fdb81510e0812a07405e568960c554e Mon Sep 17 00:00:00 2001 From: fadrian06 Date: Sat, 15 Aug 2026 14:42:22 -0400 Subject: [PATCH 15/35] add FilteredCallable to replace Dispatcher events to named callables (self explanatory). - FilteredCallable wraps a callable and helps static analyzers to check if after filters are using the callable return type as output type. - FilteredCallable handles after filters with two parameters (deprecated) and one parameter (new required signature) --- flight/core/FilteredCallable.php | 90 ++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 flight/core/FilteredCallable.php diff --git a/flight/core/FilteredCallable.php b/flight/core/FilteredCallable.php new file mode 100644 index 00000000..00c99fc3 --- /dev/null +++ b/flight/core/FilteredCallable.php @@ -0,0 +1,90 @@ +closure = Closure::fromCallable($callable); + } + + /** + * @param mixed ...$input + * @return Output + * @throws Throwable + */ + public function __invoke(...$input) + { + foreach ($this->beforeFilters as $filter) { + $filterReturnValue = $filter($input); + + if ($filterReturnValue === false) { + break; + } + } + + $closure = $this->closure; + + $output = $closure(...$input); + + foreach ($this->afterFilters as $filter) { + $filterReturnValue = $filter($output); + + if ($filterReturnValue === false) { + break; + } + } + + return $output; + } + + /** @param callable(mixed[] &$input): (void|never|false) $filter */ + public function pushBeforeFilter(callable $filter): void + { + if (!in_array($filter, $this->beforeFilters)) { + $this->beforeFilters[] = $filter; + } + } + + /** @param callable(Output &$output): (void|never|false) $filter */ + public function pushAfterFilter(callable $filter): void + { + if (!in_array($filter, $this->afterFilters)) { + $filterReflectionFunction = new ReflectionFunction($filter); + + if ($filterReflectionFunction->getNumberOfParameters() === 2) { + $filter = static function (&$output) use ($filter) { + static $input = []; + + return $filter($input, $output); + }; + } + + $this->afterFilters[] = $filter; + } + } +} From 5ea5d5b8add7920c2ee77bf82adea5595fc37035 Mon Sep 17 00:00:00 2001 From: fadrian06 Date: Sat, 15 Aug 2026 14:43:17 -0400 Subject: [PATCH 16/35] add Dispatcher namedFilteredCallables property --- flight/core/Dispatcher.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/flight/core/Dispatcher.php b/flight/core/Dispatcher.php index e60b0155..7d4fc92a 100644 --- a/flight/core/Dispatcher.php +++ b/flight/core/Dispatcher.php @@ -36,6 +36,9 @@ class Dispatcher */ protected array $events = []; + /** @var array */ + private array $namedFilteredCallables = []; + /** * @deprecated Don't use this property, use `hook()` instead. * @var array Date: Sat, 15 Aug 2026 14:43:57 -0400 Subject: [PATCH 17/35] update before and filters signature --- flight/core/Dispatcher.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flight/core/Dispatcher.php b/flight/core/Dispatcher.php index 7d4fc92a..46c19753 100644 --- a/flight/core/Dispatcher.php +++ b/flight/core/Dispatcher.php @@ -42,8 +42,8 @@ class Dispatcher /** * @deprecated Don't use this property, use `hook()` instead. * @var array */ protected array $filters = []; From 2d772f3642a3254f042aba4e2d7d384505f2c1db Mon Sep 17 00:00:00 2001 From: fadrian06 Date: Sat, 15 Aug 2026 14:44:38 -0400 Subject: [PATCH 18/35] update Dispatcher description --- flight/core/Dispatcher.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/flight/core/Dispatcher.php b/flight/core/Dispatcher.php index 46c19753..205ec409 100644 --- a/flight/core/Dispatcher.php +++ b/flight/core/Dispatcher.php @@ -16,8 +16,11 @@ /** * Responsible for dispatching named callables. * - * The Dispatcher allows you to hook others callables that can modify the named - * callable arguments and/or its return value. + * The Dispatcher allows you to add filters to a named callable that can modify + * the named callable `input` and/or `output`. + * + * - The `input` is the arguments passed to the named callable. + * - The `output` is the return value of the named callable. * * @license MIT, http://flightphp.com/license * @copyright Copyright (c) 2011, Mike Cao From 80705ec1580efd2a2547cf8978b49e1e5dbc485a Mon Sep 17 00:00:00 2001 From: fadrian06 Date: Sat, 15 Aug 2026 14:47:52 -0400 Subject: [PATCH 19/35] update Dispatcher@run, set, get, has, hook, clear and reset to use the namedFilteredCallable property --- flight/core/Dispatcher.php | 75 +++++++++++++++++++++++++------------- 1 file changed, 50 insertions(+), 25 deletions(-) diff --git a/flight/core/Dispatcher.php b/flight/core/Dispatcher.php index 205ec409..f7081ec6 100644 --- a/flight/core/Dispatcher.php +++ b/flight/core/Dispatcher.php @@ -76,22 +76,35 @@ public function setEngine(Engine $engine): void } /** - * Dispatches an event. + * Runs a named callable and its filters. * - * @param string $name Event name. - * @param mixed[] $params Event callable parameters. - * @return mixed Output of event callable. + * @param string $name Callable name. + * @param mixed[] $params Callable input. + * @return void|never|mixed Callable output. * @throws Throwable If the callable or its filters throw an `Throwable`. * @throws OutOfBoundsException If callable name is not found. */ public function run(string $name, array $params = []) { - $this->runPreFilters($name, $params); + if (get_called_class() !== self::class) { + /* If dispatcher was extended, use the possibly overridden methods + for pre/post filters and event execution. */ + $this->runPreFilters($name, $params); $output = $this->runEvent($name, $params); return $this->runPostFilters($name, $output); } + // Executes the FilteredCallable, responsible of running its filters. + $filteredCallable = $this->get($name); + + if (!$filteredCallable) { + throw new OutOfBoundsException("Event '$name' isn't found."); + } + + return $filteredCallable(...$params); + } + /** * @param array &$params * @@ -146,36 +159,34 @@ protected function runPostFilters(string $eventName, &$output) } /** - * Assigns a callback to an event. + * Assigns a name to a callable. * - * @param string $name Event name. - * @param callable(): (void|mixed) $callback Callback function. + * @param string $name Callable name. + * @param callable $callback Callable. */ public function set(string $name, callable $callback): self { $this->events[$name] = $callback; + $this->namedFilteredCallables[$name] = new FilteredCallable($callback); return $this; } /** - * Gets an assigned callback. - * - * @param string $name Event name. + * Returns a callable by its name. * - * @return null|(callable(): (void|mixed)) $callback Callback function. + * @param string $name Callable name. + * @return ?FilteredCallable */ public function get(string $name): ?callable { - return $this->events[$name] ?? null; + return $this->namedFilteredCallables[$name] ?? $this->events[$name] ?? null; } /** - * Checks if an event has been set. + * Checks if a callable exists by its name. * - * @param string $name Event name. - * - * @return bool If event exists or doesn't exists. + * @param string $name Callable name. */ public function has(string $name): bool { @@ -183,15 +194,18 @@ public function has(string $name): bool } /** - * Clears an event. If no name is given, all events will be removed. + * Clears a callable and its filters by its name. + * + * If no name is provided, clears all callables names and theirs filters. * - * @param ?string $name Event name. + * @param ?string $name Callable name. */ public function clear(?string $name = null): void { if ($name !== null) { unset($this->events[$name]); unset($this->filters[$name]); + unset($this->namedFilteredCallables[$name]); return; } @@ -200,11 +214,11 @@ public function clear(?string $name = null): void } /** - * Hooks a callback to an event. + * Adds a filter to a callable. * - * @param string $name Event name + * @param string $name Callable name. * @param 'before'|'after' $type Filter type. - * @param callable(array &$params, mixed &$output): (void|false)|callable(mixed &$output): (void|false) $callback + * @param callable(mixed[] &$params): (void|never|false)|callable(mixed &$output): (void|never|false) $callback */ public function hook(string $name, string $type, callable $callback): self { @@ -228,6 +242,18 @@ public function hook(string $name, string $type, callable $callback): self $this->filters[$name][$type][] = $callback; + $filteredCallable = $this->get($name); + + if ($filteredCallable) { + if ($type === self::FILTER_BEFORE) { + $filteredCallable->pushBeforeFilter($callback); + } + + if ($type === self::FILTER_AFTER) { + $filteredCallable->pushAfterFilter($callback); + } + } + return $this; } @@ -480,13 +506,12 @@ protected function fixOutputBuffering(): void } } - /** - * Resets the object to the initial state. - */ + /** Resets the dispatcher state by clearing all events, filters, and named filtered callables. */ public function reset(): self { $this->events = []; $this->filters = []; + $this->namedFilteredCallables = []; return $this; } From b0203d861c7aeb0dc7f2df19a218ad69aed7e02d Mon Sep 17 00:00:00 2001 From: fadrian06 Date: Sat, 15 Aug 2026 14:49:47 -0400 Subject: [PATCH 20/35] deprecate Dispatcher@runPreFilters, runEvent, runPostFilters and filter. Now filters are handled by each FilteredCallable. --- flight/core/Dispatcher.php | 43 ++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/flight/core/Dispatcher.php b/flight/core/Dispatcher.php index f7081ec6..b1c80d3a 100644 --- a/flight/core/Dispatcher.php +++ b/flight/core/Dispatcher.php @@ -106,9 +106,10 @@ public function run(string $name, array $params = []) } /** - * @param array &$params - * - * @throws Exception + * @deprecated Don't override this method. + * @param string $eventName Callable name. + * @param mixed[] &$params Callable input. + * @throws Throwable If any of the callable filters throw an `Throwable`. */ protected function runPreFilters(string $eventName, array &$params): self { @@ -122,9 +123,10 @@ protected function runPreFilters(string $eventName, array &$params): self } /** - * @param array &$params - * - * @return void|mixed + * @deprecated Don't override this method. + * @param string $eventName Callable name. + * @param mixed[] &$params Callable input. + * @return void|never|mixed * @throws Throwable If the callable or its filters throw an `Throwable`. * @throws OutOfBoundsException If callable name is not found. */ @@ -140,10 +142,11 @@ protected function runEvent(string $eventName, array &$params) } /** - * @param mixed &$output - * - * @return mixed - * @throws Exception + * @deprecated Don't override this method. + * @template Output of mixed + * @param Output &$output Callable output. + * @return Output Callable output. + * @throws Throwable If any of the callable filters throw an `Throwable`. */ protected function runPostFilters(string $eventName, &$output) { @@ -258,23 +261,23 @@ public function hook(string $name, string $type, callable $callback): self } /** - * Executes a chain of method filters. + * Executes a list of callable filters. * - * @param array &$params, mixed &$output): (void|false)> $filters - * Chain of filters. - * @param array $params Method parameters. - * @param mixed $output Method output. - * - * @throws Exception If an event throws an `Exception` or if `$filters` contains an invalid filter. + * @deprecated This method will be removed. + * @param (callable(mixed[] &$params, mixed &$output): (void|never|false))[] $filters Callable filters. + * @param mixed[] $params Callable input. + * @param mixed $output Callable output. + * @throws Throwable If any of the callable filters throw an `Throwable`. + * @throws InvalidArgumentException If any of the callable filters is not a `callable`. */ public function filter(array $filters, array &$params, &$output): void { - foreach ($filters as $key => $callback) { - if (!is_callable($callback)) { + foreach ($filters as $key => $filter) { + if (!is_callable($filter)) { throw new InvalidArgumentException("Invalid callable \$filters[$key]."); } - $continue = $callback($params, $output); + $continue = $filter($params, $output); if ($continue === false) { break; From ddb57620a0296dd95575536e3e2dc0e5d70997e2 Mon Sep 17 00:00:00 2001 From: fadrian06 Date: Sat, 15 Aug 2026 15:42:24 -0400 Subject: [PATCH 21/35] add default template values in FilteredCallable --- flight/core/FilteredCallable.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flight/core/FilteredCallable.php b/flight/core/FilteredCallable.php index 00c99fc3..9e686cdc 100644 --- a/flight/core/FilteredCallable.php +++ b/flight/core/FilteredCallable.php @@ -9,8 +9,8 @@ use Throwable; /** - * @template CallableWithoutFilters of Closure - * @template Output of mixed + * @template CallableWithoutFilters of Closure = Closure + * @template Output = mixed */ final class FilteredCallable { From 740524490f34ab290fc1b8fc18ff7767d065ae40 Mon Sep 17 00:00:00 2001 From: fadrian06 Date: Sun, 16 Aug 2026 00:49:15 -0400 Subject: [PATCH 22/35] extract Dispatcher::CALLABLE_STRING_OPERATORS --- flight/core/Dispatcher.php | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/flight/core/Dispatcher.php b/flight/core/Dispatcher.php index b1c80d3a..2a1c9a14 100644 --- a/flight/core/Dispatcher.php +++ b/flight/core/Dispatcher.php @@ -29,6 +29,7 @@ class Dispatcher { public const FILTER_BEFORE = 'before'; public const FILTER_AFTER = 'after'; + private const CALLABLE_STRING_OPERATORS = ['->', '::']; protected ?Throwable $containerException = null; protected ?Engine $engine = null; @@ -305,21 +306,29 @@ public function execute($callback, array &$params = []) } /** - * Parses a string into a class and method. + * Parses a string with an unloaded class and method into an array. * - * @param string $classAndMethod Class and method - * - * @return array{0: class-string|object, 1: string} Class and method + * @deprecated Use `execute()` instead. + * @param string $classAndMethod An string with an unloaded class and method, + * like `ClassName::method` or `ClassName->method`. + * @return array{class-string, string} + * @throws InvalidArgumentException If the string is not in a valid format. */ public function parseStringClassAndMethod(string $classAndMethod): array { - $classParts = explode('->', $classAndMethod); + foreach (self::CALLABLE_STRING_OPERATORS as $operator) { + $classAndMethod = explode($operator, $classAndMethod); + + if (count($classAndMethod) === 2) { + return [$classAndMethod[0], $classAndMethod[1]]; + } - if (count($classParts) === 1) { - $classParts = explode('::', $classParts[0]); + [$classAndMethod] = $classAndMethod; } - return $classParts; + $message = "Invalid string format '$classAndMethod', use 'ClassName::method' or 'ClassName->method'."; + + throw new InvalidArgumentException($message); } /** From 35a6bbabed214facb8803af8353e9dca056c9348 Mon Sep 17 00:00:00 2001 From: fadrian06 Date: Sun, 16 Aug 2026 00:52:41 -0400 Subject: [PATCH 23/35] update Dispatcher@fixOutputBuffering description --- flight/core/Dispatcher.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flight/core/Dispatcher.php b/flight/core/Dispatcher.php index 2a1c9a14..39a9be1d 100644 --- a/flight/core/Dispatcher.php +++ b/flight/core/Dispatcher.php @@ -509,7 +509,7 @@ public function mustUseContainer($class): bool ); } - /** Because this could throw an exception in the middle of an output buffer, */ + /** Fixes output buffering issues when an exception is thrown. */ protected function fixOutputBuffering(): void { // Cause PHPUnit has 1 level of output buffering by default From 4afb553a9672f3851195a62cda639b4c0cf998c4 Mon Sep 17 00:00:00 2001 From: fadrian06 Date: Sun, 16 Aug 2026 00:53:43 -0400 Subject: [PATCH 24/35] simplify Dispatcher@mustUseContainer --- flight/core/Dispatcher.php | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/flight/core/Dispatcher.php b/flight/core/Dispatcher.php index 39a9be1d..cf9fc399 100644 --- a/flight/core/Dispatcher.php +++ b/flight/core/Dispatcher.php @@ -495,18 +495,24 @@ public function resolveContainerClass(string $class, array &$params) } /** - * Checks to see if a container should be used or not. + * Checks if the class must be resolved by the container. * - * @param string|object $class the class to verify - * - * @return boolean + * @deprecated This method will be removed. + * @param class-string|object $class Class name or object. */ public function mustUseContainer($class): bool { - return $this->containerHandler !== null && ( - (is_object($class) === true && strpos(get_class($class), 'flight\\') === false) - || is_string($class) - ); + $container = $this->containerHandler; + + if (is_object($class)) { + $class = get_class($class); + } + + if ($container instanceof Container && $container->has($class)) { + return true; + } + + return is_callable($container); } /** Fixes output buffering issues when an exception is thrown. */ From cbc1cb30befa366e7cf529d2c8704488f7f3cf57 Mon Sep 17 00:00:00 2001 From: fadrian06 Date: Sun, 16 Aug 2026 00:55:56 -0400 Subject: [PATCH 25/35] refactor Dispatcher@execute --- flight/core/Dispatcher.php | 264 +++++++++++++++++++++---------------- 1 file changed, 148 insertions(+), 116 deletions(-) diff --git a/flight/core/Dispatcher.php b/flight/core/Dispatcher.php index cf9fc399..cd49555d 100644 --- a/flight/core/Dispatcher.php +++ b/flight/core/Dispatcher.php @@ -4,14 +4,13 @@ namespace flight\core; -use Exception; use flight\Engine; use InvalidArgumentException; use OutOfBoundsException; +use Psr\Container\ContainerExceptionInterface; use Psr\Container\ContainerInterface as Container; use ReflectionFunction; use Throwable; -use TypeError; /** * Responsible for dispatching named callables. @@ -91,10 +90,10 @@ public function run(string $name, array $params = []) /* If dispatcher was extended, use the possibly overridden methods for pre/post filters and event execution. */ $this->runPreFilters($name, $params); - $output = $this->runEvent($name, $params); + $output = $this->runEvent($name, $params); - return $this->runPostFilters($name, $output); - } + return $this->runPostFilters($name, $output); + } // Executes the FilteredCallable, responsible of running its filters. $filteredCallable = $this->get($name); @@ -126,12 +125,12 @@ protected function runPreFilters(string $eventName, array &$params): self /** * @deprecated Don't override this method. * @param string $eventName Callable name. - * @param mixed[] &$params Callable input. + * @param mixed[] $params Callable input. * @return void|never|mixed * @throws Throwable If the callable or its filters throw an `Throwable`. * @throws OutOfBoundsException If callable name is not found. */ - protected function runEvent(string $eventName, array &$params) + protected function runEvent(string $eventName, array $params) { $requestedMethod = $this->get($eventName); @@ -287,22 +286,50 @@ public function filter(array $filters, array &$params, &$output): void } /** - * Executes a callback function. - * - * @param callable-string|(callable(): mixed)|array{class-string|object, string} $callback - * Callback function. - * @param array $params Function parameters. + * Executes a callable. * - * @return mixed Function results. - * @throws Exception If `$callback` also throws an `Exception`. + * @param callable|array{class-string|object, string}|string $callback Callable. + * @param mixed[] $params Callable input. + * @return mixed Callable output. + * @throws Throwable If the callable throws an `Throwable`. */ - public function execute($callback, array &$params = []) + public function execute($callback, array $params = []) { - if (is_string($callback) === true && (strpos($callback, '->') !== false || strpos($callback, '::') !== false)) { + $container = $this->containerHandler; + + $this->verifyValidFunction($callback); + + if (is_string($callback)) { $callback = $this->parseStringClassAndMethod($callback); } - return $this->invokeCallable($callback, $params); + if (is_callable($callback) && !is_array($callback)) { + return $callback(...$params); + } + + [$class, $method] = $callback; + $object = null; + + if (is_object($class)) { + return $class->$method(...$params); + } + + if ($this->mustUseContainer($class)) { + $object = $this->resolveContainerClass($class, $params); + + if (is_object($object)) { + $class = $object; + } + } + + $this->verifyValidClassCallable($class, $method, $object); + + // Class is a string, and method exists, create the object by hand and inject only the Engine + if (is_string($class)) { + $class = new $class($this->engine); + } + + return call_user_func_array([$class, $method], $params); } /** @@ -332,122 +359,128 @@ public function parseStringClassAndMethod(string $classAndMethod): array } /** - * Calls a function. - * - * @param callable $func Name of function to call. - * @param array &$params Function parameters. + * Executes a callable. * - * @return mixed Function results. - * @deprecated 3.7.0 Use invokeCallable instead + * @deprecated Use execute instead. + * @param callable $func Callable. + * @param mixed[] $params Callable input. + * @return mixed Callable output. + * @throws Throwable If the callable throws an `Throwable`. */ - public function callFunction(callable $func, array &$params = []) + public function callFunction(callable $func, array $params = []) { - return $this->invokeCallable($func, $params); + return $this->execute($func, $params); } /** - * Invokes a method. - * - * @param array{0: class-string|object, 1: string} $func Class method. - * @param array &$params Class method parameters. + * Executes a callable. * - * @return mixed Function results. - * @throws TypeError For nonexistent class name. - * @deprecated 3.7.0 Use invokeCallable instead. + * @deprecated Use execute instead. + * @param array{class-string|object, string} $func Callable. + * @param mixed[] $params Callable input. + * @return mixed Callable output. + * @throws Throwable If the callable throws an `Throwable`. */ - public function invokeMethod(array $func, array &$params = []) + public function invokeMethod(array $func, array $params = []) { - return $this->invokeCallable($func, $params); + return $this->execute($func, $params); } /** - * Invokes a callable (anonymous function or Class->method). + * Executes a callable. * - * @param array{0: class-string|object, 1: string}|callable $func Class method. - * @param array &$params Class method parameters. - * - * @return mixed Function results. - * @throws TypeError For nonexistent class name. - * @throws InvalidArgumentException If the constructor requires parameters. - * @version 3.7.0 + * @deprecated Use execute instead. + * @param callable|array{class-string|object, string}|string $func Callable. + * @param mixed[] $params Callable input. + * @return mixed Callable output. + * @throws Throwable If the callable throws an `Throwable`. */ - public function invokeCallable($func, array &$params = []) + public function invokeCallable($func, array $params = []) { - // If this is a directly callable function, call it - if (is_array($func) === false) { - $this->verifyValidFunction($func); + return $this->execute($func, $params); + } - return call_user_func_array($func, $params); + /** + * Verifies if the provided function is valid callable. + * + * @deprecated This method will be removed. + * @param callable|array{class-string|object, string}|string $callback Callable. + * @throws InvalidArgumentException If the function is not valid callable. + */ + protected function verifyValidFunction($callback): void + { + /* + ✔️ function () {} + ✔️ Closure + ✔️ Object that implements __invoke + ✔️ 'existingFunction' + ✔️ 'ExistingClass::existingAccessibleStaticMethod' + ✔️ ['ExistingClass', 'existingAccessibleStaticMethod'] + ✔️ [$object, 'existingAccessibleMethod'] + ✔️ [$object, 'existingAccessibleStaticMethod'] + */ + if (is_callable($callback)) { + return; } - [$class, $method] = $func; + /* + ✔️ ['UnloadedClass', 'method'] + ✔️ ['UnloadedClass', 'staticMethod'] + */ + if ( + is_array($callback) + && count($callback) === 2 + && is_string($callback[0]) + && is_string($callback[1]) + ) { + return; + } - $mustUseTheContainer = $this->mustUseContainer($class); + /* + ✔️ 'UnloadedClass::method' + ✔️ 'UnloadedClass->method' + */ + if (is_string($callback)) { + foreach (self::CALLABLE_STRING_OPERATORS as $operator) { + $callback = explode($operator, $callback); - if ($mustUseTheContainer === true) { - $resolvedClass = $this->resolveContainerClass($class, $params); + if (count($callback) === 2) { + return; + } - if ($resolvedClass) { - $class = $resolvedClass; + [$callback] = $callback; } } - $this->verifyValidClassCallable($class, $method, $resolvedClass ?? null); - - // Class is a string, and method exists, create the object by hand and inject only the Engine - if (is_string($class)) { - $class = new $class($this->engine); - } - - return call_user_func_array([$class, $method], $params); - } - - /** - * Handles invalid callback types. - * - * @param callable-string|(callable(): mixed)|array{0: class-string|object, 1: string} $callback - * Callback function. - * - * @throws InvalidArgumentException If `$callback` is an invalid type. - */ - protected function verifyValidFunction($callback): void - { - if (is_string($callback) && !function_exists($callback)) { - throw new InvalidArgumentException('Invalid callback specified.'); - } + throw new InvalidArgumentException('Invalid callback specified.'); } /** - * Verifies if the provided class and method are valid callable. - * - * @param class-string|object $class The class name. - * @param string $method The method name. - * @param object|null $resolvedClass The resolved class. - * - * @throws Exception If the class or method is not found. + * @deprecated This method will be removed. + * @template T of object + * @param class-string|T $class The class name or object. + * @param ?T $resolvedClass A class instance. + * @return void|never + * @throws InvalidArgumentException If the class or method is not found. + * @throws Throwable If the container throws an exception. */ - protected function verifyValidClassCallable($class, $method, $resolvedClass): void + protected function verifyValidClassCallable($class, string $method, ?object $resolvedClass): void { $exception = null; // Final check to make sure it's actually a class and a method, or throw an error - if (is_object($class) === false && class_exists($class) === false) { - $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) { + if (!is_object($class) && !class_exists($class)) { + $message = "Class '$class' not found. Is it being correctly autoloaded with Flight::path()?"; + $exception = new InvalidArgumentException($message); + } elseif ($this->containerException) { $exception = $this->containerException; - - // Class is there, but no method - } elseif (is_object($class) === true && method_exists($class, $method) === false) { - $classNamespace = get_class($class); - $exception = new Exception("Class found, but method '$classNamespace::$method' not found."); + } elseif (is_object($class) && !method_exists($class, $method)) { + $fqcn = get_class($class); + $exception = new InvalidArgumentException("Class found, but method '$fqcn::$method' not found."); } - if ($exception !== null) { + if ($exception) { $this->fixOutputBuffering(); throw $exception; @@ -455,39 +488,38 @@ protected function verifyValidClassCallable($class, $method, $resolvedClass): vo } /** - * Resolves the container class. + * Resolves a class from the container. * - * @param class-string $class Class name. - * @param array &$params Class constructor parameters. - * - * @return ?object Class object. + * @deprecated This method will be removed. + * @template T of object + * @param class-string $class The class name. + * @param mixed[] $params Class constructor arguments. + * @return ?T The resolved class instance, or null if not found. */ - public function resolveContainerClass(string $class, array &$params) + public function resolveContainerClass(string $class, array $params): ?object { - // PSR-11 - if (is_a($this->containerHandler, '\Psr\Container\ContainerInterface')) { + $container = $this->containerHandler; + + if ($container instanceof Container) { try { - return $this->containerHandler->get($class); - } catch (Throwable $exception) { + return $container->get($class); + } catch (ContainerExceptionInterface $exception) { + $this->containerException = $exception; + return null; } } - // Just a callable where you configure the behavior (Dice, PHP-DI, etc.) - if (is_callable($this->containerHandler)) { - /* This is to catch all the error that could be thrown by whatever - container you are using */ + if (is_callable($container)) { try { - return ($this->containerHandler)($class, $params); - - // could not resolve a class for some reason - } catch (Exception $exception) { + return $container($class, $params); + } catch (Throwable $throwable) { // If the container throws an exception, we need to catch it // and store it somewhere. If we just let it throw itself, it // doesn't properly close the output buffers and can cause other // issues. // This is thrown in the verifyValidClassCallable method. - $this->containerException = $exception; + $this->containerException = $throwable; } } From 27598bcff51d4f64de2dd287cad7dbde60bb0591 Mon Sep 17 00:00:00 2001 From: fadrian06 Date: Sun, 16 Aug 2026 00:56:53 -0400 Subject: [PATCH 26/35] ignore spacing after control structures open braces --- phpcs.xml.dist | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/phpcs.xml.dist b/phpcs.xml.dist index 6694874d..e61a6556 100644 --- a/phpcs.xml.dist +++ b/phpcs.xml.dist @@ -6,6 +6,10 @@ flight tests - + + + + + From 9e08573896913f80c97c902c57959952e13b0212 Mon Sep 17 00:00:00 2001 From: fadrian06 Date: Sun, 16 Aug 2026 00:58:04 -0400 Subject: [PATCH 27/35] rename Dispatcher namedFilteredCallables -> namedCallables --- flight/core/Dispatcher.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/flight/core/Dispatcher.php b/flight/core/Dispatcher.php index cd49555d..c3d3a79c 100644 --- a/flight/core/Dispatcher.php +++ b/flight/core/Dispatcher.php @@ -40,7 +40,7 @@ class Dispatcher protected array $events = []; /** @var array */ - private array $namedFilteredCallables = []; + private array $namedCallables = []; /** * @deprecated Don't use this property, use `hook()` instead. @@ -170,7 +170,7 @@ protected function runPostFilters(string $eventName, &$output) public function set(string $name, callable $callback): self { $this->events[$name] = $callback; - $this->namedFilteredCallables[$name] = new FilteredCallable($callback); + $this->namedCallables[$name] = new FilteredCallable($callback); return $this; } @@ -183,7 +183,7 @@ public function set(string $name, callable $callback): self */ public function get(string $name): ?callable { - return $this->namedFilteredCallables[$name] ?? $this->events[$name] ?? null; + return $this->namedCallables[$name] ?? $this->events[$name] ?? null; } /** @@ -208,7 +208,7 @@ public function clear(?string $name = null): void if ($name !== null) { unset($this->events[$name]); unset($this->filters[$name]); - unset($this->namedFilteredCallables[$name]); + unset($this->namedCallables[$name]); return; } @@ -561,7 +561,7 @@ public function reset(): self { $this->events = []; $this->filters = []; - $this->namedFilteredCallables = []; + $this->namedCallables = []; return $this; } From 6afe25ddb5be06f55cfaf69c727cacb82bb253a3 Mon Sep 17 00:00:00 2001 From: fadrian06 Date: Sun, 16 Aug 2026 01:01:15 -0400 Subject: [PATCH 28/35] simplify object creation without container --- flight/core/Dispatcher.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/flight/core/Dispatcher.php b/flight/core/Dispatcher.php index c3d3a79c..5cd142f8 100644 --- a/flight/core/Dispatcher.php +++ b/flight/core/Dispatcher.php @@ -324,12 +324,11 @@ public function execute($callback, array $params = []) $this->verifyValidClassCallable($class, $method, $object); - // Class is a string, and method exists, create the object by hand and inject only the Engine if (is_string($class)) { $class = new $class($this->engine); } - return call_user_func_array([$class, $method], $params); + return $class->$method(...$params); } /** From 05831b39cb376910f2b812425300c98b6e502c99 Mon Sep 17 00:00:00 2001 From: fadrian06 Date: Sun, 16 Aug 2026 01:05:03 -0400 Subject: [PATCH 29/35] use always global namespace prefix --- flight/core/Dispatcher.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flight/core/Dispatcher.php b/flight/core/Dispatcher.php index 5cd142f8..e7aaacc1 100644 --- a/flight/core/Dispatcher.php +++ b/flight/core/Dispatcher.php @@ -57,7 +57,7 @@ class Dispatcher /** * @param Container|(callable(class-string $classString, mixed[] $params): ?object) $containerHandler * @throws InvalidArgumentException - * If $containerHandler is not a `callable` or instance of `Psr\Container\ContainerInterface`. + * If $containerHandler is not a `callable` or instance of `\Psr\Container\ContainerInterface`. */ public function setContainerHandler($containerHandler): void { From bdd5bdcc24de02baba7180083c72c4ea809771b7 Mon Sep 17 00:00:00 2001 From: fadrian06 Date: Sun, 16 Aug 2026 01:06:33 -0400 Subject: [PATCH 30/35] fix Dispatcher@get return type --- flight/core/Dispatcher.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flight/core/Dispatcher.php b/flight/core/Dispatcher.php index e7aaacc1..3bf27d48 100644 --- a/flight/core/Dispatcher.php +++ b/flight/core/Dispatcher.php @@ -179,7 +179,7 @@ public function set(string $name, callable $callback): self * Returns a callable by its name. * * @param string $name Callable name. - * @return ?FilteredCallable + * @return ?callable */ public function get(string $name): ?callable { From e48b976aac40cc2266911d0012e480be710b5e14 Mon Sep 17 00:00:00 2001 From: fadrian06 Date: Sun, 16 Aug 2026 01:08:07 -0400 Subject: [PATCH 31/35] fix phpstan message about Dispatcher@get return type --- flight/core/Dispatcher.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flight/core/Dispatcher.php b/flight/core/Dispatcher.php index 3bf27d48..6b5f81b8 100644 --- a/flight/core/Dispatcher.php +++ b/flight/core/Dispatcher.php @@ -247,7 +247,7 @@ public function hook(string $name, string $type, callable $callback): self $filteredCallable = $this->get($name); - if ($filteredCallable) { + if ($filteredCallable instanceof FilteredCallable) { if ($type === self::FILTER_BEFORE) { $filteredCallable->pushBeforeFilter($callback); } From cfa87bee4e17e07701758c37bf6755d70ddd7368 Mon Sep 17 00:00:00 2001 From: fadrian06 Date: Sun, 16 Aug 2026 01:11:51 -0400 Subject: [PATCH 32/35] remove unneccesary references --- flight/core/Dispatcher.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flight/core/Dispatcher.php b/flight/core/Dispatcher.php index 6b5f81b8..52a6ec1f 100644 --- a/flight/core/Dispatcher.php +++ b/flight/core/Dispatcher.php @@ -265,8 +265,8 @@ public function hook(string $name, string $type, callable $callback): self * * @deprecated This method will be removed. * @param (callable(mixed[] &$params, mixed &$output): (void|never|false))[] $filters Callable filters. - * @param mixed[] $params Callable input. - * @param mixed $output Callable output. + * @param mixed[] &$params Callable input. + * @param mixed &$output Callable output. * @throws Throwable If any of the callable filters throw an `Throwable`. * @throws InvalidArgumentException If any of the callable filters is not a `callable`. */ From 8e168e648bf2bd397ec703081b21937bfca12de1 Mon Sep 17 00:00:00 2001 From: fadrian06 Date: Sun, 16 Aug 2026 01:12:07 -0400 Subject: [PATCH 33/35] remove unused $container --- flight/core/Dispatcher.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/flight/core/Dispatcher.php b/flight/core/Dispatcher.php index 52a6ec1f..00f74c31 100644 --- a/flight/core/Dispatcher.php +++ b/flight/core/Dispatcher.php @@ -295,8 +295,6 @@ public function filter(array $filters, array &$params, &$output): void */ public function execute($callback, array $params = []) { - $container = $this->containerHandler; - $this->verifyValidFunction($callback); if (is_string($callback)) { From 17ed78763ff302f8eb1d57ff2879966828a95038 Mon Sep 17 00:00:00 2001 From: fadrian06 Date: Sun, 16 Aug 2026 01:12:22 -0400 Subject: [PATCH 34/35] add more @template --- flight/core/Dispatcher.php | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/flight/core/Dispatcher.php b/flight/core/Dispatcher.php index 00f74c31..53f193fc 100644 --- a/flight/core/Dispatcher.php +++ b/flight/core/Dispatcher.php @@ -288,7 +288,8 @@ public function filter(array $filters, array &$params, &$output): void /** * Executes a callable. * - * @param callable|array{class-string|object, string}|string $callback Callable. + * @template T of object + * @param callable|array{class-string|T, string}|string $callback Callable. * @param mixed[] $params Callable input. * @return mixed Callable output. * @throws Throwable If the callable throws an `Throwable`. @@ -373,7 +374,8 @@ public function callFunction(callable $func, array $params = []) * Executes a callable. * * @deprecated Use execute instead. - * @param array{class-string|object, string} $func Callable. + * @template T of object + * @param array{class-string|T, string} $func Callable. * @param mixed[] $params Callable input. * @return mixed Callable output. * @throws Throwable If the callable throws an `Throwable`. @@ -387,7 +389,8 @@ public function invokeMethod(array $func, array $params = []) * Executes a callable. * * @deprecated Use execute instead. - * @param callable|array{class-string|object, string}|string $func Callable. + * @template T of object + * @param callable|array{class-string|T, string}|string $func Callable. * @param mixed[] $params Callable input. * @return mixed Callable output. * @throws Throwable If the callable throws an `Throwable`. @@ -401,7 +404,8 @@ public function invokeCallable($func, array $params = []) * Verifies if the provided function is valid callable. * * @deprecated This method will be removed. - * @param callable|array{class-string|object, string}|string $callback Callable. + * @template T of object + * @param callable|array{class-string|T, string}|string $callback Callable. * @throws InvalidArgumentException If the function is not valid callable. */ protected function verifyValidFunction($callback): void @@ -527,7 +531,8 @@ public function resolveContainerClass(string $class, array $params): ?object * Checks if the class must be resolved by the container. * * @deprecated This method will be removed. - * @param class-string|object $class Class name or object. + * @template T of object + * @param class-string|T $class Class name or object. */ public function mustUseContainer($class): bool { From 7482506270abf83da0b03e4c2b1157515156cc41 Mon Sep 17 00:00:00 2001 From: fadrian06 Date: Sun, 16 Aug 2026 01:14:29 -0400 Subject: [PATCH 35/35] remove EngineTemplate --- flight/Engine.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flight/Engine.php b/flight/Engine.php index 372f9523..36666335 100644 --- a/flight/Engine.php +++ b/flight/Engine.php @@ -57,7 +57,7 @@ * @method void lastModified(int $time) * @method void download(string $filePath) * - * @phpstan-method void registerContainerHandler(ContainerInterface|callable(class-string $id, array $params): ?EngineTemplate $containerHandler) + * @phpstan-method void registerContainerHandler(ContainerInterface|callable(class-string $id, array $params): ?object $containerHandler) * @phpstan-method Route route(string $pattern, callable|string|array{0: class-string, 1: string} $callback, bool $pass_route = false, string $alias = '') * @phpstan-method void group(string $pattern, callable $callback, (class-string|callable|array{0: class-string, 1: string})[] $group_middlewares = []) * @phpstan-method Route post(string $pattern, callable|string|array{0: class-string, 1: string} $callback, bool $pass_route = false, string $alias = '') @@ -117,7 +117,7 @@ class Engine /** Class loader. */ protected Loader $loader; - /** @var Dispatcher Method and class dispatcher. */ + /** Method and class dispatcher. */ protected Dispatcher $dispatcher; /** Event dispatcher. */