Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:119587 Return-Path: Delivered-To: mailing list internals@lists.php.net Received: (qmail 41096 invoked from network); 21 Feb 2023 13:19:12 -0000 Received: from unknown (HELO php-smtp4.php.net) (45.112.84.5) by pb1.pair.com with SMTP; 21 Feb 2023 13:19:12 -0000 Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 5412C180553 for ; Tue, 21 Feb 2023 05:19:11 -0800 (PST) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-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,FREEMAIL_FROM,HTML_MESSAGE, RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H3,RCVD_IN_MSPIKE_WL,SPF_HELO_NONE, SPF_PASS,T_SCC_BODY_TEXT_LINE autolearn=no autolearn_force=no version=3.4.2 X-Spam-ASN: AS15169 209.85.128.0/17 X-Spam-Virus: No X-Envelope-From: Received: from mail-pg1-f169.google.com (mail-pg1-f169.google.com [209.85.215.169]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange ECDHE (P-256) server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by php-smtp4.php.net (Postfix) with ESMTPS for ; Tue, 21 Feb 2023 05:19:10 -0800 (PST) Received: by mail-pg1-f169.google.com with SMTP id 132so2202439pgh.13 for ; Tue, 21 Feb 2023 05:19:10 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:from:to:cc:subject:date:message-id:reply-to; bh=3WGuui1DSpTygM1IQvDCceVvvtHymfCeoV/hMuVgzW4=; b=pdFNZIb8RxElcMIDzKbGGfXA7hf2X71BeKvlQIRmdaLCjkGLOQUGd2xgG2yKCyTLDl yanjdQNmWzBO08pkxvLiJw91ac9Z/La5VgVOiI3PR48CWRw7L+q/Z1FZLBIihPlcsvIy Ef3OzdJ/g/AAImLhzUMIo8qCXHYgj7VzlzK16kl5Y4X63RvfTVcxnm2tJupgD1kSFHlZ XHpPb4mbiN1EvJoELMDHvEqYRPKc34RRnZrA6L52F0+D+UQYH47M4xtgU+XnDXAzzhl/ RYd6RpQ1o1jo8zC3MA/qI3KmypM9+1l4ddHI18zp9lNFqTDkqAh5rd4ah89EbpnBJ2gm J7hw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=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=3WGuui1DSpTygM1IQvDCceVvvtHymfCeoV/hMuVgzW4=; b=lwFptWUEd2fLbhcGqds1VsfN2oFyBW/GlCt5+yvNg0M38nxIIRMN3vJ7E9/YVAWSXi 7mRoxy3mnYOaC8Yxxmg6WPCsefQwXb99uRI2PG/QfW8BG3BDaR4TFMIVP96EE13R+YPn i7+xeRscPtx6dKtoP2aFp3zmGYpIcvDsEENg2K6/7+3/brN5JrXoEG+kZatAjIi1j3S5 myf7kQiWlbohQLiUBrII1uC0Is69+PCpWVPOzi9Ktvzf/8zhqgW2Bmh7PpalkYoxpnez Fy5+O3PbMsoXWesZFHEhro0RnxbUexBLX7KTfIvRnd0iuDlcvOxXMb7aTEPFb4MDitfA w89w== X-Gm-Message-State: AO0yUKVv42TuaYZdyHJ4OMWeaQ8gaOzBl+4rQaVrAOH3WFPC3PsZPyOn f/Fls8n90WAlNFtbt/rI7wYwlHGlZbZS3eH1dkHqbpAH X-Google-Smtp-Source: AK7set/HyVynm3yDKcwUKEn+PAPckGa4HbDiczltnb2cS4BvIIXXCNhEuwCKIoqNa6SblBjmUQo8fOa3lfPF8IseaXg= X-Received: by 2002:a63:d809:0:b0:4fb:a38d:c09b with SMTP id b9-20020a63d809000000b004fba38dc09bmr618102pgh.10.1676985549625; Tue, 21 Feb 2023 05:19:09 -0800 (PST) MIME-Version: 1.0 References: In-Reply-To: Date: Tue, 21 Feb 2023 15:18:52 +0200 Message-ID: To: someniatko Cc: php internals Content-Type: multipart/alternative; boundary="000000000000faa13005f5359f5b" Subject: Re: [PHP-DEV] Class Re-implementation Mechanism From: drealecs@gmail.com (=?UTF-8?Q?Alexandru_P=C4=83tr=C4=83nescu?=) --000000000000faa13005f5359f5b Content-Type: text/plain; charset="UTF-8" Hi Someniatko, On Tue, Feb 21, 2023 at 1:52 PM someniatko wrote: > Hi again, internals > > My marathon of some crazy ideas continues :D, with less crazy one this > time. > > > ## Idea > > Allow "reimplementing" the non-static public API (that is public > properties and methods, excluding constructor) of a class by other > classes like this: > > ```php > final class interface A { > public string $s; > > public function __construct(string $s) { $this->s = $s; } > > public static function fromInt(int $i): self { return new > self((string) $i); } > > public function foo(): int { return 42; } > } > > final class B implements A { > public string $s = 'hello'; > > public function foo(): int { return 69; } > } > > function usesA(A $param): void {} > > usesA(new B); // this works > ``` > > > ## Explanation > > Consider there is a class like this: > > ```php > final class Service { > public function __construct(private SomeDependency $dependency) {} > // ... > } > > final class SomeDependency { > // ... > } > ``` > > We want to write some tests for the Service class, but we don't want > to use a real SomeDependency instance > during tests. A common approach is to either extract an interface > (JUST to make it testable), or to drop the > `final` keyword and allow extending the class. > > Both approaches have their flaws: > - extracting an interface unnecessarily complicates the system, where > only one "real" implementation of an interface is assumed. > - dropping the `final` keyword allows for the rabbit-hole of > inheritance abuse, like greatly described in this article: > https://front-line-php.com/object-oriented > > I believe I came up with a better idea: what if we could leave both > benefits of prohibiting the inheritance abuse and also allow not to > clutter our namespace with excess entities like interfaces? I hereby > suggest to combine the responsibilities of a class and an interface > into one thing like that: > > ```php > final class interface C {} > final class D implements C {} > ``` > > Now other classes can "implement" this class as well. Introduction of > the new syntax (`class interface`) also solves BC problem - if you > want to forbid your classes to be reimplemented whatsoever, you can > still stick to the `final class` syntax. Although it is also possible > to allow "reimplementing" ANY class, then new syntax is not needed - > but some library writers like Marco Pivetta could be sad about that I > suppose. > > Soo..., what do you think? Could this be a valuable addition to the > language? > This sounds interesting but it breaks some expectations. Interesting because you can have any class act as an interface for other classes with the interface being built up of any public properties or method that exists on that class. Ok, maybe not any class but just a final class. And also maybe just a final class that doesn't yet implement an interface. The expectation it breaks is that if you have a final class, whenever you use it, you expect some specific implementation exists on it. And this would break the expectation and in real life someone might pass a totally different implementation. Will not break generic LSP but still will break some expectations. I believe other options should be followed that are more straightforward: If it's your internal code not shared with others, you can easily extract an interface at any point where you need to inject another instance (when testing). Also, if it's internal code, you can just not mark the class as final. If it's a library code you use or share with others, a class should not be marked as final if there is no interface they implement and that should be fixed by extracting an interface. If it's a value object, you don't need to mock it, wherever it exists. Regards, Alex --000000000000faa13005f5359f5b--