Skip to content
Snippets Groups Projects

Issue #3313404: Use symfony/runtime for less bespoke bootstrap/compatibility...

Open Issue #3313404: Use symfony/runtime for less bespoke bootstrap/compatibility...
9 unresolved threads
Open Alexander Varwijk requested to merge issue/drupal-3313404:feature/symfony-runtime into 11.x
9 unresolved threads

Issue #3313404: Use symfony/runtime for less bespoke bootstrap/compatibility with varied runtime environments

A custom runtime is provided which knows how to create the Drupal specific runner that will use the DrupalKernel. The Drupal specific runner is currently the same as Symfony's HttpKernelRunner. However, it provides us with a place to put runtime specific code such as Revolt's eventloop.

Merge request reports

Members who can merge are allowed to add commits.
Code Quality is loading
Test summary results are being parsed
Metrics reports are loading
Ready to merge by members who can write to the target branch.
Loading

Activity

Filter activity
  • Approvals
  • Assignees & reviewers
  • Comments (from bots)
  • Comments (from users)
  • Commits & branches
  • Edits
  • Labels
  • Lock status
  • Mentions
  • Merge request status
  • Tracking
18 */
19 private function __construct() {
20 }
21
22 /**
23 * Generates the autoload_runtime file at the specified location.
24 *
25 * This only writes a bit of PHP that includes the autoload_runtime file that
26 * Composer generated. Drupal does this so that it can guarantee that there
27 * will always be an `autoload_runtime.php` file in a well-known location.
28 *
29 * @param \Composer\IO\IOInterface $io
30 * IOInterface to write to.
31 * @param string $package_name
32 * The name of the package defining the autoload_runtime file
33 * (the root package).
  • nicxvan
    nicxvan @nicxvan started a thread on the diff
  • 29 * @param \Composer\IO\IOInterface $io
    30 * IOInterface to write to.
    31 * @param string $package_name
    32 * The name of the package defining the autoload_runtime file
    33 * (the root package).
    34 * @param string $web_root
    35 * The path to the web root.
    36 * @param string $vendor
    37 * The path to the vendor directory.
    38 *
    39 * @return \Drupal\Composer\Plugin\Scaffold\Operations\ScaffoldResult
    40 * The result of the autoload_runtime file generation.
    41 */
    42 public static function generateAutoloadRuntime(IOInterface $io, $package_name, $web_root, $vendor) {
    43 $autoload_runtime_path = static::autoloadRuntimePath($package_name, $web_root);
    44 // Calculate the relative path from the webroot (location of the project
    • On my phone so I can't do multiline suggestions.

      The parenthetical in the middle makes this harder to parse, moving that to is own clause at the end would be clearer I think.

      Something like:

      // Calculate the relative path from the webroot to the vendor directory. /The autoload_runtime.php must know the path to the vendor directory.

      Edited by nicxvan
    • Please register or sign in to reply
  • nicxvan
    nicxvan @nicxvan started a thread on the diff
  • 67 public static function autoloadRuntimeFileCommitted(IOInterface $io, $package_name, $web_root) {
    68 $autoload_runtime_path = static::autoloadRuntimePath($package_name, $web_root);
    69 $autoload_runtime_file = $autoload_runtime_path->fullPath();
    70 $location = dirname($autoload_runtime_file);
    71 if (!file_exists($autoload_runtime_file)) {
    72 return FALSE;
    73 }
    74 return Git::checkTracked($io, $autoload_runtime_file, $location);
    75 }
    76
    77 /**
    78 * Generates a scaffold file path object for the autoload_runtime file.
    79 *
    80 * @param string $package_name
    81 * The name of the package defining the autoload_runtime file
    82 * (the root package).
  • nicxvan
    nicxvan @nicxvan started a thread on the diff
  • 76
    77 /**
    78 * Generates a scaffold file path object for the autoload_runtime file.
    79 *
    80 * @param string $package_name
    81 * The name of the package defining the autoload_runtime file
    82 * (the root package).
    83 * @param string $web_root
    84 * The path to the web root.
    85 *
    86 * @return \Drupal\Composer\Plugin\Scaffold\ScaffoldFilePath
    87 * Object wrapping the relative and absolute path to the destination file.
    88 */
    89 protected static function autoloadRuntimePath($package_name, $web_root) {
    90 $rel_path = 'autoload_runtime.php';
    91 $dest_rel_path = '[web-root]/' . $rel_path;
    • Is this templating for the relative path?

    • The [web-root] is the web directory configured for DrupalScaffold. By default this is web/ but many projects set it to html/.

      I'll admit I don't understand the full functionality of GenerateAutoloadReferenceFile::autoloadRuntimePath. However, it has worked for autoload.php and been reviewed by smart people, and autoload_runtime.php will be created next to autoload.php and the reference file should live next to the autoload reference file, so the logic they use should be identical.

    • Please register or sign in to reply
  • 1 <?php
    2
    3 namespace Drupal\Core\Runtime;
    4
    5 use Drupal\Core\DrupalKernelInterface;
    6 use Symfony\Component\HttpFoundation\Request;
    7 use Symfony\Component\HttpKernel\TerminableInterface;
    8 use Symfony\Component\Runtime\RunnerInterface;
    9
    10 /**
    11 * The Drupal Kernel Runner.
    12 *
    13 * Is used by the DrupalRuntime to handle a single request in the context of a
  • 4
    5 use Drupal\Core\DrupalKernelInterface;
    6 use Symfony\Component\HttpFoundation\Request;
    7 use Symfony\Component\HttpKernel\TerminableInterface;
    8 use Symfony\Component\Runtime\RunnerInterface;
    9
    10 /**
    11 * The Drupal Kernel Runner.
    12 *
    13 * Is used by the DrupalRuntime to handle a single request in the context of a
    14 * set-up using PHP-FPM like nginx or Apache.
    15 */
    16 class DrupalKernelRunner implements RunnerInterface {
    17
    18 /**
    19 * Create a new DrupalKernelRunner interface.
  • nicxvan
  • nicxvan
    nicxvan @nicxvan started a thread on the diff
  • 28 private Request $request,
    29 ) {
    30 }
    31
    32 /**
    33 * {@inheritdoc}
    34 */
    35 public function run(): int {
    36 $response = $this->kernel->handle($this->request);
    37 $response->send();
    38
    39 if ($this->kernel instanceof TerminableInterface) {
    40 $this->kernel->terminate($this->request, $response);
    41 }
    42
    43 return 0;
    • I'm going to have to read up on why this is the case.

    • To help your reading, this is covered by the Symfony Runtime configuration: https://symfony.com/doc/current/components/runtime.html#create-your-own-runtime

      1. The RunnerInterface::run(object $application) is called and it returns the exit status code as int.
      2. The PHP engine is terminated with this status code.

      This exit happens in autoload_runtime.php which is provided by the symfony/runtime package. This happens at the end of execution of the front-controller (e.g. index.php/update.php) which is itself fully executed when required by the autoload_runtime.php file.

      It's a bit of mental gymnastics to understand the interesting thing Symfony did here, but it works quite elegantly:

      1. Request starts at front-controller (e.g. index.php)
      2. Code is executed until autoload_runtime.php is called and execution is handed over
        1. autoload_runtime.php prevents itself from running multiple times by checking if autoload.php was required more than once
        2. autoload_runtime.php requires the file that the application was started with
          1. The front-controller is executed again
          2. require_once protects the autoloop_runtime re-entry from looping (and has the fallback within autoloop_runtime itself in case require is used).
          3. The front-controller code runs through the entire file, returning the actual application set-up closure
        3. autoload_runtime.php performs its bootstrapping d. autoload_runtime.php executes the application using the selected runtime
      3. autoload_runtime.php calls exit to prevent code beyond the require_once from being executed twice (this was already executed in 2b).

      The default behaviour for PHP in case exit is not explicitly called would be to return 0 when the index.php file runs to conclusion, or a non-zero code in case of exception. So that behaviour is preserved by Symfony Runtime. From an outside perspective (looking at exit code) the presence of Symfony Runtime isn't detectable.

    • Thanks for the great details!

    • Please register or sign in to reply
  • nicxvan
    nicxvan @nicxvan started a thread on the diff
  • 1 <?php
    2
    3 /**
    4 * @file
    5 * Includes the autoload_runtime created by the Symfony Runtime component.
    6 *
    • So this is a static file used to include the dynamically generated one right?

    • Sort of but not entirely.

      The DrupalScaffold plugin would normally create the autoload_runtime.php file in the web-root. That file in the web root is always static from the point of view of the front-controllers (index.php or update.php). See the current reference to autoload.php. The contents of the autoload_runtime.php are dynamic and will ensure that the file itself points to vendor/autoload_runtime.php regardless of where the web root is located.

      The autoload_runtime.php file is statically included in the drupal/drupal repository because the development repository does not execute the scaffold plugin. This behaviour is preserved from what was previously done for autoload.php.

      I do see that the repository copy of autoload.php does not have the scaffold line of text, which I'll remove.

    • Please register or sign in to reply
  • 1 <?php
    2
    3 namespace Drupal\Core\Runtime;
    4
    5 use Drupal\Core\DrupalKernelInterface;
    6 use Symfony\Component\HttpFoundation\Request;
    7 use Symfony\Component\HttpKernel\TerminableInterface;
    8 use Symfony\Component\Runtime\RunnerInterface;
    9
    10 /**
    11 * The Drupal Kernel Runner.
    12 *
    13 * Is used by the DrupalRuntime to handle a single request in the context of a
    14 * set-up using PHP-FPM like nginx or Apache.
    15 */
    16 class DrupalKernelRunner implements RunnerInterface {
    • I covered this in the commit message, but it's good to include it here for visibility:

      A custom runtime is provided which knows how to create the Drupal specific runner that will use the DrupalKernel. The Drupal specific runner is currently the same as Symfony's HttpKernelRunner. However, it provides us with a place to put runtime specific code such as Revolt's eventloop.

      In theory I could make the change in another issue. However, if there's multiple issues that would want to make changes then they all need to do it and it becomes a race. So I decided to present the whole system here for understanding and scope the later changes to just that issue, rather than also to the Runtime system.

      There's also a subtle difference where Symfony's version has changes to flushng as of version 6 and up (we use ^7.2) and I was unsure what that would mean for Drupal, so I decided to stick with keeping Drupal's behaviour exactly.

    • Please register or sign in to reply
  • added 1 commit

    • ceab480a - Issue #3313404: Use symfony/runtime for less bespoke bootstrap/compatibility...

    Compare with previous version

  • Please register or sign in to reply
    Loading