Containers. (was Re: [PHP-DEV] Two-step creation for PHP modules)

From: Date: Sat, 13 Jun 2026 15:32:08 +0000
Subject: Containers. (was Re: [PHP-DEV] Two-step creation for PHP modules)
References: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17  Groups: php.internals 
Request: Send a blank email to internals+get-131226@lists.php.net to get a copy of this message
On Sat, Jun 13, 2026 at 6:32 AM Alex "Pierstoval" Rock <pierstoval@gmail.com> wrote: > > Containers as "code block that's executed in another thread" seems like > something completely different than implementing modules, or am I missing > something? > It gives you what you're looking for without having to overhaul the language in any massive way. I made the prior post on my phone where it's hard to make a message, but I'm on my computer this morning so I can elaborate now. And while elaborating, I realized this probably needs it's own discussion thread entirely because it can solve not just the original plugin problem, but also the process workers problem if handled correctly. Let's review. PHP's ecosystem has packages already. It doesn't need modules as seen in JavaScript. What it needs in certain corner cases is better package isolation: The ability for a package to do its thing without disturbing or being disturbed by outside code. Drupal, Laravel, Symfony, et al do a great job of isolating their libraries, but they still have to register their class names and constants on the global name table. Under the hood Namespaces in PHP are a bit of a hack - they prepend the stated namespace to all labels declared in the file, and the use statement allows the fully qualified name of a class, function or constant to be stated only once, usually at the head of the file. It also allows aliasing, say if you have a "Parser" class out of two different packages - you can alias them as "FooParser" and "BarParser". It looks like Java or JavaScript importing, but it isn't. The global namespace of PHP, both for variables and constants, is limited to the current thread. PHP isn't natively multithreaded - there's a whole other RFC on async/await in progress. What I'm about to go over would benefit from that work, but it isn't a prerequisite to implementing containers. A container is an interface to a PHP process on another thread. It takes the form of an object that is returned by the included file. Where returns are optional for include/require, the new statement require_container mandates that an object be returned. I'd go even further and mandate that the object must implement a ContainerInterface that is added to PHP core. By virtue of running on its own thread a container has its own symbol table both for constants/classes/functions but also for variables. It is further restricted by having no superglobals - no $_REQUIRE, $_POST, $_GET, etc. If it needs to work with these then they'll have to come in through the interface from the outside code, or perhaps a PHP internal function can supply them with their own copy of these variables, but crucially any modification they make will not affect other containers. When PHP starts a container, it will cache the returned code and hand over a copy. This differs slightly from normal OP_Cache which caches the bytecode of the file itself so that new parsing isn't needed. It was at this moment I realized that two problems, not one, get solved by this. Let's start with the original though, package isolation. We'll use Guzzle for an example. The calling code <?php $guzzle = require_container('path/to/guzzle/container/class') ?> Now in PHP we've tried to get away from having to reference specific paths like this. Coming up with ways to resolve that path can be done in userland just as composer does this in userland. One example might look like this: <?php $guzzle = require_container(Container::getPath('Guzzle')) ?> The required file might look like this, at least in part <?php use GuzzleHttp\Client; require 'path/to/my/package/composer/autoloader'; return new GuzzleContainer implements Container { public static getClient($options) { return new Client($options); } } ?> And I trust the folk who know how the engine works can clearly see if this has a hope in Hell of working or not. I'm going to stop here for their input. If it does though, here's the other problem that might be solved. Here, we look at Drupal's existing entry index.php <?php use Drupal\Core\DrupalKernel; require_once 'autoload_runtime.php'; return static function () { return new DrupalKernel('prod', require 'autoload.php');}; ?> Without digging deeply, suffice to say that "new DrupalKernel" call kicks off a lot of config processing that has to be done before the request can even start to be processed, and this is redundant to every request. Quite a bit of caching is in play to reduce this burden as much as possible, but if we have a container system, we can fetch a pre-built worker and avoid all of it. <?php (require_container 'drupal_worker.php')->parse($_GET, $_POST, $FILES, $_SERVER, $_SESSION); ?> Remembering what I mentioned before, that the PHP Engine caches the worker's state at the moment it is returned to the calling file. So it can get its own autoloader setup and configuration parsing completely done and be in a ready state to handle the request. It would likely require some serious refactoring to fully take advantage of this, but the performance gains should be very real. Thoughts?

« previous php.internals (#131226) next »