Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:121792 Return-Path: Delivered-To: mailing list internals@lists.php.net Received: (qmail 15197 invoked from network); 23 Nov 2023 21:50:35 -0000 Received: from unknown (HELO php-smtp4.php.net) (45.112.84.5) by pb1.pair.com with SMTP; 23 Nov 2023 21:50:35 -0000 Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 191C618002B for ; Thu, 23 Nov 2023 13:50:39 -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-oo1-f49.google.com (mail-oo1-f49.google.com [209.85.161.49]) (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 13:50:37 -0800 (PST) Received: by mail-oo1-f49.google.com with SMTP id 006d021491bc7-58ceabd7cdeso671038eaf.3 for ; Thu, 23 Nov 2023 13:50:33 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1700776232; x=1701381032; 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=pT0Q8VzDs+p0axRsprwvV+N32045bZ9xKRWTaHq4nJ0=; b=UBRZnq2FAUA5iFJDGYqWTzYbB8opt61NvqOZwRSfY8D/NpZA40jLNbV8ZG2C7wuMCv leRD4MSEGD39cT3RQ/gFb0QC9V60QT9oZ+pAc77gS5Vc+jRW8W3wBJ46hcXkN+fLRh/r BYJAG74P1aFlybPenOKhp2JD9rzNLi+BCGIfpmf93J1H3T3xwACSIb9WdQ/58cal/4o/ JRkGsvttTLStxs5k4SFaAaBUPmxmoXBG08v0ZcXBU+p0umrW8luoCJMA+W+xtzNGzVHU piZuxrEaGn5XKSziApnwAjGfysjcAc16SVnVlEwfNhWeAdXkyC7+q5KnPAYfCwRPDhhm I5Mg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1700776232; x=1701381032; 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=pT0Q8VzDs+p0axRsprwvV+N32045bZ9xKRWTaHq4nJ0=; b=AO0SrcB6SqGyrf+drb6H/NpAqYsw44Icbu9cLgCCJ3hk/tKbF82cDpM1wzOUCIAyFW KrgSVZqQ4BYewBn+9023dLyGbAHwt1nBS+TS5gZlEXlGreg28lH5notIwYQXsh/TC7/m BrfTKUvQbK/1TzYnKkAlAu5KsPxwHr1AEmkusDdzPTpE0h3j8CjS3GRz8C8L1kLwmPdB EDGzd/yzwnQO3gUEGDs5D7wJUXPZKkeNDpF0w9ZmHuzy7ljxnYmXtEcSoPfKJttncZb7 300S4GArTPSa22b/f61iP9LBad+8j2eB4lbuqwI8G/j32EFyEQGf2luGfiZVt9/8e+7F o1dg== X-Gm-Message-State: AOJu0Ywoa9S1NA0SBOcl79nKHzGZe2yDZKf13u4l7NMCTqrtRqckRsri UBxSo0bZhs/45rvB6xx3ylOyoerfm7b40NglpwA= X-Google-Smtp-Source: AGHT+IEWSd9Nxnw7WnUh5vTp2TCeWZeX40br9fxHBTI1L6bUqUjqp2AnB6inoMKDWnNhSV1vMV51oB8z87QqZwdr2LY= X-Received: by 2002:a4a:3109:0:b0:58a:271f:1ab6 with SMTP id k9-20020a4a3109000000b0058a271f1ab6mr893164ooa.2.1700776232302; Thu, 23 Nov 2023 13:50:32 -0800 (PST) MIME-Version: 1.0 References: In-Reply-To: Date: Thu, 23 Nov 2023 22:50:18 +0100 Message-ID: To: Deleu 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 Thu, Nov 23, 2023 at 10:30=E2=80=AFPM Deleu wrote: > > > > On Thu, Nov 23, 2023 at 5:31=E2=80=AFPM Robert Landers wrote: >> >> Hello Internals, >> >> As you may know, an inherited method cannot reduce the visibility of >> an overridden method. For example, this results in a fatal error >> during compilation: >> >> class P { >> public function hello($name =3D 'world') { >> echo "hello $name\n"; >> } >> } >> >> class C extends P { >> private function hello($name =3D 'world') { >> parent::hello($name); >> echo "goodbye $name\n"; >> } >> } >> >> However, we can make certain methods private anyway, namely, >> constructors (I haven't gone hunting for other built-in methods yet). >> This is perfectly allowed: >> >> class P { >> public function __construct($name =3D 'waldo') { >> echo "hello $name\n"; >> } >> } >> >> class C extends P { >> private function __construct($name =3D 'world') { >> parent::__construct($name); >> echo "goodbye $name\n"; >> } >> } >> >> To my somewhat trained eye, this appears to violate the Liskov >> Substitution Principle, for example, this now can have hidden errors: >> >> function create(P $class) { >> return new (get_class($class))(); >> } >> >> proven by: >> >> $c =3D (new ReflectionClass(C::class)) >> ->newInstanceWithoutConstructor(); >> >> create($c); >> >> Even though we thought we knew that the constructor was declared public. >> >> I'd like to propose an RFC to enforce the covariance of constructors >> (just like is done for other methods), to take effect in PHP 9, with a >> deprecation notice in 8.3.x. >> >> I'm more than happy to implement it. >> >> Does anyone feel strongly about this one way or the other? > > > 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. Robert Landers Software Engineer Utrecht NL