Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:129182 X-Original-To: internals@lists.php.net Delivered-To: internals@lists.php.net Received: from php-smtp4.php.net (php-smtp4.php.net [45.112.84.5]) by lists.php.net (Postfix) with ESMTPS id E3BEB1A00C3 for ; Sun, 9 Nov 2025 20:31:53 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1762720318; bh=iKvoXHORIi0i6JIxT3+HwJYygmFw4gb0nU32TTDjoEI=; h=Date:From:To:Cc:In-Reply-To:References:Subject:From; b=fd0dAYUBqBBBYb80OZKuVIqPlivgdVETcxtGEfI4x+Sj2UNVdjDlF7EqbrErC01I5 CbPTnSXslxkgaj+xMXijlGH+k70CL82db2DDelXJsofLiSP8eCYWBF36+yQj9bkaKM NLmXzChAwg0DKy97ELWKVtViZX9b5cP5ViiGRRb61saiNX673Gx/DlHxL3TvCPbItR 9k11qLbFp1nw7hMUbxEh/6ztz/Uq9M0QgE3ZMy1BgWm6RH7qMe30l99QMw5pu0vcBK zd1PRyOF5EVaxuapRNrXTcAXrFm3Q4lAfcSHK7V7LavxoegGPVIb4rtMnj51xqDnHY S/v3QXUyf5pfQ== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 626D8180041 for ; Sun, 9 Nov 2025 20:31:56 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-25) on php-smtp4.php.net X-Spam-Level: X-Spam-Status: No, score=-0.1 required=5.0 tests=BAYES_50,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_MISSING,HTML_MESSAGE, RCVD_IN_DNSWL_LOW,SPF_HELO_PASS,SPF_PASS autolearn=no autolearn_force=no version=4.0.1 X-Spam-Virus: No X-Envelope-From: Received: from fhigh-b8-smtp.messagingengine.com (fhigh-b8-smtp.messagingengine.com [202.12.124.159]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by php-smtp4.php.net (Postfix) with ESMTPS for ; Sun, 9 Nov 2025 20:31:54 +0000 (UTC) Received: from phl-compute-12.internal (phl-compute-12.internal [10.202.2.52]) by mailfhigh.stl.internal (Postfix) with ESMTP id C55B87A0082; Sun, 9 Nov 2025 15:31:48 -0500 (EST) Received: from phl-imap-05 ([10.202.2.95]) by phl-compute-12.internal (MEProxy); Sun, 09 Nov 2025 15:31:48 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bottled.codes; h=cc:cc:content-type:content-type:date:date:from:from :in-reply-to:in-reply-to:message-id:mime-version:references :reply-to:subject:subject:to:to; s=fm1; t=1762720308; x= 1762806708; bh=WSA7AF+1L/+OO/9Kd6io+lJwbN8GKIbUGIwHF1vGCbs=; b=k 3IbiVqZ7XLxiYAQ+R6hX9SbAxt1C97bBnBI/lXFR7pr/ppPS07z6QEzFo/Mc9Lbs TLqNogR5djav86yWUCEmx0UHEBCQQP5bZwFerrTFtJ9I9yBhW7ZdjuUnMsp0vu/Y kBsBuxLAbrIRinKh2tnlMxEt0WibUBpihJwbXZ3jk1Ub6rWycqbAV40dGVkL7nrv S6SNAHuYrY+X9tpo8f+stifLGHq7Jm6qKOhrOdOjA3k+i//xcdREmzfkAUvpkaj/ sHEaUI11cl7jPGoJMQAw9oG61oVoqp3Goyy3S+yERGl7m6xIPwlN7ZsIvQYs6HPJ tr/K4h5zjJkY8Z8e61Enw== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:cc:content-type:content-type:date:date :feedback-id:feedback-id:from:from:in-reply-to:in-reply-to :message-id:mime-version:references:reply-to:subject:subject:to :to:x-me-proxy:x-me-sender:x-me-sender:x-sasl-enc; s=fm3; t= 1762720308; x=1762806708; bh=WSA7AF+1L/+OO/9Kd6io+lJwbN8GKIbUGIw HF1vGCbs=; b=ZApS52CyI33lwu0eEimOIXrxa6Zye4yqy0SgatJ1nZEx0QtsfLU ruqUggu1db4OS/OCJ8qVn9bxEVqrW4N9r/EoTAIbsqX+6hatfgOHlJLkaHtKq40j UbzabyhzhqykhfDHvwvlunDK2w4NOO6JpmM/dUO3sKR+bZgYlpVopb6jdPQkX+fR 0a/kUKAiZw1bZ31WW04E0hofGmwKvmecbK3zQt3R5TXEDDYGH0UHCKTV4QIUYEMM 5NQtLIhNAw0E+E4+M0ISl6ilobGEvzMTiXfgolARH08Zy+W8QNrQQgyhEeX4QLMM I8kFG/CluGMEctraDNin0ybjqvcwkpzwcBw== X-ME-Sender: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeeffedrtdeggdduleeifeelucetufdoteggodetrf dotffvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfurfetoffkrfgpnffqhgenuceu rghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmnecujf gurhepofggfffhvfevkfgjfhfutgesrgdtreerredtjeenucfhrhhomhepfdftohgsucfn rghnuggvrhhsfdcuoehrohgssegsohhtthhlvggurdgtohguvghsqeenucggtffrrghtth gvrhhnpeeiueethedvvdefjefhgfeiheelheehtdfhfeekjefflefgvedvkeduteejjedt tdenucevlhhushhtvghrufhiiigvpedtnecurfgrrhgrmhepmhgrihhlfhhrohhmpehroh gssegsohhtthhlvggurdgtohguvghspdhnsggprhgtphhtthhopeefpdhmohguvgepshhm thhpohhuthdprhgtphhtthhopehtihhmsegsrghsthgvlhhsthhurdgsvgdprhgtphhtth hopegurhgvrghlvggtshesghhmrghilhdrtghomhdprhgtphhtthhopehinhhtvghrnhgr lhhssehlihhsthhsrdhphhhprdhnvght X-ME-Proxy: Feedback-ID: ifab94697:Fastmail Received: by mailuser.phl.internal (Postfix, from userid 501) id 25D66182007A; Sun, 9 Nov 2025 15:31:48 -0500 (EST) X-Mailer: MessagingEngine.com Webmail Interface Precedence: list list-help: list-unsubscribe: list-post: List-Id: x-ms-reactions: disallow MIME-Version: 1.0 X-ThreadId: AZgO4gJIj6HZ Date: Sun, 09 Nov 2025 21:31:27 +0100 To: =?UTF-8?Q?Tim_D=C3=BCsterhus?= , =?UTF-8?Q?Alexandru_P=C4=83tr=C4=83nescu?= Cc: "PHP internals" Message-ID: <378db9d8-21e3-4faf-a6b0-eaec6e4560af@app.fastmail.com> In-Reply-To: <475570ea-ff3f-4eb2-bbe6-8f86760ac5e3@bastelstu.be> References: <7e17ab87-294f-450d-9afc-d23a5b919cc5@app.fastmail.com> <475570ea-ff3f-4eb2-bbe6-8f86760ac5e3@bastelstu.be> Subject: Re: [PHP-DEV] RFC: Namespace-Scoped Visibility for Methods and Properties Content-Type: multipart/alternative; boundary=018cb6da37c34a19a6e12537bbd40e8b From: rob@bottled.codes ("Rob Landers") --018cb6da37c34a19a6e12537bbd40e8b Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable On Sun, Nov 9, 2025, at 20:55, Tim D=C3=BCsterhus wrote: > Hi >=20 > On 11/9/25 20:41, Rob Landers wrote: > > class P { > > private(namespace) function x() {} > > } > >=20 > > class C extends P { > > protected function x() {} > > } > >=20 > > This behaves the same as overriding a private method with a protecte= d/public one today: the parent=E2=80=99s method is private to its declar= ing class, so the second example is allowed. >=20 > This is unsound. As we have established, neither `private(namespace)`=20 > nor `protected` is a subset of each other. >=20 > Specifically allowing this breaks the following (everything is declare= d=20 > in the same namespace): >=20 > class P { > private(namespace) function x() { } > } > class C extends P { > protected function x() { } > } >=20 > function f(P $p) { > $p->x(); // legal, because f is in the same namespace as P. > } >=20 > f(new C()); // breaks, because C::x() is protected and thus not=20 > legal to access from f / the global scope. >=20 > Best regards > Tim D=C3=BCsterhus Initially, I treated `private(namespace)` like `private` for cross-names= pace inheritance and overlooked how dynamic dispatch actually works in P= HP. 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=E2=80=99s visible and intended for= callers inside namespace A, the runtime dispatch would pick `C::x()`. T= hat completely and utterly destroys substitutability for namespace inter= nal 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 d= ifferent namespace simply cannot declare a method with the exact same na= me. It=E2=80=99d be a compile time error to prevent shadowing and keep d= ispatch predictable. I=E2=80=99d also be open to other ways as well (suc= h as always calling the namespaced method in the parent), but I=E2=80=99= d need to see if that=E2=80=99s even possible in the engine. 2. As you pointed out, `protected `and` private(namespace)` aren=E2=80=99= t compatible; thus we should only allow the same or a superset. So, it s= hould only allow `public` or `private(namespace)` when inheriting in the= same namespace. =E2=80=94 Rob --018cb6da37c34a19a6e12537bbd40e8b Content-Type: text/html; charset=utf-8 Content-Transfer-Encoding: quoted-printable
On Sun, Nov = 9, 2025, at 20:55, Tim D=C3=BCsterhus wrote:
Hi

On 11/9/25 20:= 41, Rob Landers wrote:
> class P {
> &nbs= p;    private(namespace) function x() {}
> }=
> class C extends P {
>&= nbsp;     protected function x() {}
> }=
> This behaves the same as overriding= a private method with a protected/public one today: the parent=E2=80=99= s method is private to its declaring class, so the second example is all= owed.

This is unsound. As we have established, = neither `private(namespace)` 
nor `protected` is a subset= of each other.

Specifically allowing this brea= ks the following (everything is declared 
in the same nam= espace):

     class P {
         private(namespac= e) function x() { }
     }
 = ;    class C extends P {
   &nbs= p;     protected function x() { }
 &n= bsp;   }

     fun= ction f(P $p) {
       &nbs= p; $p->x(); // legal, because f is in the same namespace as P.
<= div>     }

  &nbs= 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=C3=BCsterhus

Initially, I treated private(namespace) like private for cross-namespace inhe= ritance 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=E2=80=99s visible and intended for calle= rs inside namespace A, the runtime dispatch would pick C::x(). That completely and ut= terly destroys substitutability for namespace internal callers, which is= exactly what this visibility is meant to protect.

<= div>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 n= ame. It=E2=80=99d be a compile time error to prevent shadowing and keep = dispatch predictable. I=E2=80=99d also be open to other ways as well (su= ch as always calling the namespaced method in the parent), but I=E2=80=99= d need to see if that=E2=80=99s even possible in the engine.
<= br>
2. As you pointed out, protected and private(namespace) aren=E2=80=99t compatible; t= hus we should only allow the same or a superset. So, it should only allo= w public or <= code style=3D"border-top-width:1px;border-right-width:1px;border-bottom-= width:1px;border-left-width:1px;border-top-style:solid;border-right-styl= e:solid;border-bottom-style:solid;border-left-style:solid;border-top-col= or:#ccc;border-right-color:#ccc;border-bottom-color:#ccc;border-left-col= or:#ccc;border-image-source:initial;border-image-slice:initial;border-im= age-width:initial;border-image-outset:initial;border-image-repeat:initia= l;border-top-left-radius:3px;border-top-right-radius:3px;border-bottom-r= ight-radius:3px;border-bottom-left-radius:3px;background-image:initial;b= ackground-position-x:initial;background-position-y:initial;background-si= ze:initial;background-repeat:initial;background-attachment:initial;backg= round-origin:initial;background-clip:initial;background-color:#f6f6f6;fo= nt-family:menlo, consolas, monospace;font-size:90%;padding-top:1px;paddi= ng-right:3px;padding-bottom:1px;padding-left:3px;">private(namespace) when inheriting in the same namespace.

=E2=80=94 Rob --018cb6da37c34a19a6e12537bbd40e8b--