Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:126459 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 D316B1A00BC for ; Tue, 18 Feb 2025 20:17:12 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1739909671; bh=wX4W1s7u2QEL7U2kvamTGAvLXhSSGXReJhcY+eT7Pn0=; h=Date:From:To:Cc:In-Reply-To:References:Subject:From; b=PcMPIbhBTV21J9+NwnKZQio9Y5cW9QjQ5woCV9JjluV2pY6eMY3aeiaekKWMqO42q YZX26eVxN/fGxaqZ2jslrJTgM7KNSxE/EhsWy9Yvtg9/K79w+yzB9hgxplhh64J0QO 5Ezox5NX5O0fjjxMHPuP9bdvUckF+AzemhyBdfRAwVu+2in4MkJ8Ab22famwYhHxAK jYa79ttDNWBFWf/9O26aTw29uVicxIotrmjkReYH7trYud2KOwhk6UCgfhiQcLVaDO g9/9S3mutJX5Kei1ihc0wOPma8RkoTFvT0LfsQehZ3D1YoN/agcZ6paLz4b634TBLk ty3ikMaC8k9FQ== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id B336B180059 for ; Tue, 18 Feb 2025 20:14:30 +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 fhigh-a3-smtp.messagingengine.com (fhigh-a3-smtp.messagingengine.com [103.168.172.154]) (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 ; Tue, 18 Feb 2025 20:14:30 +0000 (UTC) Received: from phl-compute-13.internal (phl-compute-13.phl.internal [10.202.2.53]) by mailfhigh.phl.internal (Postfix) with ESMTP id 15DF61140146; Tue, 18 Feb 2025 15:17:11 -0500 (EST) Received: from phl-imap-09 ([10.202.2.99]) by phl-compute-13.internal (MEProxy); Tue, 18 Feb 2025 15:17:11 -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=1739909831; x= 1739996231; bh=ycBYmA+2skOHUvIBN1LM4meiFjqIHuhwVu+MsGvkgS8=; b=B n5QWg/Kd00IUb8nMlVFrv3e0JiFesPRc39MmctpQB/EhYRqdJkmaUF/PDFj9G6DE 21PH56XZheCbPw17OXAEjsxp0WCYODOiTbPciVyoyyi0vFtr+n0SjmNb+aQgYYKk M0ZaPO0iWZC82F/rw+WjMj5kwGFihkZOkaSHUoThgkq6qIlO4YaFyrlOatbZQlr0 a9XH6btPpI4JqhayT+er3LPmr0vxHBas7tboMetAg6R2ShVVcavl/FHFWRqzAPtB jKNgCi2XVDel+ov57fTpfQxQqBmAzZ6SkCrq83inwpm5f2ZwhqEC4RIak2LPz74k IxrpjxzkuwCCQsov2TxDA== 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= 1739909831; x=1739996231; bh=ycBYmA+2skOHUvIBN1LM4meiFjqIHuhwVu+ MsGvkgS8=; b=yIAhF2cSY71HuDabS3l4wqrYJHScQkJx+0oCSraIj7EGSxLBda+ jwqjf3jHtxl7l/UhpZzH+nE1hpx+FsEuUP9YUrxVJ/qJJhBdtWus+rdJ7FFRnlm3 PLDZFKStVb6R3h2coS8EkAj1csR3CK1MtFujLeJOryQ2SJD/AugiP8/DHhnxy6fQ FubvTu/BU1gmv0wH1bwS92G5oXq0eqAYosXZsDlwde9lxdOCERNkMQVbJK+v6ebJ vNYwg7TO/6kmvjb/UVOW0MSxNA8F8N0vUg1B9XtZOiyEB04C7nUhkU+ffNDylsh+ 68T782nFkk+l5ZjTNMj0Exnq08oZ5tceXoQ== X-ME-Sender: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeefvddrtddtgdeivddviecutefuodetggdotefrod ftvfcurfhrohhfihhlvgemucfhrghsthforghilhdpggftfghnshhusghstghrihgsvgdp uffrtefokffrpgfnqfghnecuuegrihhlohhuthemuceftddtnecusecvtfgvtghiphhivg hnthhsucdlqddutddtmdenucfjughrpefoggffhffvvefkjghfufgtsegrtderreertdej necuhfhrohhmpedftfhosgcunfgrnhguvghrshdfuceorhhosgessghothhtlhgvugdrtg houggvsheqnecuggftrfgrthhtvghrnhepffffgfffudeileehvdegfedtueefgeehfefg hedvleekueeffeffledtieejhedtnecuffhomhgrihhnpegvgihtvghrnhgrlhhsrdhioh enucevlhhushhtvghrufhiiigvpedtnecurfgrrhgrmhepmhgrihhlfhhrohhmpehrohgs segsohhtthhlvggurdgtohguvghspdhnsggprhgtphhtthhopedvpdhmohguvgepshhmth hpohhuthdprhgtphhtthhopehjuhhrihhssehglhgrihhvvgdrphhrohdprhgtphhtthho pehinhhtvghrnhgrlhhssehlihhsthhsrdhphhhprdhnvght X-ME-Proxy: Feedback-ID: ifab94697:Fastmail Received: by mailuser.phl.internal (Postfix, from userid 501) id A2941780069; Tue, 18 Feb 2025 15:17:10 -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: Tue, 18 Feb 2025 21:16:29 +0100 To: "Juris Evertovskis" Cc: internals@lists.php.net Message-ID: <6192e713-c26e-407c-b107-31477989e7ad@app.fastmail.com> In-Reply-To: <2d86d841d82344bd5aa48053771e6294@glaive.pro> References: <04b901db8225$788d5250$69a7f6f0$@glaive.pro> <2d86d841d82344bd5aa48053771e6294@glaive.pro> Subject: Re: [PHP-DEV] Fully overridable factory methods Content-Type: multipart/alternative; boundary=c51fd96629744823a217cbfee12476fc From: rob@bottled.codes ("Rob Landers") --c51fd96629744823a217cbfee12476fc Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable On Tue, Feb 18, 2025, at 20:29, Juris Evertovskis wrote: > On 2025-02-18 19:31, Rob Landers wrote: >=20 >> =20 >> =20 >> On Tue, Feb 18, 2025, at 17:52, Juris Evertovskis wrote: >>> Hi, >>> =20 >>> A recent discussion brought up the need for multiple constructors fo= r the same class. >>> https://externals.io/message/126428 >>> =20 >>> The need was mostly answered by suggestions to use factory methods. >>> However on an earlier discussion it was discussed how it's fine and = even useful for constructor methods to violate the LSP. >>> https://externals.io/message/121789 >>> =20 >>> I've felt a similar need on some projects. To have a named factory m= ethod that would have an overridable signature. >>> =20 >>> Maybe a new keyword like `factory`that would make the method only ca= llable statically? I'm thinking of something like >>> =20 >>> class Triangle extends Polygon >>> { >>> =20 >>> public factory method fromPoints(Point $p1, Point $p2, Point= $p3): static >>> { >>>=20 >>>=20 >>> // ... >>> } >>> =20 >>> =20 >>> public factory method wrap(static|string $triangle): static >>> { >>>=20 >>>=20 >>> return $triangle instanceof static ? $triangle : static:= :fromWkt($triangle); >>> } >>> } >>> =20 >>> Sure, I'm not saying this API is currently impossible. I can do the = more narrow checks within the methods and throw from there. But sometime= s it's useful to precisely describe the acceptable arguments in the meth= od signature. >>> =20 >>> What do you think? Would such a feature be useless and/or harmful? >>> =20 >>> BR, >>> Juris >> =20 >> Hey Juris: >> =20 >> You may be interested in a super-draft RFC that I'll propose in the n= ext few days... (just fixing a couple of edge cases in the implementatio= n) it allows for nested classes and can support what you are thinking. I= will add this use case to the RFC. >> =20 >> Adapting your example: >> =20 >> class Triangle extends Polygon { >> public final class fromPoints extends Triangle { >> public function __construct(Point $p1, Point $p2, Point $p3) {} >> } >> =20 >> public final class wrap extends Triangle { >> public function __construct(Triangle $triangle) {} >> } >> } >> =20 >> $triange =3D new Triangle::fromPoints(...$points); >> =20 >> I don't know if this is what you are thinking of, but it basically al= lows the expression you are suggesting. If we were to implement your ide= a though, then nested classes would probably be impossible (and vice-ver= sa). >> =20 >> =E2=80=94 Rob > Hi Rob, >=20 > I'm a bit confused about the use of `final` in your example. >=20 The gist is that visibility applies to the outer class while final/reado= nly/etc apply to the inner class. So, in this case it is public (can ins= tantiate it from outside the class) and final (cannot extend it, which w= ould be weird to extend an inner class. I'm still debating if I want to = make inner classes final by default -- but that will probably come up in= the actual rfc discussion). There's already a thread discussing this topic from a few weeks ago, so = I don't mind discussing some details and "testing the waters", as they s= ay. > Would it allow defining Polygon::fromPoints such that it accepts varia= ble number of arguments and Triangle::fromPoints such that it only accep= ts exactly 3 args? >=20 > Or having a hierarchy like `Square extends Rectangle extends Polygon` = while having a wrap with the following covariant behaviour? > - Polygon::wrap(Polygon $poly) -- accepts any Polygons > - Rectangle::wrap(Rectangle $poly) -- accepts Rectangles and Squares, = but not other Polygons > - Square::wrap(Square $poly) -- only accepts Square arguments > Juris >=20 The inner class is its own class and belongs to the outer class -- it is= inherited similar to a static property. So to answer your question, yes= , all of that is possible. From inside the outer class you can even use = static:: or self:: or parent:: to access the inner classes as well, so i= t should be quite flexible for these types of hierarchies and allow for = some nice generic code reuse (e.g. parent::wrap($this)) =E2=80=94 Rob --c51fd96629744823a217cbfee12476fc Content-Type: text/html; charset=utf-8 Content-Transfer-Encoding: quoted-printable <= body>
On Tue, Feb 18, 2025, at 20:29, Juris Evertovskis wrote:

On 2025-02-18= 19:31, Rob Landers wrote:

 
 
<= /div>
On Tue, Feb 18, 2025, at 17:52, Juris Evertovskis wrote:

Hi,

 

A recent discussion brought up the need for multiple constructors f= or the same class.

https://externals.io/message/126428

 

The need was m= ostly answered by suggestions to use factory methods.

However on an earlier discussion it was discussed ho= w it's fine and even useful for constructor methods to violate the LSP.<= br>

https://exte= rnals.io/message/121789

 =

I've felt a similar need on some = projects. To have a named factory method that would have an overridable = signature.

 

Maybe a new keyword like `factory`that would make th= e method only callable statically? I'm thinking of something like

 

    class Triangle extends Polygon

    {

 

        pub= lic factory method fromPoints(Point $p1, Point $p2, Point $p3): static
        {

      = ;      // ...

        }

 

 
<= /p>

        public factory method= wrap(static|string $triangle): static
   &= nbsp;    {


            = return $triangle instanceof static ? $triangle : static::fromWkt($triang= le);

     &nbs= p;  }

   = }

 

Sure, I'm not saying this API is currently impossible. I c= an do the more narrow checks within the methods and throw from there. Bu= t sometimes it's useful to precisely describe the acceptable arguments i= n the method signature.

 
=

What do you think? Would such a featu= re be useless and/or harmful?

&nbs= p;

BR,

Juris

 
Hey= Juris:
 
You may be interested in a su= per-draft RFC that I'll propose in the next few days... (just fixing a c= ouple of edge cases in the implementation) it allows for nested classes = and can support what you are thinking. I will add this use case to the R= FC.
 
Adapting your example:
<= div> 
class Triangle extends Polygon {
=   public final class fromPoints extends Triangle {
&n= bsp;   public function __construct(Point $p1, Point $p2, Point $p3)= {}
  }
 
  pub= lic final class wrap extends Triangle {
    publ= ic function __construct(Triangle $triangle) {}
  }
}
 
$triange =3D new Triang= le::fromPoints(...$points);
 
I don't k= now if this is what you are thinking of, but it basically allows the exp= ression you are suggesting. If we were to implement your idea though, th= en nested classes would probably be impossible (and vice-versa).
 
=E2=80=94 Rob

Hi Rob,

I'm a bit confused abo= ut the use of `final` in your example.


The gist is that visibility applies to the outer class while fina= l/readonly/etc apply to the inner class. So, in this case it is public (= can instantiate it from outside the class) and final (cannot extend it, = which would be weird to extend an inner class. I'm still debating if I w= ant to make inner classes final by default -- but that will probably com= e up in the actual rfc discussion).

There's= already a thread discussing this topic from a few weeks ago, so I don't= mind discussing some details and "testing the waters", as they say.

Would it allow definin= g Polygon::fromPoints such that it accepts variable number of arguments = and Triangle::fromPoints such that it only accepts exactly 3 args?

Or having a hierarchy like `Square extends Rectangle extends Poly= gon` while having a wrap with the following covariant behaviour?
- Polygon::wrap(Polygon $poly) -- accepts any Polygons
<= div>- Rectangle::wrap(Rectangle $poly) -- accepts Rectangles and Squares= , but not other Polygons
- Square::wrap(Square $poly) -- o= nly accepts Square arguments

Juris

=
The inner class is its own class and belongs to the outer= class -- it is inherited similar to a static property. So to answer you= r question, yes, all of that is possible. From inside the outer class yo= u can even use static:: or self:: or parent:: to access the inner classe= s as well, so it should be quite flexible for these types of hierarchies= and allow for some nice generic code reuse (e.g. parent::wrap($this))

=E2=80=94 Rob
<= /body> --c51fd96629744823a217cbfee12476fc--