On Sun, Nov 9, 2025, at 22:32, Rob Landers wrote:
> 
> 
> On Sun, Nov 9, 2025, at 21:51, Tim Düsterhus wrote:
>> For this one I am however not sure if it ticks the “composes well” 
>> checkbox - that greatly depends on the syntax choice and how modules 
>> will look like if/when they eventually arrive.
> 
> I understand the concern. Composability matters a lot, especially for 
> features that touch visibility. My goal with this RFC is to take a boundary 
> PHP already has (the lexical namespace) and make it enforceable without 
> needing to answer the bigger "what’s a module/package?" question first.
> 
> Right now, different people in the ecosystem use namespaces in different 
> ways: some treat them as hierarchical, some as flat prefixes, some map them 
> to directory trees, some don’t. Trying to define prefix rules, 
> upward/downward access, or package-like confinement gets us right back into 
> the same conversation we’ve been stuck on. That’s why this RFC deliberately 
> picks the simplest rule PHP could enforce today: exact namespace equality.
> 
> If a future RFC defines modules/packages, namespace-visibility can either:
> - fold into that boundary,
> - be superseded by it, or
> - be used inside it (e.g. `internal` for modules, `private(namespace)` within 
> module internals).
> 
> Nothing in this RFC makes that harder.
> 
>> 
>> Your RFC appears to use the old template for the “RFC Impact” section 
>> which doesn't yet include the “Ecosystem Impact” subsection, but 
>> indicating that “significant OPcache changes” are required makes me 
>> wonder about the cost-benefit ratio.
> 
> Thanks! I’ll look at the new template and call out ecosystem impact (this was 
> originally written back in April/May?). On the OPcache point: "significant" 
> is probably overstating it. The change is limited to persisting one 
> additional interned string on zend_op_array and refcounting it correctly. The 
> cost is paid at compile time, not at call time, so runtime performance impact 
> should be negligible. I’ll reword this to be more precise.
> 
>> 
>> > 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.
>> 
>> As noted in my reply in the thread from Faizan, calling this a 
>> refinement of `private` is not really accurate / doesn't work in practice.
> 
> Agreed. After the discussion with you, Alex, and Larray, I think it's clearer 
> to describe `private(namespace)` as a distinct caller-set, not a subset of 
> protected or private. I’ll update the RFC text to reflect that and disallow 
> weird combinations (to be more clearly defined in the RFC).
> 
>> 
>> > 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.
>> 
>> To do so, the syntax would need to account for that. I have not yet seen 
>> a good proposal for that that doesn't end up as “symbol soup” that 
>> doesn't really fit the existing language syntactically.
> 
> My earliest version simply used `namespace`:
> 
> class P {
>   namespace function x() {} 
> }
> 
> It might make sense to return to that syntax if people don’t like the current 
> syntax. I don’t have a strong attachment to the exact spelling, what matters 
> is the semantics.
> 
> — Rob

I’ve updated the RFC and the implementation with some significant 
clarifications and corrections:
 • *Inheritance semantics now follow `protected` rather than `private`*
`private(namespace)` members are inherited and must follow normal 
signature-compatibility rules.
Visibility is enforced based on the declaring namespace rather than the 
inheritance hierarchy.
 • *Incompatible redeclarations are now clearly defined*
Transitions between `protected` and `private(namespace)` are disallowed in 
either direction.
This avoids unsound cases where substitutability would be broken for callers in 
the declaring namespace.
 • *Asymmetric visibility rules clarified*
`protected` and `private(namespace)` operate on different axes (inheritance vs 
namespace), so mixed AViz like
`protected private(namespace)(set)` is now a compile-time error.
 • *Expanded examples and error messages*
The RFC now includes clearer examples of the invalid cases, inheritance rules, 
and AViz combinations.
 • *Syntax moved to an explicit open issue*
Because the semantics now line up with `protected` rather than `private`, the 
spelling `private(namespace)` may not be ideal.
I’ve listed this in the “Open Issues” section and I'll include some previously 
considered alternatives that preserve the semantics here:
   • `namespace function x() {}`
   • `local function x() {}`
   • `private:ns function x() {}`
   • `protected:ns function x() {}`
My personal preference is toward the simpler forms (`namespace` or `local`), 
but I’d like to collect feedback before changing the RFC text.

Updated RFC:
https://wiki.php.net/rfc/namespace_visibility 
<https://wiki.php.net/rfc/namespace_visibility?utm_source=chatgpt.com>

Implementation:
https://github.com/php/php-src/pull/20421

Thanks to everyone who pointed out the inheritance edge cases; those surfaced 
issues that needed to be addressed. Further feedback is welcome.

— Rob

Reply via email to