Re: [PHP-DEV] Two-step creation for PHP modules

On 13 June 2026 11:31:43 BST, "Alex "Pierstoval" Rock" <pierstoval@gmail.com> wrote:

In my opinion this ...

So yeah, "it's private so you can't test it" still has to apply, as said, just like you shouldn't test your private class methods (even though PHP allows it with hacks).

... doesn't match this:

1. Allowing internal code units, not exposed to the outside of a package or module, but accessible from within it.
This is for maintainers to have a much easier way to define non-BC-covered code.

Just because a piece of functionality is internal to the library, doesn't mean it shouldn't be covered by unit tests. Indeed that's almost the definition of a unit test vs an integration test, if the library only exposes a narrow interface; and "refactor into a separate helper" is a common answer to "I want to unit test a private method".

Meanwhile this ...

3. Allowing multiple modules with same names to be loaded at the same time, as long as they are different files.

... basically answers this:

Containers as "code block that's executed in another thread" seems like something completely different than implementing modules, or am I missing something?

In order to reuse code written assuming a class name is unique (which is every line of PHP currently in existence) you need to define a boundary where code "inside" runs without affecting the code "outside", except through a specific interface. Whether that would literally be two separate threads, I'm not sure, but it certainly has a lot in common.

And yes, that is completely different from "modules" as units of scope. It needs to affect the *public* parts of a module, as well as the *private* parts; and it needs to affect *a group of modules* which rely on each other, not each in isolation.

Rowan Tommins
[IMSoP]

On 13 June 2026 16:32:08 BST, Michael Morris <tendoaki@gmail.com> wrote:

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.

A small correction: this is exactly the same way that namespaces work in Java, with exactly the same implication: the fully qualified class name has to be globally unique. Java's solution to isolating an entire set of names is apparently to define an "Isolated ClassLoader": What is an Isolated ClassLoader in Java? A Comprehensive Guide to Definition, Use Cases, and Maven Surefire Plugin Examples — javathinking.com

C# also has this model of namespaces, but allows you to isolate a set of names from a particular "assembly" using an "extern alias" statement: extern alias - C# reference | Microsoft Learn

I suspect both mechanisms rely on details of their respective runtimes that wouldn't translate to PHP, but in principle what we're looking for is something similar.

Rowan Tommins
[IMSoP]

Le 13/06/2026 à 19:14, Rowan Tommins [IMSoP] a écrit :

On 13 June 2026 16:32:08 BST, Michael Morris <tendoaki@gmail.com> wrote:

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.

A small correction: this is exactly the same way that namespaces work in Java, with exactly the same implication: the fully qualified class name has to be globally unique. Java's solution to isolating an entire set of names is apparently to define an "Isolated ClassLoader": What is an Isolated ClassLoader in Java? A Comprehensive Guide to Definition, Use Cases, and Maven Surefire Plugin Examples — javathinking.com

C# also has this model of namespaces, but allows you to isolate a set of names from a particular "assembly" using an "extern alias" statement: extern alias - C# reference | Microsoft Learn

I suspect both mechanisms rely on details of their respective runtimes that wouldn't translate to PHP, but in principle what we're looking for is something similar.

Rowan Tommins
[IMSoP]

Namespaces being just a globally accessible and define-able symbols system is exactly why, so far, a PHP package cannot define "private internal" code. The way the language works already allows autoload-hijacking in order to override code from external libs, or even hack the visibility (like removing "final" or "private" keywords before autoloading the file...).
After 18 years of PHP coding, including 15 years as a professional, I've seen a huge lot of legacy PHP projects that benefit the language's flexibility. Sure thing many of businesses could succeed thanks to being able to override almost everything (contrary to stricter languages), but maintaining these codebases with up-to-date versions of PHP, or 3rd-party libs, is so much of a hassle that, at some point, lots of maintainers are going more and more into adding new things into PHP that enforce a bit more strictness, opt-in of course. Some other libs (I'm thinking about EasyAdmin, in the Symfony ecosystem, for example) have even made the choice to close pretty much everything and provide "maintainable" public entrypoints, enforcing the Open/Close principle with a strict approach (immutable final classes, stricter options and arguments, etc.). It has its pros and cons, and can be frustrating for some devs, but the main goal was to make maintenance a bit easier for the core devs so that they are not forced to maintain BC on too many layers.

Introducing Modules, whether they are based on namespaces or not, would help maintainers, I think, and could reinvent how we think of PHP packages, while still keeping PHP what it has always been because it would be opt-in for maintainers.

On my side, I don't care if modules are tightly coupled to namespaces or not, and if more, that's even why my first idea was to bind a module to a single-file at first, and making all modules imported from such file being non-importable-again, thus creating a single module tree that can be stored at compile-time, have no side-effect at load-time (which is a big safeguard to avoid other issues), and allowing multiple modules of seemingly same name to be loaded in the memory at the same time, similarly to how Node.js allows having different versions of the same packages to be installed, while also allowing to have one version used by many modules too.

The only drawback of having modules like that would be that final dependency trees in Composer would be non-flat, but again, I think this non-flat system should be opt-in. Similarly to how flat systems are opt-in in the Node.js ecosystem (and trust me, I've tried to enforce "flat" dependencies resolutions in certain Node projects, and it's a huge hassle). Luckily, the PHP ecosystem is quite fine so far, compared to Node, and I would definitely keep the "flat" system in Composer for as long as possible, and make non-flat resolutions only case-specific (which is rare, and usually framework-dependent, like with Wordpress plugins for instance).

I still hope we could have a grouped discussion on this.

Larry's Modules proposal (here: php-rfcs/modules/spec-brainstorm.md at master · Crell/php-rfcs · GitHub ) is IMO a very good starting point, and it only lacks the "multiple modules with different versions". And module definition with an INI file looks interesting.

Adding a "container" system when importing a module can maybe allow this, I don't know...?

@Michael: do you think you could start a new thread for PHP containers? Do you already have some research that you could bring for a pre-RFC discussion?

On Mon, Jun 15, 2026 at 5:45 AM Alex Rock <pierstoval@gmail.com> wrote:

Le 13/06/2026 à 19:14, Rowan Tommins [IMSoP] a écrit :

On 13 June 2026 16:32:08 BST, Michael Morris <tendoaki@gmail.com> wrote:

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.
A small correction: this is exactly the same way that namespaces work in Java, with exactly the same implication: the fully qualified class name has to be globally unique. Java’s solution to isolating an entire set of names is apparently to define an “Isolated ClassLoader”: https://www.javathinking.com/blog/what-is-an-isolated-classloader-in-java/

C# also has this model of namespaces, but allows you to isolate a set of names from a particular “assembly” using an “extern alias” statement: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/extern-alias

I suspect both mechanisms rely on details of their respective runtimes that wouldn’t translate to PHP, but in principle what we’re looking for is something similar.

Rowan Tommins
[IMSoP]

Namespaces being just a globally accessible and define-able symbols
system is exactly why, so far, a PHP package cannot define “private
internal” code. The way the language works already allows
autoload-hijacking in order to override code from external libs, or even
hack the visibility (like removing “final” or “private” keywords before
autoloading the file…).

PHP has never kept people from shooting themselves in the foot. And in the past it’s came with some big guns too (Register Globals in PHP 3 anyone?). Enforcing namespace visibility will come at a cost in performance, and it won’t be enough to stop people from shooting themselves because PHP code is quite mutable. I don’t see much of a point in trying to guide people’s coding, especially in a language that doesn’t have a set paradigm. That is, in Java, you’re using classes. In PHP you can approach things that way, but you write purely procedurally. You can use the language as it was originally conceived - a templating language.

After 18 years of PHP coding, including 15 years as a professional, I’ve
seen a huge lot of legacy PHP projects that benefit the language’s
flexibility. Sure thing many of businesses could succeed thanks to being
able to override almost everything (contrary to stricter languages), but
maintaining these codebases with up-to-date versions of PHP, or
3rd-party libs, is so much of a hassle that, at some point, lots of
maintainers are going more and more into adding new things into PHP that
enforce a bit more strictness, opt-in of course. Some other libs (I’m
thinking about EasyAdmin, in the Symfony ecosystem, for example) have
even made the choice to close pretty much everything and provide
“maintainable” public entrypoints, enforcing the Open/Close principle
with a strict approach (immutable final classes, stricter options and
arguments, etc.). It has its pros and cons, and can be frustrating for
some devs, but the main goal was to make maintenance a bit easier for
the core devs so that they are not forced to maintain BC on too many layers.

Introducing Modules, whether they are based on namespaces or not, would
help maintainers, I think, and could reinvent how we think of PHP
packages, while still keeping PHP what it has always been because it
would be opt-in for maintainers.

When you say modules, what are talking about, because multiple languages implement “modules” in different ways. JavaScript is the most well known, but Go also has modules and they are quite different and support versioning out of the box. In each of these examples there is a requirement that the programmer write code to import the modules at some place either in the project (Go) or in every bloody file in the project (JavaScript). PHP doesn’t require this, instead autoloading symbol definitions as they are encountered.

Any package/module/whatchamacallit system used in PHP needs to build on this principle, not swim against it, if it is to have any hope of consideration on the list.

Also, I think the following is important to point out - Composer is NOT a package manager. It is a dependency manager. There’s a subtle difference between the two.

On my side, I don’t care if modules are tightly coupled to namespaces or
not, and if more, that’s even why my first idea was to bind a module to
a single-file at first, and making all modules imported from such file
being non-importable-again, thus creating a single module tree that can
be stored at compile-time, have no side-effect at load-time (which is a
big safeguard to avoid other issues), and allowing multiple modules of
seemingly same name to be loaded in the memory at the same time,
similarly to how Node.js allows having different versions of the same
packages to be installed, while also allowing to have one version used
by many modules too.

NPM (not Node, node is the engine not the package/dependency manager) does this using aliases. PHP doesn’t have a way to alias symbols at compile time. It would be nice if it did - I’ve suggested it as a parameter to include not knowing at the time of the suggestion that include isn’t a function, it’s a statement, so include "file.php" is the same as include("file.php"). Setting that aside, PHP projects rarely execute the include themselves - they let an autoloader handle it, so the autoloader must know which version to load. This isn’t something that even NPM does - it just aliases and makes both libraries available. The caller still must specify which library is being pulled in.

Node modules have a single point of entry, defined in the package.json file but usually index.js. In most Node packages this index.js imports all the public parts of the package and immediately exports them. Further, an import in node is an object (as is everything in JavaScript) that corresponds to that file. Hence

import { foo, bar } from ‘MyPackage’;

This is using JavaScripts dereference mechanic to pluck members of an object into the current symbol table. I could go on, but I think it’s more on topic to just say that Node’s import/export mechanic, elegant as it looks, is highly bound to JavaScript’s scope rules and how its symbol table works. These aren’t compatible with PHP.

For one, PHP has two symbol tables - one for variables (which is why all variables start with $ ) and one for everything else. Variables are affected by function scope, nothing else is. For example, if you define a function within a function in JavaScript that function will be private to the function it is defined in - it can’t be seen in other scopes. In PHP the function will be visible on the symbol table.

For another, Node gives each file its own module scope. To move a symbol from one file to another you should use export and import. This creates a clear chain of custody for objects and symbols, but by God it also creates a LOT of boilerplate that the PHP community has no interest in replicating.

Without the chain of custody you rapidly run into trouble. It doesn’t matter if you try to allow the user to put a prefix on the namespace as I once suggested, or have PHP’s engine prefix it automagically somehow - you still need to correctly disambiguate every blasted reference to that namespace. This is no easy task - take a look at the code of Strauss - https://github.com/BrianHenryIE/strauss That project allows you to rewrite a package with a new namespace, in effect doing it in userland. But as a precaution it prefixes every single package imported and severs the tie back to the original packagist. Strauss is used by several WordPress plugins to make composer library use possible while still remaining portable between WordPress sites.

The only drawback of having modules like that would be that final
dependency trees in Composer would be non-flat, but again, I think this
non-flat system should be opt-in. Similarly to how flat systems are
opt-in in the Node.js ecosystem (and trust me, I’ve tried to enforce
“flat” dependencies resolutions in certain Node projects, and it’s a
huge hassle). Luckily, the PHP ecosystem is quite fine so far, compared
to Node, and I would definitely keep the “flat” system in Composer for
as long as possible, and make non-flat resolutions only case-specific
(which is rare, and usually framework-dependent, like with Wordpress
plugins for instance).

I don’t think it’s a reasonable ask of Composer or any other dependency manager to try to resolve this without having a clear binding map like the one JavaScript forces you to create by how its module system works.

I still hope we could have a grouped discussion on this.

Larry’s Modules proposal (here:
https://github.com/Crell/php-rfcs/blob/master/modules/spec-brainstorm.md )
is IMO a very good starting point, and it only lacks the “multiple
modules with different versions”. And module definition with an INI file
looks interesting.

Adding a “container” system when importing a module can maybe allow
this, I don’t know…?

@Michael: do you think you could start a new thread for PHP containers?
Do you already have some research that you could bring for a pre-RFC
discussion?

This is the Container thread - I changed the subject line on my previous reply although I did include (was: ) so that people could track I was forking.

I don’t have much to add beyond my previous post. I haven’t heard from anyone with engine experience about the feasibility or difficulty of what I propose. To review (for anyone just joining but missing the previous message)

  1. The user signals to the engine they want to include a php file AND process it independent of the current execution environment. No shared user defined symbols or variables.
  2. That file returns an interface so that the calling thread can talk to the child.
  3. As the processes reside on different threads these calls will need to be asynchronous to be optimal, otherwise the caller will be in a blocked state while the child executes the called method. It can work without the PHP Async / Await RFC’s conclusion, but it won’t be in ideal form without it.

Containers provide process isolation. Say I have a WordPress plugin that I want to use composer libraries with. Say, Guzzle. I can write my own container interface without touching Guzzle’s code exposing the parts of Guzzle I need for my plugin. Since WordPress has no current core composer support I would need to carry a loader in my distro to be ran on the install hook of the plugin, bringing in composer and letting it in turn pull the libraries that I need to use. If someone later writes a plugin that uses an incompatible version of Guzzle that doesn’t matter - even if they lazily load it into the main symbol table by not using a container. My plugin is isolated.

That isolation comes at a price. Each plugin has an independent vendor directory with no way to resolve complete redundancy. At least not in WordPress, which has no current composer implementation in core (I’m on the wp trac in a thread discussing it though).

There are open questions though that must be answered before this container idea is seriously explored. Most critical, can PHP be made to handle an object passing from one thread to another? That is, continuing from the example above, can a client instance from one GuzzleContainer coexist on the main thread with a client instance from a different container and referencing different code? Or will methods be unable to cross the barrier? What are the implications for the Reflection API and using it to examine an object that originated from another process thread referencing symbols that don’t exist on the current thread?

The idea of containers is simple to state - but the implications are profound and might be unworkable. I simply don’t know enough about the PHP engine to know.

Le 16/06/2026 à 06:45, Michael Morris a écrit :

On Mon, Jun 15, 2026 at 5:45 AM Alex Rock <pierstoval@gmail.com> wrote:

    Namespaces being just a globally accessible and define-able symbols
    system is exactly why, so far, a PHP package cannot define "private
    internal" code. The way the language works already allows
    autoload-hijacking in order to override code from external libs,
    or even
    hack the visibility (like removing "final" or "private" keywords
    before
    autoloading the file...).

PHP has never kept people from shooting themselves in the foot. And in the past it's came with some big guns too (Register Globals in PHP 3 anyone?). Enforcing namespace visibility will come at a cost in performance, and it won't be enough to stop people from shooting themselves because PHP code is quite mutable. I don't see much of a point in trying to guide people's coding, especially in a language that doesn't have a set paradigm. That is, in Java, you're using classes. In PHP you can approach things that way, but you write purely procedurally. You can use the language as it was originally conceived - a templating language.

I think it's morally good to bring new features to the core that disallow someone from shooting themselves in the foot. Readonly classes, property hooks, etc., are methods for maintainers to be able to stricten their code so that end users don't shoot themselves in the foot. Yet we have the Reflection API that permits a huge lot of things, and combining with autoload hacking or bounded closures, it means that all these requirements, strictening tools, etc., are *contracts* we sign with maintainers. If someone uses any hacky tool built in PHP to override its native behavior, it's at their own risk. But the language allows it.

With my suggestion for modules, having all structures being declared globally but with a prefix still allows someone to hack into it. It just lowers the pressure on how it's developed in the core, in order for it to be both simple to use, but also simpler to implement. Doesn't prevent feet shots, but has a big warning sign nonetheless.

When you say modules, what are talking about, because multiple languages implement "modules" in different ways. JavaScript is the most well known, but Go also has modules and they are quite different and support versioning out of the box. In each of these examples there is a requirement that the programmer write code to import the modules at some place either in the project (Go) or in every bloody file in the project (JavaScript). PHP doesn't require this, instead autoloading symbol definitions as they are encountered.

There are different terms available for the things we talk about, so as a reminder to the two/three people reading this message:

  * Module = file, similar to JS. This implies that everything defined
    in the file is private, unless "publicly exposed", either via a
    "public" keyword (as Larry suggests in his modules-brainstorming
    document), or an "export" statement (similar to JS/TS).
  * Module = package, in that case, this means that a module is composed
    of several files, but a module/package needs an entrypoint to define
    its components. Larry's aforementioned idea is about using an INI
    file as entrypoint that prevents double-loading of a list of files,
    but this could be implemented with other methods: my proposal for
    "module = file" could add a "package" system without a need for an
    INI file, and based purely on the list of publicly available
    structures from the main imported file, but implies a bidirectional
    relationship between the "main package file" (aka "entrypoint") and
    the "package-included-only file". It's just a different approach.

None of these approaches removes namespaces, and considering my first idea and re-thinking it after all the comments in here, I think focusing on "using namespaces for that" is definitely the best approach. My proposal still stands, it just needs an update to better use namespaces for that need.

NPM (not Node, node is the engine not the package/dependency manager) does this using aliases. PHP doesn't have a way to alias symbols at compile time. It would be nice if it did - I've suggested it as a parameter to include not knowing at the time of the suggestion that include isn't a function, it's a statement, so `include "file.php"` is the same as `include("file.php")`. Setting that aside, PHP projects rarely execute the include themselves - they let an autoloader handle it, so the autoloader must know which version to load. This isn't something that even NPM does - it just aliases and makes both libraries available. The caller still must specify which library is being pulled in.

Node modules have a single point of entry, defined in the package.json file but usually index.js. In most Node packages this index.js imports all the public parts of the package and immediately exports them. Further, an import in node is an object (as is everything in JavaScript) that corresponds to that file. Hence

import { foo, bar } from 'MyPackage';

This is using JavaScripts dereference mechanic to pluck members of an object into the current symbol table. I could go on, but I think it's more on topic to just say that Node's import/export mechanic, elegant as it looks, is highly bound to JavaScript's scope rules and how its symbol table works. These aren't compatible with PHP.

I'm a bit familiar with Node.js's import model. PHP could be partly compatible with this in the future, but complete scoped encapsulation of a single PHP file is too much of a paradigm change for PHP itself. My proposal is making this "private" by prepending a null character and hash to all "included modules" whatever their kind, so that it /feels/ private, but still behaves similarly to how PHP currently works.
Less maintenance for the core team, zero BC break (purely additive and opt-in), can be transparent for the end-user (and *is* transparent for transient dependencies), still overridable and hackable by developers. Some of the reasons why PHP became famous in the first place.

For one, PHP has two symbol tables - one for variables (which is why all variables start with $ ) and one for everything else. Variables are affected by function scope, nothing else is. For example, if you define a function within a function in JavaScript that function will be private to the function it is defined in - it can't be seen in other scopes. In PHP the function will be visible on the symbol table.

For another, Node gives each file its own module scope. To move a symbol from one file to another you should use export and import. This creates a clear chain of custody for objects and symbols, but by God it also creates a LOT of boilerplate that the PHP community has no interest in replicating.

Without the chain of custody you rapidly run into trouble. It doesn't matter if you try to allow the user to put a prefix on the namespace as I once suggested, or have PHP's engine prefix it automagically somehow - you still need to correctly disambiguate every blasted reference to that namespace. This is no easy task - take a look at the code of Strauss - GitHub - BrianHenryIE/strauss: Prefix PHP namespaces and classnames to allow multiple versions of libraries to exist without conflict. · GitHub That project allows you to rewrite a package with a new namespace, in effect doing it in userland. But as a precaution it prefixes every single package imported and severs the tie back to the original packagist. Strauss is used by several WordPress plugins to make composer library use possible while still remaining portable between WordPress sites.

Interesting, and auto-prefixing namespaces for module=packages systems is IMO the easiest way to implement this without breaking the compiler nor the engine. It keeps everything as-is, and visually provides all that's needed for a "modules/packages" system to work: encapsulated private structures, an explicit public API, and the ability to load the same structures in the same namespaces because they are internally hidden with a hash, similarly to anonymous classes.

    The only drawback of having modules like that would be that final
    dependency trees in Composer would be non-flat, but again, I think
    this
    non-flat system should be opt-in. Similarly to how flat systems are
    opt-in in the Node.js ecosystem (and trust me, I've tried to enforce
    "flat" dependencies resolutions in certain Node projects, and it's a
    huge hassle). Luckily, the PHP ecosystem is quite fine so far,
    compared
    to Node, and I would definitely keep the "flat" system in Composer
    for
    as long as possible, and make non-flat resolutions only case-specific
    (which is rare, and usually framework-dependent, like with Wordpress
    plugins for instance).

I don't think it's a reasonable ask of Composer or any other dependency manager to try to resolve this without having a clear binding map like the one JavaScript forces you to create by how its module system works.

Composer doesn't have to change, because modules might be autoloadable. Whatever the solution found: if it's a bidirectional relationship (meaning any included file contains information on the "package" it's in, guiding to the entrypoint) or with a config file (like with an INI file), it can be built-in, and Composer would be compatible as long as loaded structures are public and not internal to the module.

This is the Container thread - I changed the subject line on my previous reply although I did include (was: ) so that people could track I was forking.

I don't have much to add beyond my previous post. I haven't heard from anyone with engine experience about the feasibility or difficulty of what I propose. To review (for anyone just joining but missing the previous message)

1. The user signals to the engine they want to include a php file AND process it independent of the current execution environment. No shared user defined symbols or variables.
2. That file returns an interface so that the calling thread can talk to the child.
3. As the processes reside on different threads these calls will need to be asynchronous to be optimal, otherwise the caller will be in a blocked state while the child executes the called method. It can work without the PHP Async / Await RFC's conclusion, but it won't be in ideal form without it.

Containers provide process isolation. Say I have a WordPress plugin that I want to use composer libraries with. Say, Guzzle. I can write my own container interface without touching Guzzle's code exposing the parts of Guzzle I need for my plugin. Since WordPress has no current core composer support I would need to carry a loader in my distro to be ran on the install hook of the plugin, bringing in composer and letting it in turn pull the libraries that I need to use. If someone later writes a plugin that uses an incompatible version of Guzzle that doesn't matter - even if they lazily load it into the main symbol table by not using a container. My plugin is isolated.

That isolation comes at a price. Each plugin has an independent vendor directory with no way to resolve complete redundancy. At least not in WordPress, which has no current composer implementation in core (I'm on the wp trac in a thread discussing it though).

There are open questions though that must be answered before this container idea is seriously explored. Most critical, can PHP be made to handle an object passing from one thread to another? That is, continuing from the example above, can a client instance from one GuzzleContainer coexist on the main thread with a client instance from a different container and referencing different code? Or will methods be unable to cross the barrier? What are the implications for the Reflection API and using it to examine an object that originated from another process thread referencing symbols that don't exist on the current thread?

The idea of containers is simple to state - but the implications are profound and might be unworkable. I simply don't know enough about the PHP engine to know.

This seems completely out of a "modules" scope to me: threading comes with a huge cost (like state interoperability, thread healthchecks, memory usage, and many other things), and this brings a lot of complexity that is not needed yet IMO. If someone wants to "sandbox" PHP somehow, it's already possible to execute another PHP script from inside a PHP script via CLI, and passing arguments to it, but there's no back-and-forth communication, only a "request-response-like" system.

On Tue, Jun 16, 2026 at 10:29 AM Alex Rock <pierstoval@gmail.com> wrote:

Le 16/06/2026 à 06:45, Michael Morris a écrit :

On Mon, Jun 15, 2026 at 5:45 AM Alex Rock <pierstoval@gmail.com> wrote:

Namespaces being just a globally accessible and define-able symbols
system is exactly why, so far, a PHP package cannot define “private
internal” code. The way the language works already allows
autoload-hijacking in order to override code from external libs, or even
hack the visibility (like removing “final” or “private” keywords before
autoloading the file…).

PHP has never kept people from shooting themselves in the foot. And in the past it’s came with some big guns too (Register Globals in PHP 3 anyone?). Enforcing namespace visibility will come at a cost in performance, and it won’t be enough to stop people from shooting themselves because PHP code is quite mutable. I don’t see much of a point in trying to guide people’s coding, especially in a language that doesn’t have a set paradigm. That is, in Java, you’re using classes. In PHP you can approach things that way, but you write purely procedurally. You can use the language as it was originally conceived - a templating language.

I think it’s morally good to bring new features to the core that disallow someone from shooting themselves in the foot. Readonly classes, property hooks, etc., are methods for maintainers to be able to stricten their code so that end users don’t shoot themselves in the foot. Yet we have the Reflection API that permits a huge lot of things, and combining with autoload hacking or bounded closures, it means that all these requirements, strictening tools, etc., are contracts we sign with maintainers. If someone uses any hacky tool built in PHP to override its native behavior, it’s at their own risk. But the language allows it.

With my suggestion for modules, having all structures being declared globally but with a prefix still allows someone to hack into it. It just lowers the pressure on how it’s developed in the core, in order for it to be both simple to use, but also simpler to implement. Doesn’t prevent feet shots, but has a big warning sign nonetheless.

When you say modules, what are talking about, because multiple languages implement “modules” in different ways. JavaScript is the most well known, but Go also has modules and they are quite different and support versioning out of the box. In each of these examples there is a requirement that the programmer write code to import the modules at some place either in the project (Go) or in every bloody file in the project (JavaScript). PHP doesn’t require this, instead autoloading symbol definitions as they are encountered.

There are different terms available for the things we talk about, so as a reminder to the two/three people reading this message:

  • Module = file, similar to JS. This implies that everything defined in the file is private, unless “publicly exposed”, either via a “public” keyword (as Larry suggests in his modules-brainstorming document), or an “export” statement (similar to JS/TS).
  • Module = package, in that case, this means that a module is composed of several files, but a module/package needs an entrypoint to define its components. Larry’s aforementioned idea is about using an INI file as entrypoint that prevents double-loading of a list of files, but this could be implemented with other methods: my proposal for “module = file” could add a “package” system without a need for an INI file, and based purely on the list of publicly available structures from the main imported file, but implies a bidirectional relationship between the “main package file” (aka “entrypoint”) and the “package-included-only file”. It’s just a different approach.

None of these approaches removes namespaces, and considering my first idea and re-thinking it after all the comments in here, I think focusing on “using namespaces for that” is definitely the best approach. My proposal still stands, it just needs an update to better use namespaces for that need.

NPM (not Node, node is the engine not the package/dependency manager) does this using aliases. PHP doesn’t have a way to alias symbols at compile time. It would be nice if it did - I’ve suggested it as a parameter to include not knowing at the time of the suggestion that include isn’t a function, it’s a statement, so include "file.php" is the same as include("file.php"). Setting that aside, PHP projects rarely execute the include themselves - they let an autoloader handle it, so the autoloader must know which version to load. This isn’t something that even NPM does - it just aliases and makes both libraries available. The caller still must specify which library is being pulled in.

Node modules have a single point of entry, defined in the package.json file but usually index.js. In most Node packages this index.js imports all the public parts of the package and immediately exports them. Further, an import in node is an object (as is everything in JavaScript) that corresponds to that file. Hence

import { foo, bar } from ‘MyPackage’;

This is using JavaScripts dereference mechanic to pluck members of an object into the current symbol table. I could go on, but I think it’s more on topic to just say that Node’s import/export mechanic, elegant as it looks, is highly bound to JavaScript’s scope rules and how its symbol table works. These aren’t compatible with PHP.

I’m a bit familiar with Node.js’s import model. PHP could be partly compatible with this in the future, but complete scoped encapsulation of a single PHP file is too much of a paradigm change for PHP itself. My proposal is making this “private” by prepending a null character and hash to all “included modules” whatever their kind, so that it feels private, but still behaves similarly to how PHP currently works.
Less maintenance for the core team, zero BC break (purely additive and opt-in), can be transparent for the end-user (and is transparent for transient dependencies), still overridable and hackable by developers. Some of the reasons why PHP became famous in the first place.

For one, PHP has two symbol tables - one for variables (which is why all variables start with $ ) and one for everything else. Variables are affected by function scope, nothing else is. For example, if you define a function within a function in JavaScript that function will be private to the function it is defined in - it can’t be seen in other scopes. In PHP the function will be visible on the symbol table.

For another, Node gives each file its own module scope. To move a symbol from one file to another you should use export and import. This creates a clear chain of custody for objects and symbols, but by God it also creates a LOT of boilerplate that the PHP community has no interest in replicating.

Without the chain of custody you rapidly run into trouble. It doesn’t matter if you try to allow the user to put a prefix on the namespace as I once suggested, or have PHP’s engine prefix it automagically somehow - you still need to correctly disambiguate every blasted reference to that namespace. This is no easy task - take a look at the code of Strauss - https://github.com/BrianHenryIE/strauss That project allows you to rewrite a package with a new namespace, in effect doing it in userland. But as a precaution it prefixes every single package imported and severs the tie back to the original packagist. Strauss is used by several WordPress plugins to make composer library use possible while still remaining portable between WordPress sites.

Interesting, and auto-prefixing namespaces for module=packages systems is IMO the easiest way to implement this without breaking the compiler nor the engine. It keeps everything as-is, and visually provides all that’s needed for a “modules/packages” system to work: encapsulated private structures, an explicit public API, and the ability to load the same structures in the same namespaces because they are internally hidden with a hash, similarly to anonymous classes.

The only drawback of having modules like that would be that final
dependency trees in Composer would be non-flat, but again, I think this
non-flat system should be opt-in. Similarly to how flat systems are
opt-in in the Node.js ecosystem (and trust me, I’ve tried to enforce
“flat” dependencies resolutions in certain Node projects, and it’s a
huge hassle). Luckily, the PHP ecosystem is quite fine so far, compared
to Node, and I would definitely keep the “flat” system in Composer for
as long as possible, and make non-flat resolutions only case-specific
(which is rare, and usually framework-dependent, like with Wordpress
plugins for instance).

I don’t think it’s a reasonable ask of Composer or any other dependency manager to try to resolve this without having a clear binding map like the one JavaScript forces you to create by how its module system works.

Composer doesn’t have to change, because modules might be autoloadable. Whatever the solution found: if it’s a bidirectional relationship (meaning any included file contains information on the “package” it’s in, guiding to the entrypoint) or with a config file (like with an INI file), it can be built-in, and Composer would be compatible as long as loaded structures are public and not internal to the module.

This is the Container thread - I changed the subject line on my previous reply although I did include (was: ) so that people could track I was forking.

I don’t have much to add beyond my previous post. I haven’t heard from anyone with engine experience about the feasibility or difficulty of what I propose. To review (for anyone just joining but missing the previous message)

  1. The user signals to the engine they want to include a php file AND process it independent of the current execution environment. No shared user defined symbols or variables.
  2. That file returns an interface so that the calling thread can talk to the child.
  3. As the processes reside on different threads these calls will need to be asynchronous to be optimal, otherwise the caller will be in a blocked state while the child executes the called method. It can work without the PHP Async / Await RFC’s conclusion, but it won’t be in ideal form without it.

Containers provide process isolation. Say I have a WordPress plugin that I want to use composer libraries with. Say, Guzzle. I can write my own container interface without touching Guzzle’s code exposing the parts of Guzzle I need for my plugin. Since WordPress has no current core composer support I would need to carry a loader in my distro to be ran on the install hook of the plugin, bringing in composer and letting it in turn pull the libraries that I need to use. If someone later writes a plugin that uses an incompatible version of Guzzle that doesn’t matter - even if they lazily load it into the main symbol table by not using a container. My plugin is isolated.

That isolation comes at a price. Each plugin has an independent vendor directory with no way to resolve complete redundancy. At least not in WordPress, which has no current composer implementation in core (I’m on the wp trac in a thread discussing it though).

There are open questions though that must be answered before this container idea is seriously explored. Most critical, can PHP be made to handle an object passing from one thread to another? That is, continuing from the example above, can a client instance from one GuzzleContainer coexist on the main thread with a client instance from a different container and referencing different code? Or will methods be unable to cross the barrier? What are the implications for the Reflection API and using it to examine an object that originated from another process thread referencing symbols that don’t exist on the current thread?

The idea of containers is simple to state - but the implications are profound and might be unworkable. I simply don’t know enough about the PHP engine to know.

This seems completely out of a “modules” scope to me: threading comes with a huge cost (like state interoperability, thread healthchecks, memory usage, and many other things), and this brings a lot of complexity that is not needed yet IMO. If someone wants to “sandbox” PHP somehow, it’s already possible to execute another PHP script from inside a PHP script via CLI, and passing arguments to it, but there’s no back-and-forth communication, only a “request-response-like” system.

However complex threading is, the complexity of aliasing is going to be higher. I’ve walked down this thought experiment several times in the last 4 years. Threading isn’t easy, but the work is in progress by Edmond HT - https://wiki.php.net/rfc/true_async

Containers, as I’m proposing them, rest on the foundation of this already in progress RFC which in turn builds on ReactPHP, AMPHP, Swoole and others.