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
10 changes: 7 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,14 @@ 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 `BootPipeline` 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 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 adapter-based Lang provider support with `DeepL` and `Google Translate` adapters plus shared remote request/caching infrastructure (#533)

### Changed
- **BREAKING:** Replaced the boot, web and console pipelines with one generic `Pipeline` (#583):
- `Pipeline<TContext>` 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)`.
- Removed `BootPipeline` and `BootStageInterface`: build a `new Pipeline([...])`, and implement `StageInterface` with `@implements StageInterface<AppContext>` in a boot stage. `WebPipeline`, `ConsolePipeline`, `WebStageInterface` and `ConsoleStageInterface`, added earlier in 3.1.0, are removed too.
- A pipeline still rejects anything that is not a `StageInterface`, but no longer rejects a stage of another kind at runtime (a boot stage in a web pipeline); PHPStan reports it instead.
- **BREAKING:** The web and console contexts now extend `AppContext` (#579):
- `AppContext` keeps only what both the web and the console boot provide: `getBaseDir()`, `getContainer()`, `getEnvironment()` and `getConfig()`. Removed `AppContext::getRequest()`, `getResponse()` and `getRoutes()`, which only the web boot can serve: use `WebContext::getRequest()` and `WebContext::getRoutes()`, and `response()` for the shared response.
- `WebContext` and `ConsoleContext` extend `AppContext` and are built from it with the same base directory and container; `WebContext::getAppContext()` is removed. Like `ConsoleContext`, `WebContext` reads its services from the container (the request, the routes and the view cache) and the matched route from the request, and stores only what the run produces: the response that is sent and the stopped flag. `WebContext::setMatchedRoute()` and `setViewCache()` are removed.
Expand All @@ -23,8 +27,8 @@ The format is based on Keep a Changelog.
- `ConsoleAppAdapter` always runs the full boot and, when it fails, keeps the failure instead of stopping. `RunCommandStage` re-throws that original failure only for a command that requires the environment, so `core:env`, `list`, and `help` still run and `core:env --yes` can repair a missing, empty, or malformed `.env`. The reduced boot for `core:env` is gone, and the Symfony `Application` is named `UNKNOWN` until a successful boot names it from the app config.
- Added `CliCommand::$requiresEnvironment` (default `true`) with `CliCommand::requiresEnvironment()`; a command that works without the environment, like `EnvCommand`, sets it to `false`. `ConsoleContext` takes an optional boot failure and exposes it with `getBootFailure()`.
- **BREAKING:** Refactored the web and console runtime into staged pipelines (#537):
- `WebAppAdapter::start()` now runs a `WebPipeline` of the preflight, route resolution, not found, prepare, dispatch, and send stages with a `WebContext`, and `ConsoleAppAdapter::start()` runs a `ConsolePipeline` of the register core commands, register app commands, and run stages with a `ConsoleContext`; the run stage rejects a missing or unknown command before running anything, and registration goes through the new `Quantum\Console\CommandRegistrar`. Behavior is unchanged, and runtime stage events are not dispatched yet.
- Moved the web adapter, context, and pipeline to `Quantum\App\Adapters\Web`, the console ones to `Quantum\App\Adapters\Console`, and the boot stages to `Quantum\App\Stages\Boot`. Code that goes through `AppFactory` is unaffected; code that references these classes directly must update its imports.
- `WebAppAdapter::start()` now runs a `Pipeline` of the preflight, route resolution, not found, prepare, dispatch, and send stages with a `WebContext`, and `ConsoleAppAdapter::start()` runs a `Pipeline` of the register core commands, register app commands, and run stages with a `ConsoleContext`; the run stage rejects a missing or unknown command before running anything, and registration goes through the new `Quantum\Console\CommandRegistrar`. Behavior is unchanged. The pipeline dispatches a stage's `BEFORE`/`AFTER` events when the stage declares them, but the built-in web and console stages declare none yet.
- Moved the web adapter and context to `Quantum\App\Adapters\Web`, the console ones to `Quantum\App\Adapters\Console`, and the boot stages 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` now implement `AppInterface` directly.
- **BREAKING:** Replaced the `Quantum\Hook` package with `Quantum\Event` (#536):
- `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.
Expand Down
6 changes: 3 additions & 3 deletions src/App/Adapters/Console/ConsoleAppAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@
use Quantum\App\Stages\Boot\InitConsoleStage;
use Quantum\App\Stages\Boot\LoadHelpersStage;
use Quantum\App\Contracts\AppInterface;
use Quantum\App\BootPipeline;
use Quantum\App\AppContext;
use Quantum\App\Pipeline;
use Throwable;
use Exception;

Expand All @@ -44,7 +44,7 @@ public function __construct(AppContext $context)
$this->context = $context;

try {
$pipeline = new BootPipeline([
$pipeline = new Pipeline([
new InitConsoleStage(),
new LoadHelpersStage(),
new LoadEnvironmentStage(),
Expand All @@ -64,7 +64,7 @@ public function __construct(AppContext $context)
*/
public function start(): ?int
{
$pipeline = new ConsolePipeline([
$pipeline = new Pipeline([
new RegisterCoreCommandsStage(),
new RegisterAppCommandsStage(),
new RunCommandStage(),
Expand Down
53 changes: 0 additions & 53 deletions src/App/Adapters/Console/ConsolePipeline.php

This file was deleted.

6 changes: 3 additions & 3 deletions src/App/Adapters/Web/WebAppAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@
use Quantum\App\Stages\Boot\InitHttpStage;
use Quantum\App\Contracts\AppInterface;
use Quantum\App\Enums\ExitCode;
use Quantum\App\BootPipeline;
use Quantum\App\AppContext;
use Quantum\App\Pipeline;

/**
* Class WebAppAdapter
Expand All @@ -40,7 +40,7 @@ public function __construct(AppContext $context)
{
$this->context = $context;

$pipeline = new BootPipeline([
$pipeline = new Pipeline([
new LoadHelpersStage(),
new LoadEnvironmentStage(),
new LoadAppConfigStage(),
Expand All @@ -55,7 +55,7 @@ public function __construct(AppContext $context)

public function start(): ?int
{
$pipeline = new WebPipeline([
$pipeline = new Pipeline([
new HandlePreflightStage(),
new ResolveRouteStage(),
new HandleRouteNotFoundStage(),
Expand Down
54 changes: 0 additions & 54 deletions src/App/Adapters/Web/WebPipeline.php

This file was deleted.

25 changes: 0 additions & 25 deletions src/App/Contracts/ConsoleStageInterface.php

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,19 @@
use Quantum\App\AppContext;

/**
* Interface BootStageInterface
* Interface StageInterface
*
* A stage of a pipeline. A stage states the context it works with in its class docblock,
* for example `@implements StageInterface<WebContext>`.
*
* @template TContext of AppContext
* @package Quantum\App
*/
interface BootStageInterface
interface StageInterface
{
/**
* Processes a single boot stage
* Processes a single stage
* @param TContext $context
*/
public function process(AppContext $context): void;
}
25 changes: 0 additions & 25 deletions src/App/Contracts/WebStageInterface.php

This file was deleted.

28 changes: 18 additions & 10 deletions src/App/BootPipeline.php → src/App/Pipeline.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,36 +10,42 @@

namespace Quantum\App;

use Quantum\App\Contracts\BootStageInterface;
use Quantum\App\Contracts\StageInterface;
use InvalidArgumentException;

/**
* Class BootPipeline
* Class Pipeline
*
* Runs stages in order with a context. The event a stage declares in its BEFORE / AFTER
* constants is dispatched around it, with the context as the payload.
*
* @template TContext of AppContext
* @package Quantum\App
*/
class BootPipeline
final class Pipeline
{
/**
* @var BootStageInterface[]
* @var array<StageInterface<TContext>>
*/
private array $stages;

/**
* @param BootStageInterface[] $stages
* @param array<StageInterface<TContext>> $stages
*/
public function __construct(array $stages = [])
{
foreach ($stages as $stage) {
if (!$stage instanceof BootStageInterface) {
throw new InvalidArgumentException(
'All stages must implement ' . BootStageInterface::class
);
if (!$stage instanceof StageInterface) {
throw new InvalidArgumentException('All stages must implement ' . StageInterface::class);
}
}

$this->stages = $stages;
}

/**
* @param TContext $context
*/
public function run(AppContext $context): void
{
foreach ($this->stages as $stage) {
Expand All @@ -54,8 +60,10 @@ public function run(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.
* @param StageInterface<TContext> $stage
* @param TContext $context
*/
private function dispatchStageEvent(BootStageInterface $stage, string $point, AppContext $context): void
private function dispatchStageEvent(StageInterface $stage, string $point, AppContext $context): void
{
$constant = $stage::class . '::' . $point;

Expand Down
5 changes: 3 additions & 2 deletions src/App/Stages/Boot/InitConsoleStage.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
use Symfony\Component\Console\Output\ConsoleOutput;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\ArgvInput;
use Quantum\App\Contracts\BootStageInterface;
use Symfony\Component\Console\Application;
use Quantum\App\Contracts\StageInterface;
use Quantum\App\AppContext;
use Quantum\Di\Di;

Expand All @@ -25,9 +25,10 @@
* Runs before the helpers are loaded, so that the console application, input and output exist
* even when a helper file fails; for the same reason it declares no boot events.
*
* @implements StageInterface<AppContext>
* @package Quantum\App
*/
class InitConsoleStage implements BootStageInterface
class InitConsoleStage implements StageInterface
{
public function process(AppContext $context): void
{
Expand Down
5 changes: 3 additions & 2 deletions src/App/Stages/Boot/InitDebuggerStage.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@

namespace Quantum\App\Stages\Boot;

use Quantum\App\Contracts\BootStageInterface;
use Quantum\App\Contracts\StageInterface;
use Quantum\App\AppContext;

/**
* Class InitDebuggerStage
* @implements StageInterface<AppContext>
* @package Quantum\App
*/
class InitDebuggerStage implements BootStageInterface
class InitDebuggerStage implements StageInterface
{
public function process(AppContext $context): void
{
Expand Down
5 changes: 3 additions & 2 deletions src/App/Stages/Boot/InitHttpStage.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,18 @@

namespace Quantum\App\Stages\Boot;

use Quantum\App\Contracts\BootStageInterface;
use Quantum\App\Contracts\StageInterface;
use Quantum\App\AppContext;
use Quantum\Http\Response;
use Quantum\Http\Request;
use Quantum\Di\Di;

/**
* Class InitHttpStage
* @implements StageInterface<AppContext>
* @package Quantum\App
*/
class InitHttpStage implements BootStageInterface
class InitHttpStage implements StageInterface
{
public const BEFORE = 'boot.http.before';

Expand Down
Loading
Loading