Issue #3313404: Use symfony/runtime for less bespoke bootstrap/compatibility...
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
Activity
added 1 commit
added 1 commit
added 1 commit
added 1 commit
added 1 commit
added 1 commit
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). I've copy pasted this from GenerateAutoloadReferenceFile.php which was created in https://www.drupal.org/project/drupal/issues/2982684. The only change is a careful replace of autoload with autoload_runtime (since by Symfony's definition they should live next to each other).
I'm not sure if we want to make improvements only here and diverge the two files? The same question goes for other review feedback for comments in this file.
The way I understand it "root package" is a term borrowed from Composer, which would be apt in the context of a Composer plugin.
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
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). 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; The
[web-root]
is the web directory configured for DrupalScaffold. By default this isweb/
but many projects set it tohtml/
.I'll admit I don't understand the full functionality of
GenerateAutoloadReferenceFile::autoloadRuntimePath
. However, it has worked forautoload.php
and been reviewed by smart people, andautoload_runtime.php
will be created next toautoload.php
and the reference file should live next to the autoload reference file, so the logic they use should be identical.
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 changed this line in version 8 of the diff
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. changed this line in version 8 of the diff
- Resolved by Andrey Postnikov
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; To help your reading, this is covered by the Symfony Runtime configuration: https://symfony.com/doc/current/components/runtime.html#create-your-own-runtime
- The RunnerInterface::run(object $application) is called and it returns the exit status code as int.
- 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:
- Request starts at front-controller (e.g. index.php)
- Code is executed until autoload_runtime.php is called and execution is handed over
- autoload_runtime.php prevents itself from running multiple times by checking if autoload.php was required more than once
- autoload_runtime.php requires the file that the application was started with
- The front-controller is executed again
- require_once protects the autoloop_runtime re-entry from looping (and has the fallback within autoloop_runtime itself in case
require
is used). - The front-controller code runs through the entire file, returning the actual application set-up closure
- autoload_runtime.php performs its bootstrapping d. autoload_runtime.php executes the application using the selected runtime
- 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 return0
when theindex.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.
- autoload_runtime.php 0 → 100644
1 <?php 2 3 /** 4 * @file 5 * Includes the autoload_runtime created by the Symfony Runtime component. 6 * 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
orupdate.php
). See the current reference to autoload.php. The contents of theautoload_runtime.php
are dynamic and will ensure that the file itself points tovendor/autoload_runtime.php
regardless of where the web root is located.The
autoload_runtime.php
file is statically included in thedrupal/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.
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 { Why do we need this instead of https://github.com/symfony/runtime/blob/7.2/Runner/Symfony/HttpKernelRunner.php? I don't see much difference
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.
added 1 commit