On Tue, Jun 16, 2026 at 10:29 AM Alex Rock <[email protected]> wrote:
> Le 16/06/2026 à 06:45, Michael Morris a écrit : > > > On Mon, Jun 15, 2026 at 5:45 AM Alex Rock <[email protected]> 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.
