Stop caching app.root and site.path in DI containter

Created on 23 October 2024, about 1 month ago

Problem/Motivation

The Drupal kernel resolves the application root (app.root) and site path when initializing the container and sets them as parameters app.root and site.path in the container, with the following comment:

Drupal provides two dynamic parameters to access specific paths that are determined from the request.

But when those parameters are used as arguments for services, they are statically cached, like in the module_handler service:

  module_handler:
    class: Drupal\Core\Extension\ModuleHandler
    arguments: ['%app.root%', '%container.modules%', '@cache.bootstrap']
    tags:
      - { name: needs_destruction }

Those paths (especially app.root) actually can change per request, which is why they are explicitly set dynamically
on the container. Therefore they must not be statically cached, as that can break a Drupal site. It might not be obvious why the app.root path might change between two requests, but the most common case is a shared web hosting with chrooted SSH access. Usually the app root on such environments is in /var/www/vhosts/your-domain.tld/htdocs/web. The SSH access (and with it Drush) is chrooted and Drush\DrupalFinder\DrushDrupalFinder detects the app root as /htdocs/web.

Here, a HTTP request and a Drush command (which technically is handled as a request by Drupal as well) will have different app.root paths.

This is based on the PHP-FPM bug (#62279) explaining that the relevant server variables still unchrooted:

$_SERVER["SCRIPT_FILENAME"]
$_SERVER["PATH_TRANSLATED"]
$_SERVER["DOCUMENT_ROOT"]

Steps to reproduce

  • Setup a standard Drupal installation on a shared webhosting (like) environment with chrooted shell access.
  • Eventually set PHP error reporting to E_ALL and enable "display errors" to get the relevant PHP error messages.
  • Compare the outputs for "Drupal root" from drush status and browser based PHP Info or by displaying Drupal\Core\DrupalKernel::getAppRoot() via hook_requirements().

Execute drush cache:rebuild and then reload the site in the browser. This can crash the site.

I got the following error messages (in Drupal 10.3):

Warning: file_exists(): open_basedir restriction in effect. File(/httpdocs/web/modules/contrib/address/address.info.yml) is not within the allowed path(s): (/var/www/vhosts/your-domain.tld/:/tmp/:/var/lib/php/sessions) in Drupal\Core\Extension\Extension->__construct() (Line 73 in /var/www/vhosts/your-domain.tld/httpdocs/web/core/lib/Drupal/Core/Extension/Extension.php)

AssertionError: The file specified by the given app root, relative path and file name (/httpdocs/web/modules/contrib/address/address.info.yml) do not exist. in assert() (Line 73 in /var/www/vhosts/your-domain.tld/httpdocs/web/core/lib/Drupal/Core/Extension/Extension.php)

Further information: [Plesk Forum] How to set up chroot for php-fpm?

Proposed resolution

Resolve parameters %app.root% and %site.path% dynamically when instantiating services instead of caching the resolved values.

If that is not possible,

  • provide factory functions returning uncached values for %app.root% and %site.path% parameters and replace all usages in service definitions etc. in Core by calls to the corresponding factory functions.
  • Recommend all contrib modules to use the factory functions as well.

Remaining tasks

TODO

API changes

TODO

Release notes snippet

TODO

🐛 Bug report
Status

Active

Version

11.0 🔥

Component

base system

Created by

🇦🇹Austria mvonfrie

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

Production build 0.71.5 2024