Le 11/06/2026 à 11:54, Rowan Tommins [IMSoP] a écrit :
On 11 June 2026 09:12:52 BST, Alex Rock<[email protected]> wrote:
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.
Why would you need that?
namespace visibility would still be easily "hackable" without having to touch
PHP's internals (like from using Reflection).
Visibility isn't a security tool, it's a helper to stop people making mistakes.
That's why some CS fixers enforce adding "use function" or add the "\" root
namespace to function calls, to prevent from overriding native functions.
I believe that's actually about performance: if you don't specify, the engine
has to try the lookup on every call, in case a namespaced function was
registered.
So far, I don't see how namespace/file visibility would help if you don't have
a bi-directional definition system.
It's really, really simple: you mark the internal parts "internal". Then you
use them from all the same places they're intended, inside the package's namespace; and
people using them outside those intended places get a helpful error message that they're
using the library wrong.
It's possible we also want to add a way to "seal" the namespace against new
definitions anyway, to help with optimisation. Namespaces, not files, would still be the
right starting point for that.
What if we have this:
<?php
// vendor/my-library/src/MyClass.php
namespace MyNamespace;
internal class MyClass {};
Fine code would look like this:
<?php
// index.php
function main() {
$o = new \MyNamespace\MyClass(); // Fatal Error: cannot create instance
of internal class MyNamespace\MyClass
outside of the MyNamespace namespace
}
And "not fine code" would look like this:
<?php
// index.php
namespace MyNamespace {
function createObject() {
return new \MyNamespace\MyClass();
}
}
namespace {
function main() {
$o = \MyNamespace\MyHack::createObject(); // Works.
}
}
Your "internal" system would be quite useless, because anyone would be
able to do it. It can even be done at the autoloader level if one
wanted. Doesn't use Reflection or such.
Then, it leads to your first question:
On 11 June 2026 09:12:52 BST, Alex Rock<[email protected]> wrote:
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.
Why would you need that?
Well, to solve the previously shown issue.
The "internal class" previously set (or moved to "internal namespace":
same issue) is useless, and it would kinda do the same thing as /**
@internal */ today: cannot be enforced at runtime, so not safe at all.
There's no need to add "internal class" if this system doesn't
internalize anything and is easily "hackable" from outside.
If you want to have *actual* internal code, there are only two ways to
achieve this:
- have code that's internal on said file, with the proposed "module"
system. Sure, it's only for a file, but it's better than nothing
- have a "package" system so that a package has an entrypoint that
defines private and public files, and said files expose which "package"
I repeat: since PHP is hugely flexible, especially via the whole
Reflection API, I don't mean to "make hacking impossible", because,
well, with PHP you can override everything. I mean to "make it as hard
and ugly as possible". Sure you can execute a private method in a
class/object by using either Reflection (or a bounded closure), but
these are explicit hacks.
With "just namespaces", you can just use the widely used autoload system
and create a class somewhere with a static method, or a global function
(like above) , it doesn't even look like a hack.
- 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.
Non-issue: as above, visibility is not a security tool.
If bad practices are easily accessible from a new feature, you will
expect lots of bad practices, because that's what lots of coders end up
doing. We all know that, and if you don't know it, I hope you'll realize
it some day. That's why "enforcing good practices" is a better approach,
because it doesn't disallow bad code, it just makes it harder to
produce, costing more energy, time, money, and technical debt.
- It doesn't solve the issue of having the same package in different versions,
since namespaces will be the same
A solution to that which requires hundreds of thousands of libraries to rewrite
all their code to use relative references rather than namespaced global names
isn't going to get very far.
I'll say it one more time, then stop: any solution needs to start with what we
have, and what we have is namespaces.
In that case, let's move directly to "packages" without talking about
"modules" at all, and make namespaces package-aware somehow. Or the
contrary: make a package implicitly create "protected" namespaces.
And use it like this for example:
<?php // vendor/some-package/package.php
define package MyPackage;
package_include MyPackage\MyClass from __DIR__.'/src/MyClass.php';
// ... and possibly all other includes
// (note: this is an idea, there might be another way.
// It could even be an spl_register_internal() function or anything similar
// that makes sure it's not possible to define package-bounded structures
// from outside the package definition file
<?php // vendor/some-package/src/MyClass.php
package MyPackage; // implies "namespace MyPackage;" too, to globally reference
non-internal structures
internal class MyClass {};
<?php // invalid include
include 'vendor/some-package/src/MyClass.php'; // Fatal error: package
"MyPackage" was not defined.
<?php // invalid instantiation
include 'vendor/some-package/src/package.php';
include 'vendor/some-package/src/MyClass.php'; // Just an appendage to the
MyPackage, could even be autoloaded
new MyPackage\MyClass(); // Fatal error: cannot create instance of internal
class MyClass
This way, once a `define package MyPackage` has been called, any further
file doing `package MyPackage` will throw an error
The "Package" thing is a bit more recommended, but IMO it needs a
bidirectional system.
Though, the concept of "modules" as I explained it, whether it uses
namespaces or not, makes "package-alike" systems possible, even if it's
in one single file.
Another "workaround" to define structures each in their file and merging
them together in a module would be a "merging script" system, but that's
ugly, and it is similar to the "compilation/transpilation" process in
Node.js, where your publicly available package is a "built" version of
your source code (what happens in NPM packages), rather than the source
code itself (like in PHP or Rust).
I think I won't convice you of anything, and so far there are not many
people participating in this discussion, so feel free to drop out if
it's annoying or boring for you, I won't take it personally :)