On Sun, Nov 9, 2025, at 20:55, Tim Düsterhus wrote:
> Hi
> 
> On 11/9/25 20:41, Rob Landers wrote:
> > class P {
> >      private(namespace) function x() {}
> > }
> > 
> > class C extends P {
> >      protected function x() {}
> > }
> > 
> > This behaves the same as overriding a private method with a 
> > protected/public one today: the parent’s method is private to its declaring 
> > class, so the second example is allowed.
> 
> This is unsound. As we have established, neither `private(namespace)` 
> nor `protected` is a subset of each other.
> 
> Specifically allowing this breaks the following (everything is declared 
> in the same namespace):
> 
>      class P {
>          private(namespace) function x() { }
>      }
>      class C extends P {
>          protected function x() { }
>      }
> 
>      function f(P $p) {
>          $p->x(); // legal, because f is in the same namespace as P.
>      }
> 
>      f(new C()); // breaks, because C::x() is protected and thus not 
> legal to access from f / the global scope.
> 
> Best regards
> Tim Düsterhus

Initially, I treated `private(namespace)` like `private` for cross-namespace 
inheritance and overlooked how dynamic dispatch actually works in PHP. This 
means that a same-named method in a child would simply be a new method. But 
like you point out, that leads to a subtle problem.

Even though `P::x()` is a method that’s visible and intended for callers inside 
namespace A, the runtime dispatch would pick `C::x()`. That completely and 
utterly destroys substitutability for namespace internal callers, which is 
exactly what this visibility is meant to protect.

So ... it seems we need a couple of additional rules here:

1. If a parent has a `private(namespace)` method, then a subclass in a 
different namespace simply cannot declare a method with the exact same name. 
It’d be a compile time error to prevent shadowing and keep dispatch 
predictable. I’d also be open to other ways as well (such as always calling the 
namespaced method in the parent), but I’d need to see if that’s even possible 
in the engine.

2. As you pointed out, `protected `and` private(namespace)` aren’t compatible; 
thus we should only allow the same or a superset. So, it should only allow 
`public` or `private(namespace)` when inheriting in the same namespace.

— Rob

Reply via email to