Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ The format is based on Keep a Changelog.

### Added
- Added `Request::expectsJson()`: true when the `Accept` header lists a JSON media type (`application/json` or a `+json` type, parameters and case ignored), or for an AJAX request that accepts any content type (#581)
- Added boot stage lifecycle events: stages may declare `BEFORE`/`AFTER` event names and the boot `Pipeline` dispatches them with the `AppContext` as a named payload. The environment, config, error handler, HTTP, and modules stages declare `boot.<stage>.before` and `boot.<stage>.after` (#536)
- Added lifecycle events for the web and console runtime: the runtime stages declare `web.preflight`, `web.route`, `web.not_found`, `web.prepare`, `web.dispatch`, `web.send` and `console.command`, each with `.before` and `.after`, and the `Pipeline` dispatches them with the runtime context as a named `context` payload. A stage that does not run dispatches nothing, a stage that throws dispatches no `.after`, and the closing `web.send` events also follow an OPTIONS or not found response. Boot stages declare no events. `console.command.after` fires only with a Symfony application that does not exit after the command (#536, #538)
- Added adapter-based Lang provider support with `DeepL` and `Google Translate` adapters plus shared remote request/caching infrastructure (#533)

### Changed
- **BREAKING:** Reworked the app boot and runtime into pipelines of stages that the adapters run in explicit steps (#537, #575, #576, #579, #583, #589):
- **BREAKING:** Reworked the app boot and runtime into pipelines of stages that the adapters run in explicit steps (#537, #538, #575, #576, #579, #583, #589):
- `AppInterface` gains `boot()`. The adapter constructors only store the context, and `AppFactory` calls `boot()` right after creating the adapter, so `AppFactory::create(...)->start()` is unchanged. Code that creates an adapter directly must call `boot()` before `start()`.
- Removed the `AppAdapter` base class and the `WebAppTrait` and `ConsoleAppTrait` traits: `WebAppAdapter` and `ConsoleAppAdapter` implement `AppInterface` directly and moved to `Quantum\App\Adapters\Web` and `Quantum\App\Adapters\Console`. The boot stages moved to `Quantum\App\Stages\Boot`. Code that goes through `AppFactory` is unaffected; code that references these classes directly must update its imports.
- Removed the `AppAdapter` base class and the `WebAppTrait` and `ConsoleAppTrait` traits: `WebAppAdapter` and `ConsoleAppAdapter` implement `AppInterface` directly and moved to `Quantum\App\Adapters\Web` and `Quantum\App\Adapters\Console`. The stages are grouped by the adapter that runs them: the boot stages both adapters run in `Quantum\App\Stages\Boot`, the others in `Quantum\App\Adapters\Web\Stages\Boot`, `...\Web\Stages\Runtime`, `...\Console\Stages\Boot` and `...\Console\Stages\Runtime`. Code that goes through `AppFactory` is unaffected; code that references these classes directly must update its imports.
- Replaced `BootPipeline` and `BootStageInterface` with one generic `Quantum\App\Pipeline<TContext>`, which runs `StageInterface<TContext>` stages in order and dispatches the `BEFORE`/`AFTER` events they declare. The context type is a PHPDoc template checked by PHPStan: a stage states it with `@implements StageInterface<AppContext>` (or `WebContext`, `ConsoleContext`) and takes `process(AppContext $context)`. A stage that calls `stop()` on the context ends the run; an optional closing stage, the second constructor argument, runs last, also after a stop, but not after a stage throws.
- `AppContext` keeps `getBaseDir()`, `getContainer()`, `getEnvironment()` and `getConfig()`, and carries the state of a run: `stop()`, `isStopped()`, `getExitCode()` and `setExitCode()`. Removed `AppContext::getRequest()`, `getResponse()` and `getRoutes()`: use `WebContext::getRequest()` and `WebContext::getRoutes()`, and `response()` for the shared response.
- The new `WebContext` and `ConsoleContext` extend `AppContext` with the same base directory and container. `WebContext` reads the request, the routes and the view cache from the container and stores the response to send; `ConsoleContext` reads the Symfony `Application`, the input and the output from the container.
Expand All @@ -26,7 +26,7 @@ The format is based on Keep a Changelog.
- `hook()` is now `event()`; `on()` and `fire()` are now `listen()` and `dispatch()`, and no aliases remain. Listeners receive one named payload array, which is empty when none is given.
- Event names no longer need to be registered, so `shared/config/hooks.php` is no longer read; dispatching a name without listeners does nothing and an empty name throws `EventException`.
- Listeners stay registered after `dispatch()` instead of being removed after the first call.
- Listeners can be registered from app helper files, which load before config and before any boot event.
- Listeners can be registered from app helper files, which load before the config and before any runtime event.
- Removed the unused `hooks_dir()` helper and renamed the debugger tab constant `Debugger::HOOKS` to `Debugger::EVENTS`; the debugbar tab is now `events` and lists only events that have listeners.
- **BREAKING:** Removed `Quantum\Loader` and moved its responsibilities to their owning packages (#535):
- Moved `Setup` to `Quantum\Config\Setup`; required config failures now use `ConfigException`, and optional imports use `Config::importIfExists()`.
Expand Down
12 changes: 6 additions & 6 deletions src/App/Adapters/Console/ConsoleAppAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@

namespace Quantum\App\Adapters\Console;

use Quantum\App\Stages\Boot\SetupConsoleApplicationStage;
use Quantum\App\Stages\Boot\RegisterCoreCommandsStage;
use Quantum\App\Stages\Boot\RegisterAppCommandsStage;
use Quantum\App\Stages\Boot\ResolveEnvironmentStage;
use Quantum\App\Adapters\Console\Stages\Boot\SetupConsoleApplicationStage;
use Quantum\App\Adapters\Console\Stages\Boot\RegisterCoreCommandsStage;
use Quantum\App\Adapters\Console\Stages\Boot\RegisterAppCommandsStage;
use Quantum\App\Adapters\Console\Stages\Boot\ResolveEnvironmentStage;
use Quantum\App\Adapters\Console\Stages\Runtime\RunCommandStage;
use Quantum\App\Adapters\Console\Stages\Boot\InitConsoleStage;
use Quantum\App\Stages\Boot\SetupErrorHandlerStage;
use Quantum\App\Stages\Boot\LoadEnvironmentStage;
use Quantum\App\Stages\Console\RunCommandStage;
use Quantum\App\Stages\Boot\LoadAppConfigStage;
use Quantum\App\Stages\Boot\InitConsoleStage;
use Quantum\App\Stages\Boot\LoadHelpersStage;
use Quantum\App\Contracts\AppInterface;
use Quantum\App\AppContext;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
* @link https://quantumphp.io
*/

namespace Quantum\App\Stages\Boot;
namespace Quantum\App\Adapters\Console\Stages\Boot;

use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Output\ConsoleOutput;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
* @link https://quantumphp.io
*/

namespace Quantum\App\Stages\Boot;
namespace Quantum\App\Adapters\Console\Stages\Boot;

use Symfony\Component\Console\Application;
use Quantum\App\Contracts\StageInterface;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
* @link https://quantumphp.io
*/

namespace Quantum\App\Stages\Boot;
namespace Quantum\App\Adapters\Console\Stages\Boot;

use Symfony\Component\Console\Application;
use Quantum\App\Contracts\StageInterface;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
* @link https://quantumphp.io
*/

namespace Quantum\App\Stages\Boot;
namespace Quantum\App\Adapters\Console\Stages\Boot;

use Symfony\Component\Console\Input\InputInterface;
use Quantum\Environment\Exceptions\EnvException;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
* @link https://quantumphp.io
*/

namespace Quantum\App\Stages\Boot;
namespace Quantum\App\Adapters\Console\Stages\Boot;

use Symfony\Component\Console\Application;
use Quantum\App\Contracts\StageInterface;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
* @link https://quantumphp.io
*/

namespace Quantum\App\Stages\Console;
namespace Quantum\App\Adapters\Console\Stages\Runtime;

use Quantum\App\Adapters\Console\ConsoleContext;
use Quantum\App\Contracts\StageInterface;
Expand All @@ -25,6 +25,10 @@
*/
class RunCommandStage implements StageInterface
{
public const BEFORE = 'console.command.before';

public const AFTER = 'console.command.after';

/**
* @param ConsoleContext $context
* @throws Exception
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
* @link https://quantumphp.io
*/

namespace Quantum\App\Stages\Boot;
namespace Quantum\App\Adapters\Web\Stages\Boot;

use Quantum\App\Contracts\StageInterface;
use Quantum\App\AppContext;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
* @link https://quantumphp.io
*/

namespace Quantum\App\Stages\Boot;
namespace Quantum\App\Adapters\Web\Stages\Boot;

use Quantum\App\Contracts\StageInterface;
use Quantum\App\AppContext;
Expand All @@ -23,10 +23,6 @@
*/
class InitHttpStage implements StageInterface
{
public const BEFORE = 'boot.http.before';

public const AFTER = 'boot.http.after';

public function process(AppContext $context): void
{
if (!Di::isRegistered(Request::class)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
* @link https://quantumphp.io
*/

namespace Quantum\App\Stages\Boot;
namespace Quantum\App\Adapters\Web\Stages\Boot;

use Quantum\Module\Exceptions\ModuleException;
use Quantum\Router\Exceptions\RouteException;
Expand All @@ -28,10 +28,6 @@
*/
class LoadModulesStage implements StageInterface
{
public const BEFORE = 'boot.modules.before';

public const AFTER = 'boot.modules.after';

/**
* @throws ModuleException|RouteException|DiException|ReflectionException
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
* @link https://quantumphp.io
*/

namespace Quantum\App\Stages\Web;
namespace Quantum\App\Adapters\Web\Stages\Runtime;

use Quantum\Middleware\Exceptions\MiddlewareException;
use Quantum\App\Contracts\StageInterface;
Expand All @@ -33,6 +33,10 @@
*/
class DispatchRequestStage implements StageInterface
{
public const BEFORE = 'web.dispatch.before';

public const AFTER = 'web.dispatch.after';

/**
* @param WebContext $context
* @throws MiddlewareException|BaseException|DiException
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
* @link https://quantumphp.io
*/

namespace Quantum\App\Stages\Web;
namespace Quantum\App\Adapters\Web\Stages\Runtime;

use Quantum\App\Contracts\StageInterface;
use Quantum\App\Adapters\Web\WebContext;
Expand All @@ -23,6 +23,10 @@
*/
class HandlePreflightStage implements StageInterface
{
public const BEFORE = 'web.preflight.before';

public const AFTER = 'web.preflight.after';

/**
* Answers an OPTIONS request with an empty response and stops the remaining stages
* @param WebContext $context
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
* @link https://quantumphp.io
*/

namespace Quantum\App\Stages\Web;
namespace Quantum\App\Adapters\Web\Stages\Runtime;

use Quantum\Config\Exceptions\ConfigException;
use Quantum\App\Contracts\StageInterface;
Expand All @@ -25,6 +25,10 @@
*/
class HandleRouteNotFoundStage implements StageInterface
{
public const BEFORE = 'web.not_found.before';

public const AFTER = 'web.not_found.after';

/**
* Answers a request that matched no route with the page not found response and stops the remaining stages
* @param WebContext $context
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
* @link https://quantumphp.io
*/

namespace Quantum\App\Stages\Web;
namespace Quantum\App\Adapters\Web\Stages\Runtime;

use Quantum\Config\Exceptions\ConfigException;
use Quantum\Lang\Exceptions\LangException;
Expand All @@ -32,6 +32,10 @@
*/
class PrepareRequestStage implements StageInterface
{
public const BEFORE = 'web.prepare.before';

public const AFTER = 'web.prepare.after';

/**
* @param WebContext $context
* @throws LangException|ConfigException|DiException|ReflectionException
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
* @link https://quantumphp.io
*/

namespace Quantum\App\Stages\Web;
namespace Quantum\App\Adapters\Web\Stages\Runtime;

use Quantum\Router\Exceptions\RouteException;
use Quantum\App\Contracts\StageInterface;
Expand All @@ -25,6 +25,10 @@
*/
class ResolveRouteStage implements StageInterface
{
public const BEFORE = 'web.route.before';

public const AFTER = 'web.route.after';

/**
* Finds the route for the current request and stores it on the request, or null when nothing matches
* @param WebContext $context
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
* @link https://quantumphp.io
*/

namespace Quantum\App\Stages\Web;
namespace Quantum\App\Adapters\Web\Stages\Runtime;

use Quantum\Config\Exceptions\ConfigException;
use Quantum\App\Contracts\StageInterface;
Expand All @@ -34,6 +34,10 @@
*/
class SendResponseStage implements StageInterface
{
public const BEFORE = 'web.send.before';

public const AFTER = 'web.send.after';

/**
* @param WebContext $context
* @throws ConfigException|DiException|ReflectionException|Exception
Expand Down
18 changes: 9 additions & 9 deletions src/App/Adapters/Web/WebAppAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,19 @@

namespace Quantum\App\Adapters\Web;

use Quantum\App\Stages\Web\HandleRouteNotFoundStage;
use Quantum\App\Adapters\Web\Stages\Runtime\HandleRouteNotFoundStage;
use Quantum\App\Adapters\Web\Stages\Runtime\HandlePreflightStage;
use Quantum\App\Adapters\Web\Stages\Runtime\DispatchRequestStage;
use Quantum\App\Adapters\Web\Stages\Runtime\PrepareRequestStage;
use Quantum\App\Adapters\Web\Stages\Runtime\SendResponseStage;
use Quantum\App\Adapters\Web\Stages\Runtime\ResolveRouteStage;
use Quantum\App\Adapters\Web\Stages\Boot\InitDebuggerStage;
use Quantum\App\Adapters\Web\Stages\Boot\LoadModulesStage;
use Quantum\App\Adapters\Web\Stages\Boot\InitHttpStage;
use Quantum\App\Stages\Boot\SetupErrorHandlerStage;
use Quantum\App\Stages\Boot\LoadEnvironmentStage;
use Quantum\App\Stages\Web\HandlePreflightStage;
use Quantum\App\Stages\Web\DispatchRequestStage;
use Quantum\App\Stages\Web\PrepareRequestStage;
use Quantum\App\Stages\Boot\LoadAppConfigStage;
use Quantum\App\Stages\Boot\InitDebuggerStage;
use Quantum\App\Stages\Web\SendResponseStage;
use Quantum\App\Stages\Web\ResolveRouteStage;
use Quantum\App\Stages\Boot\LoadModulesStage;
use Quantum\App\Stages\Boot\LoadHelpersStage;
use Quantum\App\Stages\Boot\InitHttpStage;
use Quantum\App\Contracts\AppInterface;
use Quantum\App\Enums\ExitCode;
use Quantum\App\AppContext;
Expand Down
4 changes: 2 additions & 2 deletions src/App/Pipeline.php
Original file line number Diff line number Diff line change
Expand Up @@ -85,15 +85,15 @@ private function runStage(StageInterface $stage, AppContext $context): void

/**
* Dispatches the event a stage declares for the given point, if any.
* Skipped while the event() helper is not loaded yet, since nothing can listen then.
* Only runtime stages declare events, and they run after the helpers are loaded.
* @param StageInterface<TContext> $stage
* @param TContext $context
*/
private function dispatchStageEvent(StageInterface $stage, string $point, AppContext $context): void
{
$constant = $stage::class . '::' . $point;

if (!defined($constant) || !function_exists('event')) {
if (!defined($constant)) {
return;
}

Expand Down
4 changes: 0 additions & 4 deletions src/App/Stages/Boot/LoadAppConfigStage.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,6 @@
*/
class LoadAppConfigStage implements StageInterface
{
public const BEFORE = 'boot.config.before';

public const AFTER = 'boot.config.after';

/**
* @throws ConfigException|DiException|ReflectionException
*/
Expand Down
4 changes: 0 additions & 4 deletions src/App/Stages/Boot/LoadEnvironmentStage.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,6 @@
*/
class LoadEnvironmentStage implements StageInterface
{
public const BEFORE = 'boot.environment.before';

public const AFTER = 'boot.environment.after';

/**
* @throws EnvException|DiException|BaseException
*/
Expand Down
4 changes: 0 additions & 4 deletions src/App/Stages/Boot/SetupErrorHandlerStage.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,6 @@
*/
class SetupErrorHandlerStage implements StageInterface
{
public const BEFORE = 'boot.error_handler.before';

public const AFTER = 'boot.error_handler.after';

/**
* @throws ConfigException|DiException|BaseException|ReflectionException
*/
Expand Down
Loading
Loading