Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:126614 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 qa.php.net (Postfix) with ESMTPS id 687441A00BC for ; Thu, 6 Mar 2025 23:14:53 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1741302737; bh=L70c3JXfPjI5IaSedOyOl4nHaLFbr0pe0Nw4HrrEYlE=; h=Date:From:To:In-Reply-To:References:Subject:From; b=jV6yts8fk2hlAseJNh/0M+CCT+9+PAGDejoedy4ChFZ4jJn9QC6t9+j472a0l3tmA JCaKedkeFbuOTlDT0ylBVg8BARBE6kL0F79WydWSMtkfJW9+ko1Q5VetcP3+9MQpxl rCQEIHtS25/l06UgHLYG34TnUQULUb+HFlR3D7MTdzm+X4qIKtbbLHcm+zsP9+tV29 WT4cOBU3nLZ7HUDbZFyvahyq775pSYWaOrKBsnmExT+rok4GPJXUEQ984XGOpkMJ8D 4o5MjwCLc+qQqN1ntCnNY0e4dCiXCWyBxRi3gJROnzhv8jtxBvCW5RGIMg8g3pDE4z J/jmU+abPV6fA== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 74421180081 for ; Thu, 6 Mar 2025 23:12:16 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 4.0.0 (2022-12-13) on php-smtp4.php.net X-Spam-Level: X-Spam-Status: No, score=-2.8 required=5.0 tests=BAYES_00,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.0 X-Spam-Virus: No X-Envelope-From: Received: from fout-b1-smtp.messagingengine.com (fout-b1-smtp.messagingengine.com [202.12.124.144]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by php-smtp4.php.net (Postfix) with ESMTPS for ; Thu, 6 Mar 2025 23:12:16 +0000 (UTC) Received: from phl-compute-11.internal (phl-compute-11.phl.internal [10.202.2.51]) by mailfout.stl.internal (Postfix) with ESMTP id 12DC1114011A; Thu, 6 Mar 2025 18:14:51 -0500 (EST) Received: from phl-imap-09 ([10.202.2.99]) by phl-compute-11.internal (MEProxy); Thu, 06 Mar 2025 18:14:51 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bottled.codes; h=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=fm2; t=1741302890; x=1741389290; bh=G/YkZPrnDT kNEVEdCY5RIGpHYcnZYAxL3ZOxeZyGF2M=; b=O5DHFOngObFVfIzHXgEPK3Zw/g e5lQ9XjNsKBqE95crGRiEsOU7iBh1Y7iYdLesWavZCLlEJAepSQYbMuRbVEJGqBZ eadrxFNcLNXoeS114l7u7I2COxm2DLqvfHeE0ODW7dbdoiwKxAELDDhT0XM86S8J RWFmxZ+rFA56ByakuqcbF0zQ3sISxVdlxXYIepKbEBg4mV8MBv8M/ZQ2ATgJR/oV hmbWSvKEfVqUojpAbHPOJ9rBs9W94z6Ma328976lWxuNNDvkvEFN3cun8uRPcOX3 MG853Fuf7KmmnrXOdC14WRwij6eUV7Zqe1MRMxE7rd2AI1Lp/aG4OIUBC+jA== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=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=fm1; t= 1741302890; x=1741389290; bh=G/YkZPrnDTkNEVEdCY5RIGpHYcnZYAxL3ZO xeZyGF2M=; b=du7ZWS87u4sJaJ+qjCqEmpJowcFAgDjoRaamvtcEm+E5GuXqr0O jeYOPPaf2bTVpCk1s/L/J11JNXdzldHOyn3XDL+f0ZVHJXodQiJx3gw75OKfThIF MXDYgGTWL2qQf1KOFho94g73dldFINqiAIJZy62RxLY7ERuI7E/TkBPNqfhyNwbq IdmJezIiKd1tVCa5QbvyrS6PVJlbnmfZwf2kbgCseaSc2YPrGj6MIGaNs4/54EUf CsKl9tqZ1RscGK5Orr/wMa+oWytOX3+isiedV4+Wq0l+Bo3UL6fbs902UwGRV8Bg SZJ+7JrzqqMPO2nHAJCANtlilY8WtE1ADVQ== X-ME-Sender: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeefvddrtddtgddutdeltdehucetufdoteggodetrf dotffvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdggtfgfnhhsuhgsshgtrhhisggv pdfurfetoffkrfgpnffqhgenuceurghilhhouhhtmecufedttdenucesvcftvggtihhpih gvnhhtshculddquddttddmnecujfgurhepofggfffhvffkjghfufgtsegrtderreertdej necuhfhrohhmpedftfhosgcunfgrnhguvghrshdfuceorhhosgessghothhtlhgvugdrtg houggvsheqnecuggftrfgrthhtvghrnheptdeujedttefhueelhfdtleeiudetlefftddu leehffegtdeihefhleeijefgveegnecuvehluhhsthgvrhfuihiivgeptdenucfrrghrrg hmpehmrghilhhfrhhomheprhhosgessghothhtlhgvugdrtghouggvshdpnhgspghrtghp thhtohepfedpmhhouggvpehsmhhtphhouhhtpdhrtghpthhtohepthhimhessggrshhtvg hlshhtuhdrsggvpdhrtghpthhtohepughoshhstghhvgdrnhhivghlshesghhmrghilhdr tghomhdprhgtphhtthhopehinhhtvghrnhgrlhhssehlihhsthhsrdhphhhprdhnvght X-ME-Proxy: Feedback-ID: ifab94697:Fastmail Received: by mailuser.phl.internal (Postfix, from userid 501) id 84F9B780068; Thu, 6 Mar 2025 18:14:50 -0500 (EST) X-Mailer: MessagingEngine.com Webmail Interface Precedence: bulk list-help: list-post: List-Id: internals.lists.php.net x-ms-reactions: disallow MIME-Version: 1.0 Date: Fri, 07 Mar 2025 00:14:22 +0100 To: =?UTF-8?Q?Tim_D=C3=BCsterhus?= , "Niels Dossche" , internals@lists.php.net Message-ID: In-Reply-To: References: <8303823b-a193-411f-bb60-c0221e7f29dc@bastelstu.be> Subject: Re: [PHP-DEV] RFC: short and inner classes Content-Type: multipart/alternative; boundary=6efea0f66826415b936540a6ea461b53 From: rob@bottled.codes ("Rob Landers") --6efea0f66826415b936540a6ea461b53 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable On Thu, Mar 6, 2025, at 23:31, Tim D=C3=BCsterhus wrote: > Hi >=20 > On 3/6/25 23:05, Rob Landers wrote: > >> > >> Closure::fromCallable('Outer::Inner::method'); > >=20 > > You end up with: > >=20 > > object(Closure)#1 (1) { > > ["function"]=3D> > > string(20) "Outer::Inner::method" > > } >=20 > Okay, does calling the closure work and correctly call the `method` on=20 > the inner class?=20 Yep, it works! > The question was intended to make sure that the=20 > implementation for callables uses the correct `::` to split.=20 This is largely why nesting can only be one level deep. Multiple levels = end up with far more intrusive changes to the engine and IMHO, severely = impact readability. I'll update the RFC on this. > Here's=20 > another one that might be interesting: >=20 > Closure::fromCallable(["Outer::Inner", "method"]); > Closure::fromCallable(["Outer", "Inner::method"]); object(Closure)#1 (1) { ["function"]=3D> string(20) "Outer::Inner::method" } and=20 Uncaught TypeError: Failed to create closure from callable: class "Inner= " not found I believe this is correct? I haven't seen this form since my 5.3 days, s= o I am not sure if it is correct or a bug. >=20 > >> constant('Outer::Inner'); > >=20 > > This returns: > >=20 > > string(12) "Outer::Inner" >=20 > Okay, so this behaves as a constant containing the class name. I assum= e=20 > it's with the full namespace if the outer class is namespaced? I'm not=20 > sure if I want this to work like this (i.e. whether this should be an=20 > error). That's correct, it contains the fully qualified name. I may have to thin= k on this, but I concur that this might be better as an error. >=20 > >> $inner =3D 'Inner'; > >> Outer::{$inner}; > >=20 > > This does nothing (but resolves to "Outer::Inner") >=20 > It's consistent with `constant()` and that's good. >=20 > >> =E2=80=A6 and any other meta-programming functionality working on c= lass > >> constants or static methods. > >> > >> Also, what will happen for: > >> > >> class P { > >> class Inner { } > >> } > >> > >> class C extends P { > >> const Inner =3D 'x'; > >> } > >> > >> (and vice versa) > >=20 > > This is a really good one. If for no other reason than I did a reall= y poor job of explaining resolution(?) in the RFC. `P::Inner` belongs to= `P`, not to `C`, so you can do `new C::Inner()` and it will resolve to = `P::Inner()`: >=20 > I don't think the RFC explains =E2=80=9Cresolution=E2=80=9D at all. Th= at's why I'm=20 > asking with those specific =E2=80=9Cedge-casey=E2=80=9D examples, so t= hat the RFC=20 > explicitly spells out the behavior. This is not something that should = be=20 > =E2=80=9Cimplementation defined=E2=80=9D, but something where an expli= cit design=20 > decision has been made. 100% agree. I'll update the RFC after thinking through some points in yo= ur email. >=20 > I also don't understand why `new C::Inner()` (w|sh)ould resolve to=20 > `P::Inner()`. I think my expectation of the code snippet above would b= e=20 > that it is an error. 100% agree. It actually should be an error, according to the RFC. To be = honest, until I wrote my response to you, I forgot you could redefine co= nstants. So, I'll have to take a look at this. >=20 > Likewise, LSP being ignored for inner classes raises an interesting=20 > question about the behavior of: >=20 > class P { > class Inner { > public function __construct(public string $foo) { } > } >=20 > public static function create() { > return new static::Inner('x'); > } > } >=20 > class C extends P { > class Inner { > public function __construct(public int $bar) { } > } > } >=20 > What happens if I call `C::create()`? This should also be specified in=20 > the RFC (and tested with a .phpt test). I'll add a test for it (and detail in the RFC), but it will instantiate = a C::Inner. This doesn't violate LSP, though, because C::Inner is distin= ct from P::Inner -- they are separate classes and completely unrelated. = This is just like using static properties or constants. To explain further, static::Inner is about the same as doing `new $class= ::Inner;`-ish. You are not referencing the same class depending on where= you are calling the function from. The fact that `static` is a shorthan= d for this is what makes it weird. I wouldn't be opposed to making `new static::Inner` (or even `new self::= Inner`) an error, since it is confusing; it would force people to spell = out the class name. However, I think it is useful when the different inn= er classes implement the same interfaces (explicitly or implicitly). I'd= be interested to hear thoughts on this, but I'm now wondering if static= :: allows casual violations of LSP, in general. =F0=9F=A4=94 >=20 > > As with other static things in PHP, you can do some really strange t= hings like this. This is similar to how you can redefine static constant= s in subclasses. >=20 > We should remove the number of strange things, not add to them. Truth.=20 Thank you for these questions Tim! =E2=80=94 Rob --6efea0f66826415b936540a6ea461b53 Content-Type: text/html; charset=utf-8 Content-Transfer-Encoding: quoted-printable

=
On Thu, Mar 6, 2025, at 23:31, Tim D=C3=BCsterhus wrote:<= br>
Hi

On 3/6/25 23:05, Rob Landers wrote:
&= gt;>
>>       Closu= re::fromCallable('Outer::Inner::method');

> You end up with:

&g= t; object(Closure)#1 (1) {
>    ["functi= on"]=3D>
>    string(20) "Outer::Inne= r::method"
> }

Okay, does = calling the closure work and correctly call the `method` on 
the inner class?

Yep,= it works!

The question was intended to make sure that the 
implementation for callables uses the correct `::` to split.
=

This is largely why nesting can o= nly be one level deep. Multiple levels end up with far more intrusive ch= anges to the engine and IMHO, severely impact readability. I'll update t= he RFC on this.

Here's 
another one that might be= interesting:

     Clos= ure::fromCallable(["Outer::Inner", "method"]);
  = ;   Closure::fromCallable(["Outer", "Inner::method"]);

object(Closure)#1 (1) {
["function"]=3D>
string(20) "Outer::Inner::method"
}

and
U= ncaught TypeError: Failed to create closure from callable: class "Inner"= not found

I believe this is correct? I haven't se= en this form since my 5.3 days, so I am not sure if it is correct or a b= ug.


>>     &nbs= p; constant('Outer::Inner');

>= This returns:

> string(12) "O= uter::Inner"

Okay, so this behaves as a con= stant containing the class name. I assume 
it's with = the full namespace if the outer class is namespaced? I'm not 
sure if I want this to work like this (i.e. whether this should= be an 
error).

=
That's correct, it contains the fully qualified name. I may have to= think on this, but I concur that this might be better as an error.


=
>>       $inner =3D 'Inne= r';
>>       Outer::{$= inner};

> This does nothing (b= ut resolves to "Outer::Inner")

It's consist= ent with `constant()` and that's good.

>= > =E2=80=A6 and any other meta-programming functionality working on c= lass
>> constants or static methods.
&= gt;>
>> Also, what will happen for:
>>
>>       cla= ss P {
>>       &= nbsp;   class Inner { }
>>  &nbs= p;    }
>>
>>&nbs= p;      class C extends P {
>&= gt;            co= nst Inner =3D 'x';
>>     &= nbsp; }
>>
>> (and vice versa)

> This is a really good one. If= for no other reason than I did a really poor job of explaining resoluti= on(?) in the RFC. `P::Inner` belongs to `P`, not to `C`, so you can do `= new C::Inner()` and it will resolve to `P::Inner()`:

<= /div>
I don't think the RFC explains =E2=80=9Cresolution=E2=80=9D at= all. That's why I'm 
asking with those specific =E2=80= =9Cedge-casey=E2=80=9D examples, so that the RFC 
exp= licitly spells out the behavior. This is not something that should be&nb= sp;
=E2=80=9Cimplementation defined=E2=80=9D, but somethin= g where an explicit design 
decision has been made.

100% agree. I'll update the RFC= after thinking through some points in your email.


I = also don't understand why `new C::Inner()` (w|sh)ould resolve to 
`P::Inner()`. I think my expectation of the code snippet ab= ove would be 
that it is an error.

100% agree. It actually should be an error, acco= rding to the RFC. To be honest, until I wrote my response to you, I forg= ot you could redefine constants. So, I'll have to take a look at this.

<= br>
Likewise, LSP being ignored for inner classes raises an in= teresting 
question about the behavior of:
<= div>
     class P {
&nbs= p;        class Inner {
            = ; public function __construct(public string $foo) { }
&nbs= p;        }

<= div>         public static funct= ion create() {
       &= nbsp;     return new static::Inner('x');
         }
&nbs= p;    }

   &n= bsp; class C extends P {
     &nb= sp;   class Inner {
    &nbs= p;        public function __construct= (public int $bar) { }
      =    }
     }
What happens if I call `C::create()`? This should also be s= pecified in 
the RFC (and tested with a .phpt test).<= br>

I'll add a test for it (and de= tail in the RFC), but it will instantiate a C::Inner. This doesn't viola= te LSP, though, because C::Inner is distinct from P::Inner -- they are s= eparate classes and completely unrelated. This is just like using static= properties or constants.

To explain furthe= r, static::Inner is about the same as doing `new $class::Inner;`-ish. Yo= u are not referencing the same class depending on where you are calling = the function from. The fact that `static` is a shorthand for this is wha= t makes it weird.

I wouldn't be opposed to = making `new static::Inner` (or even `new self::Inner`) an error, since i= t is confusing; it would force people to spell out the class name. Howev= er, I think it is useful when the different inner classes implement the = same interfaces (explicitly or implicitly). I'd be interested to hear th= oughts on this, but I'm now wondering if static:: allows casual violatio= ns of LSP, in general. =F0=9F=A4=94


> As with othe= r static things in PHP, you can do some really strange things like this.= This is similar to how you can redefine static constants in subclasses.=

We should remove the number of strange thi= ngs, not add to them.

Truth.&n= bsp;

Thank you for these questions Tim!

=E2=80=94 Rob
= --6efea0f66826415b936540a6ea461b53--