Re: RFC: Namespace-Scoped Visibility for Methods and Properties
| From: | Rob Landers | Date: | Sun, 09 Nov 2025 20:31:27 +0000 |
| Subject: | Re: RFC: Namespace-Scoped Visibility for Methods and Properties | ||
| References: | 1 2 3 4 5 6 7 | Groups: | php.internals |
| Request: | Send a blank email to internals+get-129182@lists.php.net to get a copy of this message | ||
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