-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[Runtime] a new component to decouple apps from global state #36652
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
df8e064
to
e30d0ea
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for this suggestion. I like the idea and the direction this is moving towards. I also see a future Psr15Runtime
and BrefRuntime
(Not necessarily in the Symfony organisation)
What I cannot see is support for a "keep alive" runtime. In the Symfony world it would mean that the same kernel handles multiple requests. With the current implementation I need to provide my own autoload.php
. But that may be out of scope of the PR.
Thanks for the review @Nyholm
💯%
I need to showcase this yes. I think in these situations the autoload.php would be provided outside of the file (the file could still contain the "require" to allow it to boot standalone, but it would turn into a no-op, because the autoloader would be already loaded, by design.) Then, that "outside" would be responsible for running the loop. |
6153719
to
06d70e0
Compare
b0fe04a
to
247618d
Compare
c8e11bd
to
16901f5
Compare
I realized this weekend that the component could register itself as a Composer plugin to generate a new |
We've just moved away from |
Moved to #38465 btw |
…m global state (nicolas-grekas) This PR was merged into the 5.3-dev branch. Discussion ---------- [Runtime] a new component to decouple applications from global state | Q | A | ------------- | --- | Branch? | 5.x | Bug fix? | no | New feature? | yes | Deprecations? | no | Tickets | - | License | MIT | Doc PR | symfony/symfony-docs#15081 Follow up of #36652, see discussion there. What if we could decouple the bootstrapping logic of our apps from any global state? This PR makes it possible via a new proposed `symfony/runtime` component. The immediate benefit this provides is easier maintenance of Symfony apps: code that is currently shipped by recipes will be able to move to `vendor/`. Read the previous sentence twice, this is big :) Check the following PR to see how far this goes: symfony/recipes#787 The longer-term benefit is being able to run the exact same app under several runtimes: PHP-FPM, CLI, but also PHP-PM and similar. Thanks to the proposed interface, this benefit could span to any PHP apps; not only to apps using the Symfony HttpKernel/HttpFoundation components. This part could be moved to `symfony/contracts` in the future. Performance-wise, I measured no significant difference with the current way of running apps. RuntimeInterface ---------------- The core of this component is the `RuntimeInterface` which describes a high-order runtime logic. It is designed to be totally generic and able to run any application outside of the global state in 6 steps: 1. the main entry point returns a callable that wraps the application; 2. this callable is passed to `RuntimeInterface::getResolver()`, which returns a `ResolverInterface`; this resolver returns an array with the (potentially decorated) callable at index 0, and all its resolved arguments at index 1; 3. the callable is invoked with its arguments; it returns an object that represents the application; 4. that object is passed to `RuntimeInterface::getRunner()`, which returns a `RunnerInterface`: an instance that knows how to "run" the object; 5. that instance is `run()` and returns the exit status code as `int`; 6. the PHP engine is exited with this status code. This process is extremely flexible as it allows implementations of `RuntimeInterface` to hook into any critical steps. Autoloading ----------- This package registers itself as a Composer plugin to generate a `vendor/autoload_runtime.php` file. This file shall be required instead of the usual `vendor/autoload.php` in front-controllers that leverage this component and return a callable. Before requiring the `vendor/autoload_runtime.php` file, set the `$_SERVER['APP_RUNTIME']` variable to a class that implements `RuntimeInterface` and that should be used to run the returned callable. Alternatively, the class of the runtime can be defined in the `extra.runtime.class` entry of the `composer.json` file. A `SymfonyRuntime` is used by default. It knows the conventions to run Symfony and native PHP applications. Examples -------- This `public/index.php` is a "Hello World" that handles a "name" query parameter: ```php <?php require_once dirname(__DIR__).'/vendor/autoload_runtime.php'; return function (array $request, array $context): void { // $request holds keys "query", "body", "files" and "session", // which map to $_GET, $_POST, $_FILES and &$_SESSION respectively // $context maps to $_SERVER $name = $request['query']['name'] ?? 'World'; $time = $context['REQUEST_TIME']; echo sprintf('Hello %s, the current Unix timestamp is %s.', $name, $time); }; ``` This `bin/console.php` is a single-command "Hello World" application (run `composer require symfony/console` before launching it): ```php <?php use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; require_once dirname(__DIR__).'/vendor/autoload_runtime.php'; return function (Command $command) { $command->addArgument('name', null, 'Who should I greet?', 'World'); return $command->setCode(function (InputInterface $input, OutputInterface $output) { $name = $input->getArgument('name'); $output->writeln(sprintf('Hello <comment>%s</>', $name)); }); }; ``` The `SymfonyRuntime` can resolve and handle many types related to the `symfony/http-foundation` and `symfony/console` components. Check its source code for more information. Commits ------- 61b32ab [Runtime] a new component to decouple applications from global state
runtime logic. | ||
|
||
It is designed to be totally generic and able to run any application outside of | ||
the global state in 6 steps: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
q: what does "outside of the global state" refer to, in this phrase?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"decoupled" might be better? PR welcome
which returns a `ResolvedAppInterface`. This is an invokable with zero | ||
arguments that returns whatever object of yours represents your app | ||
(e.g a Symfony kernel or response, a console application or command); | ||
3. this invokable is called and returns this object that represents your app; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"this object" => "the object" ?
2. the arguments of this closure are resolved by `RuntimeInterface::resolve()` | ||
which returns a `ResolvedAppInterface`. This is an invokable with zero | ||
arguments that returns whatever object of yours represents your app | ||
(e.g a Symfony kernel or response, a console application or command); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
An Sf response might be correct, but it seems confusing in this context, as I would not expect it to represent an "app".
In fact I have a bit of a hard time understanding this whole explanation, because to me "the app" is primarily the bits of code which are given in the examples below, not the Sf kernel or Command... (which I would probably call runtimes ;-) )
What if we could decouple the bootstrapping logic of our apps from any global state?
This PR makes it possible via a new proposed
symfony/runtime
component.The immediate benefit this provides is easier maintenance of Symfony apps: code that is currently shipped by recipes will be able to move to
vendor/
. Read the previous sentence twice, this is big :)Check the following PR to see how far this goes: symfony/recipes#787
The longer-term benefit is being able to run the exact same app under several runtimes: PHP-FPM, CLI, but also PHP-PM and similar. Thanks to the proposed interface, this benefit could span to any PHP apps; not only to apps using the Symfony HttpKernel/HttpFoundation components. This part could be moved to
symfony/contracts
in the future.Performance-wise, I measured no significant difference with the current way of running apps.
RuntimeInterface
The core of this component is the
RuntimeInterface
which describes a high-orderruntime logic.
It is designed to be totally generic and able to run any application outside of
the global state in 6 steps:
RuntimeInterface::resolve()
which returns a
ResolvedAppInterface
. This is an invokable with zeroarguments that returns whatever object of yours represents your app
(e.g a Symfony kernel or response, a console application or command);
RuntimeInterface::start()
, which returns aStartedAppInterface
: an invokable that knows how to "run" your app;This process is extremely flexible as it allows implementations of
RuntimeInterface
to hook into any critical steps.Autoloading
This package registers itself as a Composer plugin to generate a
vendor/autoload_runtime.php
file. You need to require it instead of the usualvendor/autoload.php
in front-controllers that leverage this component andreturn a closure.
Before requiring the
vendor/autoload_runtime.php
file, you can set the$_SERVER['APP_RUNTIME']
variable to a class that implementsRuntimeInterface
and that should be used to run the app.
A
SymfonyRuntime
is used by default. It knows the conventions to runSymfony and native PHP apps.
Examples
This
public/index.php
is a "Hello World" that handles a "name" query parameter:This
bin/console.php
is a single-command "Hello World" app(run
composer require symfony/console
before launching it):The
SymfonyRuntime
can resolve and handle many types related to thesymfony/http-foundation
andsymfony/console
components.Check its source code for more information.