Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:128436 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 lists.php.net (Postfix) with ESMTPS id 3266D1A00BC for ; Fri, 8 Aug 2025 14:05:05 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1754661805; bh=ON/5IhnTrsAo6krP6aOdCiKm6y/VDSo5Rs35ZcmzEq4=; h=Date:From:To:In-Reply-To:References:Subject:From; b=oH/o4CioyCjhJ8n9OzV140C6rySCgeKT3ALDznr52YcUakvq58gyPsy7bbNGOCvTS ZvJUhGmOZ0VizrQzxOjNOpF8s8LkNdAM6CJsnmLd0NrVvR5pLEauGb30rmpiW5R0As fK1/eQXOrpXC+gfSs4IXbL6ySGHRYuvTSmF9jcfS7y2Cd3poiMrfqSNyhKBa8zJ38P 4T+iAU55uGRCatCpkDe+8kq7OUvlFqix4rQFHpza8jqOuey/4fP8RmD79dck2stsPO sCAqaKAKa40JJcT8Alg+FMCYcEhe2trGXRSrcAcGSKrGW3dk0Kxn9xQMoG4wjxz2KE Yj8A9migNZg6A== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id BC620180074 for ; Fri, 8 Aug 2025 14:03:24 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-25) on php-smtp4.php.net X-Spam-Level: X-Spam-Status: No, score=-0.1 required=5.0 tests=BAYES_50,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.1 X-Spam-Virus: No X-Envelope-From: Received: from fhigh-b5-smtp.messagingengine.com (fhigh-b5-smtp.messagingengine.com [202.12.124.156]) (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, 8 Aug 2025 14:03:14 +0000 (UTC) Received: from phl-compute-05.internal (phl-compute-05.internal [10.202.2.45]) by mailfhigh.stl.internal (Postfix) with ESMTP id 6BBB77A0120; Fri, 8 Aug 2025 10:04:52 -0400 (EDT) Received: from phl-imap-05 ([10.202.2.95]) by phl-compute-05.internal (MEProxy); Fri, 08 Aug 2025 10:04:52 -0400 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=fm1; t=1754661892; x=1754748292; bh=y7/+A7JO/W DXl9klT3KYo4/yuqcStK22QWnvI9XVQi8=; b=Kpu59YglnDCJfMj2VRohrWFdAT 71cL40NI13COFBjtbc0lPK4+q7wVOu0aykYmpQRm8clMRDhq/FJ9d1kgXtRrzI0B F88YO7ysDsro1Wi2ROS727eVGI+h+4u0IaWJL1aJCn3ie8k1jTeH1Gp/KlAE+Rtq nzIczIlcWMcWUiCbbR5Ii6erXW4yHQIIVhV7SztYHPe1eOJS/ggYU19AQaWpSa+8 F8XNGDQy4v9wvawxFI17CjUfKM+jPLfc+7VzrXSqD25cu/4ZpfNri1qhRJSa2Lvl MtPAk5+oZ13Du0AKE6D8tZiqQ5hrNKnen1d0kkDIJkcdPNyMvKaKutpFMJew== 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=fm3; t= 1754661892; x=1754748292; bh=y7/+A7JO/WDXl9klT3KYo4/yuqcStK22QWn vI9XVQi8=; b=T7w5OUmUyn7bkrSO2/QVldieHTuYR5tdiZ/Rg6jD58JsP+VKzKd ofeXW892ajI9rYDEM3HfSY+2dGo7MIogQwy2/RR2eCLu2CADU62cZPN3AUF4bCQr sNYvYJjLaTffLprQsWzLXRP054MP6E68OaLU9FnKNT3DA/gM/U+6Otpta+WhXxp1 fJ3C3NwqJCRXGOVtdgJNgtpNdx6p/DoDg5JyF0HDcSozK+h+6EVBp4+NQNHs6Lno MdB1zXEWGNTh2MMzFIz20qXgk2XjWAMbtePh+xdC5Kk7rGseZyFZT5H/Utu2wJmc GfWIvc92QjMVL/fgt1q3cMhtc1L5UlYVN3Q== X-ME-Sender: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeeffedrtdefgdduvdegtdduucetufdoteggodetrf dotffvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfurfetoffkrfgpnffqhgenuceu rghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmnecujf gurhepofggfffhvffkjghfufgtsegrtderreertdejnecuhfhrohhmpedftfhosgcunfgr nhguvghrshdfuceorhhosgessghothhtlhgvugdrtghouggvsheqnecuggftrfgrthhtvg hrnheptdeujedttefhueelhfdtleeiudetlefftdduleehffegtdeihefhleeijefgveeg necuvehluhhsthgvrhfuihiivgeptdenucfrrghrrghmpehmrghilhhfrhhomheprhhosg essghothhtlhgvugdrtghouggvshdpnhgspghrtghpthhtohepfedpmhhouggvpehsmhht phhouhhtpdhrtghpthhtohepihhnthgvrhhnrghlsheslhhishhtshdrphhhphdrnhgvth dprhgtphhtthhopeifvggvughprggtkhgvthesvhgrrhhtvghgrdhniidprhgtphhtthho pehhrghnshhkrhgvnhhtvghlseihrghhohhordguvg X-ME-Proxy: Feedback-ID: ifab94697:Fastmail Received: by mailuser.phl.internal (Postfix, from userid 501) id 6C7541820074; Fri, 8 Aug 2025 10:04:51 -0400 (EDT) X-Mailer: MessagingEngine.com Webmail Interface Precedence: list list-help: list-post: List-Id: x-ms-reactions: disallow MIME-Version: 1.0 X-ThreadId: ApgyNlIcRpMw Date: Fri, 08 Aug 2025 16:03:56 +0200 To: "Hans Krentel" , weedpacket@varteg.nz, internals@lists.php.net Message-ID: <64e8ee39-15ed-4f70-9c59-66f880db528b@app.fastmail.com> In-Reply-To: <1754655377927.1104458686.3594147701@yahoo.de> References: <2d2cc2d2-5e62-41da-b697-cd752e48b1f2@varteg.nz> <1754655377927.1104458686.3594147701@yahoo.de> Subject: Re: [PHP-DEV] Protected inheritance hierarchies Content-Type: multipart/alternative; boundary=fa559c3bdb7042f78dd5d5068c4d9a79 From: rob@bottled.codes ("Rob Landers") --fa559c3bdb7042f78dd5d5068c4d9a79 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable On Fri, Aug 8, 2025, at 15:11, Hans Krentel wrote: >=20 >=20 >=20 > On Friday 08 August 2025 00:49:27 (+02:00), Morgan wrote: >=20 > > On 2025-08-08 10:01, Rob Landers wrote: > > > For example, if |A::foo(): int| promises to always return a non-ze= ro integer, and a subclass overrides |foo()| to only return zero, that v= iolates the contract=E2=80=94even though the type signatures match perfe= ctly. This is the sort of semantic guarantee that LSP is about, and it i= s discussed in nearly every reputable textbook and conference talk on OO= design. Languages like PHP can=E2=80=99t enforce these contracts, but t= he / principle/ is what helps prevent subtle design bugs and behavioural= =E2=80=9Csurprises.=E2=80=9D > > > Indeed. Those contracts only become enforceable in the type signat= ure when your type system is robust enough to be able to express them. I= f you could declare |A::foo(): NonzeroInt| then the signature could prev= ent an overriding subclass C from returning zero from that function (whi= le still allowing |C::foo(): PositiveInt|, say); the compiler can verify= that the type of the expression C::foo() is written to return is of typ= e NonzeroInt (or PositiveInt). > >=20 > > In the absence of being able to declare such constraints in the type= system, one has to fall back on other documentation about what behaviou= r is and isn't acceptable. > >=20 >=20 > That is certainly always useful, especially when it was written down, = as it allows to read both, the fine-print, and between the lines. As you= have just quoted Rob's suggestion while replying to it, allow me the op= portunity to highlight a key sentence for me under the pretext of the ea= rlier quote, and the much appreciated association with documentation of = yours to illustrate that: >=20 > > [...] the / principle/ [here, LSP,] is what helps prevent subtle de= sign bugs and behavioural =E2=80=9Csurprises.=E2=80=9D >=20 > Documentation, e.g. of pre- and postconditions, loop (in)variants, cla= ss invariants, etc, we can already make use of those corner-stones of th= e LSP to reason about sub-types, the LSP can inspire us of what or how w= e document. And that's what I've learned these days from Rob: Without re= asoning about the LSP. I knew already for what I love Liskov for, but th= e LSP is so much a loaded thing in discussions, I had to get a couple of= things out of the way first to only understand Robs reasoning. I came h= ere by the error message, and was looking for what I was missing with it. >=20 > "Oh the types in PHP must do that for us, otherwise my scripts are doo= med." >=20 > No. >=20 > The compiler can only handle the type but not the sub-type, because of= the halting problem. >=20 > And while my programs can be doomed because of the halting problem (4.= Every program is a part of some other program and rarely fits.), I as h= uman am still able to reason about the correctness of my program. >=20 > Just my 2 cents >=20 > -- hakre To add to this, here=E2=80=99s one of my favorite examples, ironically, = one often used in beginner OOP tutorials: class Rectangle { public int $width; public int $height; } class Square extends Rectangle { public int $width { get =3D> $this->width; set =3D> $this->width =3D $= this->height =3D $value; } public int $height { get =3D> $this->height; set =3D> $this->height =3D= $this->width =3D $value; } } This is a classic LSP violation. Square changes Rectangle's contract by = linking width/height, removing the independence that Rectangle promised.= Meaning when we pass it as a Rectangle and we try to make the Square fu= ll screen, it will either be too short length (set width first) or too t= all (set height first). In other words, a "Square is a Rectangle" violat= es LSP, in practice. Yet, this is quite often taught as a beginner examp= le to OOP. Are there cases where you'd want to do this deliberately? Yes, probably.= Would you be surprised to find a rectangle extended to the edge of the = screen did not extend to the edge of the screen? Yes, probably. In fact,= it would probably be filed as a bug. Interestingly, your product people will tell you to "stretch" it (or per= form some other transform) making a Square behave like a Rectangle again= . You'd probably end up with something more akin to this: class Rectangle { public int $width; public int $height; public static function asSquare(int $widthAndHeight): static { /* set = width and height */ } } Anyway, I digress. Software design is fun... it's a great time to be ali= ve. =E2=80=94 Rob --fa559c3bdb7042f78dd5d5068c4d9a79 Content-Type: text/html; charset=utf-8 Content-Transfer-Encoding: quoted-printable


On Fri, Aug 8, 2025, at 15:11, Hans Krentel wrote:


On Friday 08 August 2025 00:49:27 (+02:00), M= organ wrote:

> On 2025-08-08 10:01, Rob Land= ers wrote:
> > For example, if |A::foo(): int| promises = to always return a non-zero integer, and a subclass overrides |foo()| to= only return zero, that violates the contract=E2=80=94even though the ty= pe signatures match perfectly. This is the sort of semantic guarantee th= at LSP is about, and it is discussed in nearly every reputable textbook = and conference talk on OO design. Languages like PHP can=E2=80=99t enfor= ce these contracts, but the / principle/ is what helps prevent subtle de= sign bugs and behavioural =E2=80=9Csurprises.=E2=80=9D
> &g= t; Indeed. Those contracts only become enforceable in the type signature= when your type system is robust enough to be able to express them. If y= ou could declare |A::foo(): NonzeroInt| then the signature could prevent= an overriding subclass C from returning zero from that function (while = still allowing |C::foo(): PositiveInt|, say); the compiler can verify th= at the type of the expression C::foo() is written to return is of type N= onzeroInt (or PositiveInt).
> In the a= bsence of being able to declare such constraints in the type system, one= has to fall back on other documentation about what behaviour is and isn= 't acceptable.

That is cer= tainly always useful, especially when it was written down, as it allows = to read both, the fine-print, and between the lines. As you have just qu= oted Rob's suggestion while replying to it, allow me the opportunity to = highlight a key sentence for me under the pretext of the earlier quote, = and the much appreciated association with documentation of yours to illu= strate that:

>  [...] the / principle/ = [here, LSP,] is what helps prevent subtle design bugs and behavioural =E2= =80=9Csurprises.=E2=80=9D

Documentation, e.g. o= f pre- and postconditions, loop (in)variants, class invariants, etc, we = can already make use of those corner-stones of the LSP to reason about s= ub-types, the LSP can inspire us of what or how we document. And that's = what I've learned these days from Rob: Without reasoning about the LSP. = I knew already for what I love Liskov for, but the LSP is so much a load= ed thing in discussions, I had to get a couple of things out of the way = first to only understand Robs reasoning. I came here by the error messag= e, and was looking for what I was missing with it.

<= div>"Oh the types in PHP must do that for us, otherwise my scripts are d= oomed."

No.

The compil= er can only handle the type but not the sub-type, because of the halting= problem.

And while my programs can be doomed b= ecause of the halting problem (4. Every program is a part of some other = program and rarely fits.), I as human am still able to reason about the = correctness of my program.

Just my 2 cents

-- hakre

To a= dd to this, here=E2=80=99s one of my favorite examples, ironically, one = often used in beginner OOP tutorials:

class Rec= tangle {
  public int $width;
  public int= $height;
}

class Square extends Rect= angle {
  public int $width { get =3D> $this->width= ; set =3D> $this->width =3D $this->height =3D $value; }
  public int $height { get =3D> $this->height; set =3D>= $this->height =3D $this->width =3D $value; }
}

This is a classic LSP violation. Square changes Rectang= le's contract by linking width/height, removing the independence that Re= ctangle promised. Meaning when we pass it as a Rectangle and we try to m= ake the Square full screen, it will either be too short length (set widt= h first) or too tall (set height first). In other words, a "Square is a = Rectangle" violates LSP, in practice. Yet, this is quite often taught as= a beginner example to OOP.

Are there cases whe= re you'd want to do this deliberately? Yes, probably. Would you be surpr= ised to find a rectangle extended to the edge of the screen did not exte= nd to the edge of the screen? Yes, probably. In fact, it would probably = be filed as a bug.

Interestingly, your product = people will tell you to "stretch" it (or perform some other transform) m= aking a Square behave like a Rectangle again. You'd probably end up with= something more akin to this:

class Rectangle {=
  public int $width;
  public int $height= ;
  public static function asSquare(int $widthAndHeight):= static { /* set width and height */ }
}

Anyway, I digress. Software design is fun... it's a great time t= o be alive.

=E2=80=94 Rob --fa559c3bdb7042f78dd5d5068c4d9a79--