On Sun, Nov 9, 2025, at 16:33, Larry Garfield wrote:
On Sat, Nov 8, 2025, at 5:13 PM, Rowan Tommins [IMSoP] wrote:
On 08/11/2025 21:56, Rob Landers wrote:
One advantage of keeping this RFC small is that it can be expanded
later without a BC break. For example, prefix matching (maybe
something like private(namespace: Acme\AuthLib)) or a
protected-namespace variant could be layered on top if the community
wants to go in that direction.
That’s a reasonable argument, but the risk is that if we don’t consider
what that future scope would look like, we can end up with syntax or
semantics that makes our lives unnecessarily difficult later. As I
understand it, this happened to some extent with the “readonly” keyword.
Exactly the example I was thinking of.
“Junior version first” approaches sometimes work out (FCC doesn’t seem to have caused issues for PFA in practice), and other times not (readonly was a major PITA for aviz, and even delayed it for a release as we figured out how they should interact). I’d much rather know what the “full solution” looks like and plan for it, even if it’s in incremental steps, than throw a junior version at the wall and hope for the best.
This RFC isn’t intended to solve the entire space; it’s the smallest
useful step that seems to fairly cleanly fit into PHP’s current model
without too much churn on the engine.
I think my fundamental question is: is it actually that useful? How
often, in practice, do people want exact namespace matching, vs
something more flexible?
In practice, the extra-class boundary I most often want is “my composer package.” I rarely need to protect things beyond that.
I am broadly in favor of extra-class visibility controls, whether they go through a module system or something else. Whether the current RFC is a good way of doing so, I am not convinced.
In particular, the syntax is a hard-no. It runs contrary to how the aviz syntax was defined, as others have already noted.
If you don’t want to use internal(set) to save internal for some other use, that’s fine. Come up with a different keyword.
But () is the syntax model that aviz established, and tossing extra parens in there just confuses things for everyone.
If it really does need to be an entirely separate dimension of scoping, then I would argue it’s too complex to capture in just keywords and Rowan’s attribute proposal becomes even more compelling.
That said, I’m not convinced it is a separate dimension yet. I’d put “nsviz” between public and protected, not protected and private. The assumption is that your “allowed” scope is something you control. So if you don’t want to use that property from child classes in your own namespace… just don’t. If you don’t want to allow it to be used by child classes in another namespace… then nsviz(get) solves that problem.
I will also note that while it’s typical for namespace and file path to be 1:1, there’s nothing that requires it. That means any strictly namespace-based visibility is trivial to bypass.
For example:
[snip]
Whether that is a design flaw or a feature is, I suppose, debatable.
–Larry Garfield
Hi Larry (and everyone else following the thread),
I think we’re all aligned that PHP would benefit from a proper module or package boundary. Many features people want — including namespace visibility — become cleaner once such a boundary exists. One doesn’t strictly require the other, but they definitely complement each other. The difficulty is that PHP currently has no agreed-upon definition of what a “module” or “package” is:
Because of that, there’s no existing boundary we can safely build on without first settling that much larger question. That’s why this RFC deliberately stays within a boundary the language already defines: the lexical namespace. It doesn’t try to define packages or introduce any new scoping model. It also doesn’t prevent a future RFC from doing so.
If the community reaches consensus on a formal module/package boundary later, namespace-level visibility can coexist with it or even be folded into it. Nothing in this RFC blocks that path.
What this RFC does is provide a useful, enforceable intermediate step that doesn’t require the community to solve “packages” first. If we require a full module system before adding any visibility improvements, we’re essentially saying that no incremental progress is acceptable until the hardest part of the problem is fully solved. That’s historically not how PHP evolves.
To keep this discussion focused:
If someone has concerns about this RFC’s semantics, performance, syntax, implementation, error messaging, interaction with inheritance/traits, or BC impact — that’s absolutely fair and I’d like to address them:
But I’d like to avoid turning this into a thread about defining a package/specification/module system. That’s a valid discussion, and I’d happily participate in another thread, but it’s not one this RFC is trying to solve.
Namespace visibility is intentionally small, intentionally conservative, and intentionally compatible with whatever direction modules eventually take. It’s the smallest useful step the language can take today, without blocking future work, while still being useful.
Now, to reply to your specific points about this RFC:
First, syntax vs. AViz.
Aviz establishes visibility(operation) as the pattern for asymmetric visibility, where the keyword controls the caller set and parentheses restrict the operation (get/set). That’s why private(namespace)(set) follows the same rule: the base visibility is still “private”, and the parentheses narrows who may call it.
If we introduced a standalone keyword like internal or nsviz, we’d effectively be adding a new visibility class, not a refinement of private and would bring its own semantics, collision issues, and interactions with any future module work. This RFC aims to minimise surface area, which is why it treats namespace visibility as a refinement.
That said, if the consensus emerges around a new keyword, that’s something I can adjust. The important behaviour is the caller rule, not the exact token spelling.
Second, is exact-match useful enough?
Yes, there are plenty of codebases where internal helpers live side-by-side in one namespace, especially in domain-driven or layered architectures where namespaces are the boundaries. Today, teams either make them public, wrap them in a service class, use @internal, or wire them together via reflection or debug_backtrace.
All of those come with tradeoffs. Exact-match namespace visibility offers a simple enforceable boundary without designing a whole package system or assuming anything about code hierarchy. It’s not the most expressive possible rule, but it’s useful and predictable.
If the community prefers prefix-based visibility or package-level visibility, that could be explored in a follow-up RFC. I’m not opposed to more expressive forms; I’m just not binding this RFC to a package model the language hasn’t defined yet.
And lastly, isn’t namespace visibility easy to bypass?
Yes — but so is private and protected. No PHP visibility is intended as a security boundary. This is a developer-intent boundary: encapsulation, static analysis, and making accidental misuse harder.
If a future module/package system introduces stronger enforcement, this RFC can layer underneath it without a conflict.
Sincerely,
— Rob