Skip to content
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

[Runtime] a new component to decouple applications from global state #38465

Merged
merged 1 commit into from Mar 10, 2021

Conversation

nicolas-grekas
Copy link
Member

@nicolas-grekas nicolas-grekas commented Oct 7, 2020

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

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

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.

@nicolas-grekas
Copy link
Member Author

PR is rebased and ready. Let me know if you have any further questions.

In symfony/recipes#787 (comment), @javiereguiluz asks if this could work:

return function (Context $context) {
    $kernel = new Kernel($context->env(), $context->debug());

    return new Application($kernel);
};

The answer is yes. It could be nice for a separate PR.

src/Symfony/Component/Runtime/README.md Outdated Show resolved Hide resolved
src/Symfony/Component/Runtime/README.md Outdated Show resolved Hide resolved
src/Symfony/Component/Runtime/StartedAppInterface.php Outdated Show resolved Hide resolved
@nicolas-grekas nicolas-grekas changed the title [Runtime] a new component to decouple apps from global state [Runtime] a new component to decouple applications from global state Jan 5, 2021
@nicolas-grekas
Copy link
Member Author

@fabpot thanks for the review, comments addressed.

Copy link
Member

@fabpot fabpot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've made my review live with @nicolas-grekas, mainly cosmetic changes + a few bugs.
I think it's ready now.

@Nyholm
Copy link
Member

Nyholm commented Mar 9, 2021

I will do a final review tonight.

If someone really want to merge this before then, I'll follow up with PRs instead of comments (if any).

@wouterj
Copy link
Member

wouterj commented Mar 9, 2021

As this is a significant change in the front controllers, I would kindly, but strongly, ask to consider to first prepare updates for the documentation (and maybe SymfonyCasts) before merging this PR 🙏

@fabpot
Copy link
Member

fabpot commented Mar 9, 2021

@nicolas-grekas is going to create a doc PR ASAP, I think/hope that the diff will be very minimal 🤞.

Copy link
Member

@Nyholm Nyholm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here are some smaller comments.

I'll start testing this now.

@Nyholm
Copy link
Member

Nyholm commented Mar 9, 2021

Thank you Nicolas for the updates. I've been carefully reviewing and testing this while writing some docs. See symfony/symfony-docs#15081

Copy link
Member

@Nyholm Nyholm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Im happy with this PR.

Thank you

@fabpot
Copy link
Member

fabpot commented Mar 10, 2021

Thank you @nicolas-grekas.

@fabpot fabpot merged commit c06a76c into symfony:5.x Mar 10, 2021
@nicolas-grekas nicolas-grekas deleted the runtime branch March 10, 2021 18:32
wouterj added a commit to wouterj/symfony-docs that referenced this pull request Apr 7, 2021
This PR was merged into the 5.3-dev branch.

Discussion
----------

[Runtime] Init Runtime component

This is documentation for symfony/symfony#38465

The docs is focus on "how to use" instead of "how it works". I will add a "creating your own Runtime" which will include "how it works".

As you may notice. I use two concepts: "the callback" and "the application". The fact that "the application" can be a callback and will be wrapped in callbacks is irrelevant here. I found it much easier to understand this way.

Commits
-------

ef5f04b Apply suggestions from code review
76de973 [symfony#15081] Finish the new Runtime docs
e7bacf1 Apply suggestions from code review
06414b7 Apply suggestions from code review
7388cc9 Init Runtime docs
@fabpot fabpot mentioned this pull request Apr 18, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

10 participants