Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:121799 Return-Path: Delivered-To: mailing list internals@lists.php.net Received: (qmail 49884 invoked from network); 24 Nov 2023 10:06:40 -0000 Received: from unknown (HELO php-smtp4.php.net) (45.112.84.5) by pb1.pair.com with SMTP; 24 Nov 2023 10:06:40 -0000 Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 4789A18002B for ; Fri, 24 Nov 2023 02:06:44 -0800 (PST) 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.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_PASS,FREEMAIL_FROM, RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H3,RCVD_IN_MSPIKE_WL,SPF_HELO_NONE, SPF_PASS autolearn=no autolearn_force=no version=4.0.0 X-Spam-Virus: No X-Envelope-From: Received: from mail-ot1-f54.google.com (mail-ot1-f54.google.com [209.85.210.54]) (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 ; Fri, 24 Nov 2023 02:06:43 -0800 (PST) Received: by mail-ot1-f54.google.com with SMTP id 46e09a7af769-6ce2c5b2154so988842a34.3 for ; Fri, 24 Nov 2023 02:06:38 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1700820398; x=1701425198; darn=lists.php.net; h=content-transfer-encoding:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:from:to:cc:subject:date :message-id:reply-to; bh=hOJGE7Ne/ZTXpuW+7X4U/UJdD1U8szmMvUj+HkzcYwY=; b=TyfVnCqSfq73/2lKJmbU+EUtQK2UUJ7oy4uKjtE1VO0S0X63CPwrE6kzDRRuENDA+e eLLw+zJmkjdmJES+qqnqLMz/dZeAwRSYZBxZVHRiYazNastrcqd7+WnSGmlW1KDpsfPx /sNRhxh456gDxJxAvZ0Ze5XoJn1Iq1Y/N16cbDPvp1Y52OnggX67caCvqaFW4nbnLR/A 58Ry/7/q11zHNvF4mfxS4q/pWBdCSQk8EmbdJpBP9jcuSoMn2QK2Q+t7TULdPmL5wXjF /9H3CftzvXXX5qviLZR0BWfMWQ/XVnEsorUuImujxdqI/UB41UP+7sDVxREkK9fD+vEp l35w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1700820398; x=1701425198; h=content-transfer-encoding:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=hOJGE7Ne/ZTXpuW+7X4U/UJdD1U8szmMvUj+HkzcYwY=; b=h7reKh4rt5pmsHDfAfouCR8p2WhX2KzwuQ7sVb0V2W6QyPbbqWadO2O/PrgmSnDzMB Ro2Al4Ywpf3yyvYJIregH7hjsR1v/jO8LcYgVzdcEw6Pveh/BpgeVNhjrsDMc6gZnVwx fyR3c40SgYehnR1krlsGdWW7voI6voli7+Rz0fe1r17LEoR3txF2QU5MBs7EJCc1xPjQ yvy3KHWB6Q/r7eXzv/DsILVNAyLH08oOyYr8VndCltL4bb83N+GJfIJ+O5WPekcTsNsi OQd+lo0uSoHDWJuyq5KTghVeOETvUZYrvwDkImoDS0/9YWdied+h5EZjIX1COPh5k7/3 20Hg== X-Gm-Message-State: AOJu0YymiHf2FpMwgscFj4sXgpVpA/ukce6ZIHebEn5proPToZc8ImTw mMRdg+qxwGviU7lr3AGMyXjcTPY/l51w7gcHJDmBw9hOKTw= X-Google-Smtp-Source: AGHT+IGzWZU+ywoD+UN1a9QBGSBJXT4oLUYIOYZWCprWS9ySlbnBL4tJvk3N4UFvGw8KrrpLntNS8ICXGAS/t03vxkg= X-Received: by 2002:a05:6871:4089:b0:1e9:badb:acaa with SMTP id kz9-20020a056871408900b001e9badbacaamr2913440oab.23.1700820397687; Fri, 24 Nov 2023 02:06:37 -0800 (PST) MIME-Version: 1.0 References: In-Reply-To: Date: Fri, 24 Nov 2023 11:06:26 +0100 Message-ID: To: Mike Schinkel Cc: internals Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable Subject: Re: [PHP-DEV] [RFC][Discussion] Why can constructors violate LSP? From: landers.robert@gmail.com (Robert Landers) On Fri, Nov 24, 2023 at 5:06=E2=80=AFAM Mike Schinkel = wrote: > > > > On Nov 23, 2023, at 4:50 PM, Robert Landers wr= ote: > > On Thu, Nov 23, 2023 at 10:30=E2=80=AFPM Deleu wrote= : > > > Constructors are an implementation detail of a specialized class and as s= uch they're not subject to LSP because the goal of LSP is to be able to mak= e sure that any object of a given type hierarchy can be used to accomplish = a certain behavior. If you take a step back from PHP's dynamic nature and t= hink about LSP from a more pure type system, the fact you're expecting an o= bject of type C, but then you completely disregard everything about the obj= ect itself and dive into it's metadata to build another object, that's the = moment you're no longer playing by the rules of OOP. It's like those mathem= atical equations that prove that 1 =3D 2, they all have one thing in common= : they end up dividing by 0 at some point. > > OOP here dictates that you should reach for patterns like Builder, Abstra= ct Factory or similar. That way you constraint yourself to the rules of OOP= and you won't get weird outcomes. > > From another point of view, when a type is expected by a function or meth= od, all we can expect from it is whatever was defined as the blueprint (cla= ss/interface) of that object and the __construct() is a special method that= is not assumed to be part of that blueprint because it's not reasonable to= do `$object->__construct();` after receiving an object. As such, a constru= ctor cannot break LSP because the constructor is not part of the object's A= PI from a "receptor" point of view. > > I don't have a vote so take my opinion with a bucket of salt, but if I co= uld I would definitely vote against such RFC. > > > -- > Marco Deleu > > > Thanks Marco, > > That's an interesting perspective and one I would agree with for the > most part, especially if you take my illustration at face value. Where > it gets weird/breaks down is when you have a class-string, that you > assert is the correct type, and then try to instantiate it: > > // from somewhere > $class =3D "C"; > > if(is_subclass_of($class, P::class)) { > $example =3D new $class("world"); > } > > If PHP didn't offer these built-in methods, then I would fully agree > with you, but it does, which puts it into a weird position where > sometimes a class is substitutable, and in this one special case, it > is not. > > > What Marco said pretty much mirrors the answer in Software Engineering SE= : > > https://softwareengineering.stackexchange.com/a/270738/9114 > > As if probably obvious now, when you are using a variable containing a cl= ass name to instantiate a class you are actually not using OOP, you are usi= ng the text expansion capabilities of PHP. The fact that it appears to be s= imilar to a method call is just likely coincidence, > > Now if classes were first class objects in PHP, then there might be an ar= gument, but alas, they are not. > > Anyway, I always addressed the problem you are running into by defining a= `make_new()` static method. Here's what your example might look after an = initial refactor: > > class P { > public $msg; > function __construct($msg){ > $this->msg =3D $msg; > } > } > class C extends P { > static function make_new($props){ > return new C($props["msg"]); > } > } > > $class =3D "C"; > > if(is_subclass_of($class, P::class)) { > $example =3D $class::make_new( ["msg"=3D>"world"] ); > print_r($example); > } > > Of course, getting it to production code will take a lot more, like you c= an see here (although this code has now been update for probably five years= since I am no longer coding in PHP professionally): > > https://github.com/search?q=3Drepo%3Awplib%2Fwplib%20make_new&type=3Dcode > > And if you are dealing with 3rd party classes, you'll need to write wrapp= er classes. > > Hope this helps. > > -Mike > Hey Mike, Using a static method to enforce LSP when it should have been enforced in the first place, merely proves my point that it violates LSP. I don't know how else to spell it out, I guess we can try this one next: class A { final public static function foo() { return new static(); } } class B extends A { public function __construct(private string $name) {} } $a =3D B::foo(); It just plainly violates LSP and you can't write safe code that touches constructors. I don't know how else to spell this out.