Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
4f90f57
[#537] Characterize web runtime behavior before the refactor
armanist Oct 9, 2026
ec924c4
[#537] Characterize console runtime behavior before the refactor
armanist Oct 9, 2026
6ee8f0e
[#537] Add request pipeline skeleton
armanist Oct 9, 2026
9e62e97
[#537] Add preflight and send request stages
armanist Oct 9, 2026
5fbc38b
[#537] Add route resolution request stages
armanist Oct 9, 2026
a87ee63
[#537] Clean up view cache folders in the web characterization test
armanist Oct 9, 2026
be3c104
[#537] Add prepare request stage
armanist Oct 9, 2026
c1f0579
[#537] Add dispatch request stage
armanist Oct 9, 2026
9deb8e8
[#537] Run the web request through the request pipeline
armanist Oct 9, 2026
0676a80
[#537] Add console pipeline skeleton
armanist Oct 9, 2026
8387105
[#537] Add console stages
armanist Oct 9, 2026
7f6ffa2
[#537] Run the console command through the console pipeline
armanist Oct 9, 2026
e20b976
[#537] Remove the AppAdapter base class
armanist Oct 9, 2026
92a83ce
[#537] Organize adapters, contexts, pipelines and boot stages into fo…
armanist Oct 9, 2026
8f75262
[#537] Document the runtime pipelines in the changelog
armanist Oct 9, 2026
2dc86dd
[#537] Pin command registration behavior before changing it
armanist Oct 12, 2026
5630e4d
[#537] Add CommandRegistrar to register discovered commands
armanist Oct 12, 2026
7d0450a
[#537] Register the console commands through the CommandRegistrar
armanist Oct 12, 2026
4b66434
[#537] Keep the register stage paths in small helpers
armanist Oct 12, 2026
7fb4837
[#537] Validate the command inside RunCommandStage
armanist Oct 12, 2026
3343a05
[#537] Update the changelog for the console pipeline stages
armanist Oct 12, 2026
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ The format is based on Keep a Changelog.
- Added adapter-based Lang provider support with `DeepL` and `Google Translate` adapters plus shared remote request/caching infrastructure (#533)

### Changed
- **BREAKING:** Refactored the web and console runtime into staged pipelines (#537):
- `WebAppAdapter::start()` now runs a `RequestPipeline` of the preflight, route resolution, not found, prepare, dispatch, and send stages with a `RequestContext`, 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.
- Removed the `AppAdapter` base class and the `WebAppTrait` and `ConsoleAppTrait` traits. `WebAppAdapter` and `ConsoleAppAdapter` now implement `AppInterface` directly, and `ConsoleAppAdapter::createApplication()` stays public.
- **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.
- 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`.
Expand Down
28 changes: 0 additions & 28 deletions src/App/Adapters/AppAdapter.php

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,19 @@
* @link https://quantumphp.io
*/

namespace Quantum\App\Adapters;
namespace Quantum\App\Adapters\Console;

use Quantum\App\Stages\Console\RegisterCoreCommandsStage;
use Quantum\App\Stages\Console\RegisterAppCommandsStage;
use Symfony\Component\Console\Output\ConsoleOutput;
use Quantum\App\Stages\Console\RunCommandStage;
use Symfony\Component\Console\Input\ArgvInput;
use Quantum\App\Stages\SetupErrorHandlerStage;
use Quantum\App\Stages\LoadEnvironmentStage;
use Quantum\App\Stages\Boot\SetupErrorHandlerStage;
use Quantum\App\Stages\Boot\LoadEnvironmentStage;
use Symfony\Component\Console\Application;
use Quantum\App\Stages\LoadAppConfigStage;
use Quantum\App\Stages\LoadHelpersStage;
use Quantum\App\Traits\ConsoleAppTrait;
use Quantum\App\Stages\Boot\LoadAppConfigStage;
use Quantum\App\Stages\Boot\LoadHelpersStage;
use Quantum\App\Contracts\AppInterface;
use Quantum\App\Enums\ExitCode;
use Quantum\App\BootPipeline;
use Quantum\App\AppContext;
Expand All @@ -31,9 +34,9 @@
* Class ConsoleAppAdapter
* @package Quantum\App
*/
class ConsoleAppAdapter extends AppAdapter
class ConsoleAppAdapter implements AppInterface
{
use ConsoleAppTrait;
protected AppContext $context;

protected ArgvInput $input;

Expand All @@ -43,7 +46,7 @@ class ConsoleAppAdapter extends AppAdapter

public function __construct(AppContext $context)
{
parent::__construct($context);
$this->context = $context;

$this->input = new ArgvInput();
$this->output = new ConsoleOutput();
Expand Down Expand Up @@ -73,18 +76,26 @@ public function __construct(AppContext $context)
);
}

public function createApplication(string $name, string $version): Application
{
return new Application($name, $version);
}

/**
* @throws Exception
*/
public function start(): ?int
{
$this->registerCoreCommands();
$this->registerAppCommands();
$context = new ConsoleContext($this->context, $this->application, $this->input, $this->output);

$this->validateCommand();
$pipeline = new ConsolePipeline([
new RegisterCoreCommandsStage(),
new RegisterAppCommandsStage(),
new RunCommandStage(),
]);

$exitCode = $this->application->run($this->input, $this->output);
$pipeline->run($context);

return $exitCode ?: ExitCode::SUCCESS;
return $context->getExitCode() ?: ExitCode::SUCCESS;
}
}
80 changes: 80 additions & 0 deletions src/App/Adapters/Console/ConsoleContext.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<?php

declare(strict_types=1);

/**
* Quantum PHP Framework
* An open-source software development framework for PHP
* @link https://quantumphp.io
*/

namespace Quantum\App\Adapters\Console;

use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Application;
use Quantum\App\Enums\ExitCode;
use Quantum\App\AppContext;

/**
* Class ConsoleContext
*
* Carries the state of a single console run through the console stages.
* Unlike AppContext, which is immutable, the exit code is set by the stages.
*
* @package Quantum\App
*/
class ConsoleContext
{
private AppContext $appContext;

private Application $application;

private InputInterface $input;

private OutputInterface $output;

private int $exitCode = ExitCode::SUCCESS;

public function __construct(
AppContext $appContext,
Application $application,
InputInterface $input,
OutputInterface $output
) {
$this->appContext = $appContext;
$this->application = $application;
$this->input = $input;
$this->output = $output;
}

public function getAppContext(): AppContext
{
return $this->appContext;
}

public function getApplication(): Application
{
return $this->application;
}

public function getInput(): InputInterface
{
return $this->input;
}

public function getOutput(): OutputInterface
{
return $this->output;
}

public function getExitCode(): int
{
return $this->exitCode;
}

public function setExitCode(int $exitCode): void
{
$this->exitCode = $exitCode;
}
}
53 changes: 53 additions & 0 deletions src/App/Adapters/Console/ConsolePipeline.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php

declare(strict_types=1);

/**
* Quantum PHP Framework
* An open-source software development framework for PHP
* @link https://quantumphp.io
*/

namespace Quantum\App\Adapters\Console;

use Quantum\App\Contracts\ConsoleStageInterface;
use InvalidArgumentException;

/**
* Class ConsolePipeline
*
* Runs the console stages in order.
* Stage lifecycle events are not dispatched here yet.
*
* @package Quantum\App
*/
class ConsolePipeline
{
/**
* @var ConsoleStageInterface[]
*/
private array $stages;

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

$this->stages = $stages;
}

public function run(ConsoleContext $context): void
{
foreach ($this->stages as $stage) {
$stage->process($context);
}
}
}
91 changes: 91 additions & 0 deletions src/App/Adapters/Web/RequestContext.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
<?php

declare(strict_types=1);

/**
* Quantum PHP Framework
* An open-source software development framework for PHP
* @link https://quantumphp.io
*/

namespace Quantum\App\Adapters\Web;

use Quantum\ResourceCache\ViewCache;
use Quantum\Router\MatchedRoute;
use Quantum\App\AppContext;
use Quantum\Http\Response;

/**
* Class RequestContext
*
* Carries the state of a single web request through the request stages.
* Unlike AppContext, which is immutable, this object is mutated by the stages.
*
* @package Quantum\App
*/
class RequestContext
{
private AppContext $appContext;

private ?MatchedRoute $matchedRoute = null;

private ?Response $response = null;

private ?ViewCache $viewCache = null;

private bool $stopped = false;

public function __construct(AppContext $appContext)
{
$this->appContext = $appContext;
}

public function getAppContext(): AppContext
{
return $this->appContext;
}

public function getMatchedRoute(): ?MatchedRoute
{
return $this->matchedRoute;
}

public function setMatchedRoute(?MatchedRoute $matchedRoute): void
{
$this->matchedRoute = $matchedRoute;
}

public function getResponse(): ?Response
{
return $this->response;
}

public function setResponse(Response $response): void
{
$this->response = $response;
}

public function getViewCache(): ?ViewCache
{
return $this->viewCache;
}

public function setViewCache(ViewCache $viewCache): void
{
$this->viewCache = $viewCache;
}

/**
* Marks the request as finished early, so stages that compute a response skip their work.
* The stage that sends the response still runs.
*/
public function stop(): void
{
$this->stopped = true;
}

public function isStopped(): bool
{
return $this->stopped;
}
}
Loading
Loading