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 <allowed 
> scope>(<operation>) 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*:
 • Composer root namespaces?
 • Physical directories?
 • Compilation units?
 • Attributes?
 • Something else entirely?
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

Reply via email to