Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:121795 Return-Path: Delivered-To: mailing list internals@lists.php.net Received: (qmail 32286 invoked from network); 24 Nov 2023 04:07:01 -0000 Received: from unknown (HELO php-smtp4.php.net) (45.112.84.5) by pb1.pair.com with SMTP; 24 Nov 2023 04:07:01 -0000 Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id CD39A180048 for ; Thu, 23 Nov 2023 20:07:04 -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.9 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DMARC_MISSING,HTML_MESSAGE,RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H2,SPF_HELO_NONE,SPF_NONE autolearn=no autolearn_force=no version=4.0.0 X-Spam-Virus: No X-Envelope-From: Received: from mail-yw1-f180.google.com (mail-yw1-f180.google.com [209.85.128.180]) (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, 23 Nov 2023 20:07:04 -0800 (PST) Received: by mail-yw1-f180.google.com with SMTP id 00721157ae682-5cc77e23218so14936607b3.3 for ; Thu, 23 Nov 2023 20:06:59 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=newclarity-net.20230601.gappssmtp.com; s=20230601; t=1700798818; x=1701403618; darn=lists.php.net; h=references:to:cc:in-reply-to:date:subject:mime-version:message-id :from:from:to:cc:subject:date:message-id:reply-to; bh=OT68ol5uOWqImZSaIzjDr9ewv3kI5x9Ui/9xF8AVcW0=; b=H2RXNNlc+KkXnY8LAo145lA40vH0KdhcHztSldXQA/QYoQvZuMqO/1paETxEY0846N pN+4CNi2P4RU+zNWdpfCA8sbQyNwb6OzYSuXH+Jg5tzLB+nWIzP2///55sjcTwbyT43p 0GThRP83TUcpfbMhEYFw7t8/BmHU2sF/+tS1vKtRkckM29LLvMxQzxE4RF9kpKmqnd8O tYPPgLXlWvtMWx0UwzkIWG8L5XqDcIgQ5YdyNMPES3Y++atjv24vjwtl3ToC4JG0TVK+ m7K2d0RyXqJ9oI8Z5gdKyTe19BZnNL92aUC8sIffNalSMXzbEVnqU//VLttLiKCl2ZbL PeXg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1700798818; x=1701403618; h=references:to:cc:in-reply-to:date:subject:mime-version:message-id :from:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=OT68ol5uOWqImZSaIzjDr9ewv3kI5x9Ui/9xF8AVcW0=; b=ouMAviPlMfMWvuw8wLxHIRuWGOIISX51WBoHsHG5nj7z4bGRSgEEi3E405dPnLAUBu wkS8OLz7zDddlzpDv+WxJJYUoQ6amzga6jgkvsLd+Fm6hMH+7fGBoDejBj9cK2U2999y ZlkcD4ACXReH+ROvUIPNXGMk3WKey5gMnEEZ9WQ361s7UdbsK2pYE2FjHzH5sZBuE7ob hsyt3j8cXHW115X5eTXhmpwcd/KChWoF1l9U7PW4AaH6ydJ7VWXJdNPctahWMhHTIq8b 7Hz4wMMmGac2zUj/3TCoKiJubDXXtR2doN9JuBHu93TFijOEDYoU279ujZ1ROwSLAe2C Be+g== X-Gm-Message-State: AOJu0Yx3bq5BPOh4prVZJgGcI6dD9mwruhBuvuYDIWFWIHGpw8UdZ8mU Cglcc4ZfM9RRGZQH/bFvmHc+Iw== X-Google-Smtp-Source: AGHT+IEFfWZ+XirQlfNAdwTkCitaECw1/3lJ94vX0g9pEVIXzFE65or4XhUTW8B3flF9ZtiNRZ6lMQ== X-Received: by 2002:a81:48cd:0:b0:5ca:2691:10db with SMTP id v196-20020a8148cd000000b005ca269110dbmr1462874ywa.6.1700798818496; Thu, 23 Nov 2023 20:06:58 -0800 (PST) Received: from smtpclient.apple (c-98-252-216-111.hsd1.ga.comcast.net. [98.252.216.111]) by smtp.gmail.com with ESMTPSA id b126-20020a0df284000000b005c5a08c5c15sm777179ywf.136.2023.11.23.20.06.56 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Thu, 23 Nov 2023 20:06:57 -0800 (PST) Message-ID: Content-Type: multipart/alternative; boundary="Apple-Mail=_01C8F412-83D2-4870-8F17-E485D78CB933" Mime-Version: 1.0 (Mac OS X Mail 16.0 \(3696.120.41.1.4\)) Date: Thu, 23 Nov 2023 23:06:56 -0500 In-Reply-To: Cc: internals To: Robert Landers References: X-Mailer: Apple Mail (2.3696.120.41.1.4) Subject: Re: [PHP-DEV] [RFC][Discussion] Why can constructors violate LSP? From: mike@newclarity.net (Mike Schinkel) --Apple-Mail=_01C8F412-83D2-4870-8F17-E485D78CB933 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=utf-8 > On Nov 23, 2023, at 4:50 PM, Robert Landers = wrote: >=20 > On Thu, Nov 23, 2023 at 10:30=E2=80=AFPM Deleu = wrote: >>=20 >> Constructors are an implementation detail of a specialized class and = as such they're not subject to LSP because the goal of LSP is to be able = to make 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 think about LSP from a more pure type system, the = fact you're expecting an object of type C, but then you completely = disregard everything about the object 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 mathematical equations that prove that = 1 =3D 2, they all have one thing in common: they end up dividing by 0 at = some point. >>=20 >> OOP here dictates that you should reach for patterns like Builder, = Abstract Factory or similar. That way you constraint yourself to the = rules of OOP and you won't get weird outcomes. >>=20 >> =46rom another point of view, when a type is expected by a function = or method, all we can expect from it is whatever was defined as the = blueprint (class/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 constructor cannot break LSP because the constructor = is not part of the object's API from a "receptor" point of view. >>=20 >> I don't have a vote so take my opinion with a bucket of salt, but if = I could I would definitely vote against such RFC. >>=20 >>=20 >> -- >> Marco Deleu >=20 > Thanks Marco, >=20 > 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: >=20 > // from somewhere > $class =3D "C"; >=20 > if(is_subclass_of($class, P::class)) { > $example =3D new $class("world"); > } >=20 > 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 = class name to instantiate a class you are actually not using OOP, you = are using the text expansion capabilities of PHP. The fact that it = appears to be similar to a method call is just likely coincidence, Now if classes were first class objects in PHP, then there might be an = argument, 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 = can 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 = wrapper classes. Hope this helps. -Mike --Apple-Mail=_01C8F412-83D2-4870-8F17-E485D78CB933--