On Mon, Nov 10, 2025, at 20:19, Rob Landers wrote:
> 
> 
> 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

On my commute, I was exploring the syntax further and the feedback I've gotten 
so far. Specifically:

class A {
  local function x() {}
}

This is accssible only with the exact same namespace where it is declared (this 
RFC). I was reminded that this is very similar to "file private" but allows the 
boundary to extend across multiple files in the same namespace.

Then, we could have this in a followup RFC (which I've already started 
drafting) that doesn't actually require `local`  but provides a broader 
"package-like" scope without requiring PHP to define what a package is.

class A {
  namespace function x() {}
}

This would be accessible within the namespace it is declared, as well as parent 
and child namespaces (thus we don't make any assumptions about hierarchy).

Using the following namespace structure:
App\
├── Auth\
│   ├── SessionManager (declares namespace function validateToken())
│   ├── OAuth\
│   │   └── OAuthProvider
│   └── Session\
│       └── SessionStore
├── Billing\
│   └── PaymentProcessor
└── Controllers\
    └── LoginController

The following can access `validateToken()` :
- App\Auth\* (So, SessionStore/OAuthProvider)
- App\* (parent namespace)

But the following cannot access it:
- App\Billing\*
- App\Controllers\*
- \ (global namespace)

The global namespace is a special case where `namespace` and `local` behave 
exactly the same.

Before rewriting the RFC around this, I'd like to guage whether people feel 
like the keyword based approach is a clearer direction than 
`private(namespace)`.

— Rob

Reply via email to