Le 11/06/2026 à 08:52, Rowan Tommins [IMSoP] a écrit :
On 11 June 2026 01:23:29 BST, Morgan<[email protected]> wrote:
On 2026-06-11 03:24, Rowan Tommins [IMSoP] wrote:
Take Guzzle, for example; it has 43 source files within a specific namespace root. A
handful of those are marked "@internal", and a way for PHP to error if users
reference them directly would be useful. Some users end up wanting conflicting versions
of Guzzle simultaneously, e.g. in different WordPress plugins; so some way of isolating
or rewriting class names (and all their references) would be useful.
There are not 43 separate "modules", and the maintainers of Guzzle aren't going
to combine all of them into one file. Defining a single package with multiple files is
not a stretch goal, it's the only plausible starting point.
Just throwing this out, but couldn't the "module file" - the one that users
bring into their project and the one that exports what it's declared to export - delegate
the definitions of the functions etc. both public and private to other files?
I mean, PHP already _has_ an "include" statement...
It could, but that just moves the problem: right now, I can use any class in
Guzzle from anywhere in my application as long as I configure the right
autoloader. I can pass objects from Guzzle into and out of other libraries, and
they all agree on what each class name means.
Having to write an "import" statement means I have to be conscious of where I
"start" using Guzzle. If every import of the library creates a magically prefixed copy of
the entire library, I'm not even clear how I'd refer to different Guzzle classes in different files
of my application; let alone how libraries could pass those objects in and out and agree that they
were the same class.
Namespace visibility is really really easy; as if file visibility, if you want
that. Usable namespace rewriting is much more difficult, but doing it per file
makes it harder, not easier.
Namespace visibility could be implemented, but if you want to have some
public code that uses "include" to get access to some other private code
in the same namespace, or sub-namespace, then you would somehow hav to
specify to whom this namespace becomes visible. Extending the module
concept into "packages" makes sure that a package could have an
entrypoint that is publicly accessible, and all files in said package
would have to specify which package they are part of. But by design,
namespaces aren't dynamic, neither conditional (unless you define one
class, then register lots of aliases of this class in different
namespaces), so namespace visibility would still be easily "hackable"
without having to touch PHP's internals (like from using Reflection).
It's similar to namespace spoofing where you redefine a function in a
certain namespace just for the sake of overriding native behavior.
That's why some CS fixers enforce adding "use function" or add the "\"
root namespace to function calls, to prevent from overriding native
functions. So far, I don't see how namespace/file visibility would help
if you don't have a bi-directional definition system. That's usually
what Packages are for: they define their public entrypoints, and all the
rest is internal to the package and supposedly inaccessible from the
global scope.
Another potential workaround would be "Friend classes" (and there are
lots of discussions lately about it), but this system only works for
classes and cannot be applied to other structure types. It's not a bad
idea, but this enforces "hiding" all your internal logic in classes.
Constants would have to be internal static class constants, functions
would be internal static class functions, and so on. As said, it's not a
bad idea, but IMO this is a "workaround with PHP's existing tools", and
doesn't improve the language, it just improves the OOP part. And
wouldn't fix the "multiple versions of same package" issue either.
As Larry said, just stop trying to make file==module work. It's not the right
approach for PHP.
If you want to make namespace==module instead, this could maybe be a
nice approach for maintainers in making "some private code" in their
packages, but it's not enough:
- It still allows creating a namespace from anywhere else in order to
"hack" into it and publicly expose an API that wasn't supposed to be
exposed at all.
- It doesn't solve the issue of having the same package in different
versions, since namespaces will be the same
The file==module approach is imperfect, indeed. In Rust, you can create
tons of modules in one single file, each with their own private and
public code, but most of the times, developers create one file per
module, and the module's name is the filename itself. A Rust Crate (aka
"package") goes even further in allowing a module to expose its API
publicly only for the package. I think this is the best approach, and
it's safer than how JS modules are implemented. However, considering the
dynamic (instead of compile-only) nature of PHP, and considering the
ecosystem's occasional needs to have multiple versions for one package,
the addition of an internal hashed prefix for each module makes it
possible. It reuses everything that PHP already has, and it can be
almost transparent for the end-user: they won't write `use` but rather
`import` instead, and maybe use a local name instead of a FQCN (though
modules can define namespaces, therefore you can import a FCQN). And I
still think that using namespaces for that, and namespace visibility,
won't help, since they all register things in the global scope with the
same names, and doesn't fix the other issues.