Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:114149 Return-Path: Delivered-To: mailing list internals@lists.php.net Received: (qmail 14806 invoked from network); 25 Apr 2021 15:32:41 -0000 Received: from unknown (HELO php-smtp4.php.net) (45.112.84.5) by pb1.pair.com with SMTP; 25 Apr 2021 15:32:41 -0000 Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id EECAE1804DC for ; Sun, 25 Apr 2021 08:36:29 -0700 (PDT) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on php-smtp4.php.net X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,HTML_MESSAGE,RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H2, SPF_HELO_NONE,SPF_NONE autolearn=no autolearn_force=no version=3.4.2 X-Spam-Virus: No X-Envelope-From: Received: from mail-qv1-f51.google.com (mail-qv1-f51.google.com [209.85.219.51]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange ECDHE (P-256) server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by php-smtp4.php.net (Postfix) with ESMTPS for ; Sun, 25 Apr 2021 08:36:29 -0700 (PDT) Received: by mail-qv1-f51.google.com with SMTP id bs7so25608508qvb.12 for ; Sun, 25 Apr 2021 08:36:29 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=newclarity-net.20150623.gappssmtp.com; s=20150623; h=from:message-id:mime-version:subject:date:in-reply-to:cc:to :references; bh=0vInM4qAUPOmsp1wqioo+rNT2jq1t3Kr/AxZ5SqfEf0=; b=Bc5UAKtgGXodLxguUX/mnpj/3owQLy8S54PSN+1T8W7vhGcndz+OSrob4+aUCo+8i/ t9+1qLqNwTllAWGCBEi733YJiT7+FOTG02mT4YpJEl1i0qMynPV/t7SROchQyVfBO0kW Fsmk+XPtTWMCuoYVH9yrXzhPzLOJtV195u+jiI3XOqw2ALTL3v0Q4Uecd6VQ3BO5xodv nGpDPD+2JW+pDaXtEPUrg2NMUeWLfGAvrQ5ofqDeFHZLNLdFAe/R3CwMxk4whD/lA4ZL rVGpPw8uZvP2LW1EVdonmrb3LL4BVF8my+yrUG6id8GFm7gEncvhd/+6O+Io+yPKF/+i YdxA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:message-id:mime-version:subject:date :in-reply-to:cc:to:references; bh=0vInM4qAUPOmsp1wqioo+rNT2jq1t3Kr/AxZ5SqfEf0=; b=BQzLvD8DzRk9TBJ+tZbSbL0qd4vNt074unUS0lthpgRfqf4wHjzBbVLtSUytKJtnBu MnFfcXG8kF/QBF6vEPUYUayXc6pRnQ+PZx7pUsZhYi4LNhRvaLYurJ4hT/24t1klHgZz z8bwQutLAFAbT+9e28HXl2to37dpayr+WZ0e4asiLW8DFO5zNZa4lUX7NALfjcO/HWAu rS/937VHvGUlPNHhZgb4xjlfMthwKCe+y5AQqoDuF7VKMNrOJ3yZlt5F1lf3di+oRjMo hjrBrmX7oCJRTtODKW8zWTCyONkxUsyjL8QUP/cNKIE8DQh0Ns6p/w5GJb2bH6b90cVq ngvw== X-Gm-Message-State: AOAM531HBYxiHT0Fc+CugC247SYOnxmdyYCzIcM2yM3XTxOYoC56Y4Fj LhyxHjwn8vF96dunmvYIiOYnuQ== X-Google-Smtp-Source: ABdhPJznvvTQlXvtj0+O7EVjURvZR+oAciLKEh7EIIMOK46naRtqjaoDOxBlx87hINIPvPIhfYzLnw== X-Received: by 2002:a0c:a956:: with SMTP id z22mr13566100qva.22.1619364986173; Sun, 25 Apr 2021 08:36:26 -0700 (PDT) Received: from [192.168.1.10] (c-24-98-254-8.hsd1.ga.comcast.net. [24.98.254.8]) by smtp.gmail.com with ESMTPSA id c23sm8640026qtm.46.2021.04.25.08.36.24 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Sun, 25 Apr 2021 08:36:24 -0700 (PDT) Message-ID: <0BF84585-DDC3-4B25-BFD2-D8B916D135EE@newclarity.net> Content-Type: multipart/alternative; boundary="Apple-Mail=_FEC780C2-16CC-4D70-BA36-64CB35B93988" Mime-Version: 1.0 (Mac OS X Mail 13.4 \(3608.120.23.2.4\)) Date: Sun, 25 Apr 2021 11:36:24 -0400 In-Reply-To: Cc: PHP Internals To: David Gebler References: <5b9f1500-615a-48f1-815f-1d48b327ef90@processus.org> <179049b1475.11134368b213512.254739612773841999@void.tn> X-Mailer: Apple Mail (2.3608.120.23.2.4) Subject: Re: [PHP-DEV] [RFC][Draft] Sealed Classes From: mike@newclarity.net (Mike Schinkel) --Apple-Mail=_FEC780C2-16CC-4D70-BA36-64CB35B93988 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=utf-8 > On Apr 24, 2021, at 7:39 PM, David Gebler = wrote: >=20 > I don't love this idea, I'm not very fond of the final keyword, = either; I'll start by saying the final keyword caused me a tremendous amount of = heartache because it was used on a class in a framework that I badly, = badly needed to extend. But even so, I recognize why they used it, and I still don't have a = great argument for how they could address the reasons they used it some = other way. > I've always believed annotations (or attributes in PHP these days) are = a > better of way of indicating you, as an author of a class, did not = write it > with inheritability in mind or intended than restricting language = features > through syntactic constructs. >=20 > The RFC says "when you have a class in your code base that shares some > implementation detail between 2 or more other objects, your only = protection > against others making use of this class is to add `@internal` = annotation, > which doesn't offer any runtime guarantee that no one is extending = this > object", to which I ask - why do you need this guarantee? What does it > qualitatively add? If I make a judgement that I want to extend your = class > or implement your interface, I can just delete the sealed keyword from = your > code and carry on. So it doesn't actually offer any guarantee at all = that > I'm not extending the type. Actually, it does offer such a guarantee. It guarantees if you are = using a non-forked version of the original developer's (OD's) library or = framework then that class won't be extended. When someone pulls the = original non-forked version from its source repository =E2=80=94 such as = when using Composer =E2=80=94 then that code will be (effectively) = guaranteed not to be extended. OTOH, if you do delete the sealed (or final) keyword you have then = forked the code, in a defacto manner if not a literal one. If you use a = forked version of the code, you now own the maintenance of that code and = any bugs that are generated by your forked changes in using code. The = original developer has no moral, ethical or even contractual obligation = to care about the breakage you cause. Hypothetical example: You fork the code, remove sealed/final, then = subclass the code and add a method, let's call it ToString(). And you = write your application to use ToString(). Now the OD releases a new = minor version and they also add a ToString() method. Applications using = your fork probably cannot use the new version of the OD's library = because when the library calls ToString() your version is called. So you = have to update your application to use the new version of the library = and once again remove sealed/final. AND, if your code is instead another add-on library, now users of your = add-on library will also have to fix their code too. Which could = potentially be a large number of users if your add-on is successful. So not using final or sealed can result in some really hairy and = possibly impossible to fully resolve backward compatibility concerns for = developers who publish libraries and/or frameworks. > The best it can achieve is to indicate your > intentions, which I believe can be adequately done today through an > attribute, no addition to the language needed. Still, I concur with your concerns. Developers too often implement = final classes in libraries and frameworks without fully addressing all = the use-cases and/or adding enough extensibility points because it makes = their lives easier. Because of that final =E2=80=94 and sealed, if = added =E2=80=94 can make the life of an application developer a living = hell. So what's the answer? I don't know that I have the ultimate answer, but = I would be a lot more comfortable with adding features to PHP such as = ones like sealed that restrict the "O" in S.O.L.I.D.[0] if PHP were to = offer the following three (3) things, all of which can be found in Go, = and I am sure other languages: 1. Class embedding[1] =E2=80=94 Allows one class to embed another and = immediately have access to all its properties and methods, and also to = be able to extract an instance of that embedded class. It is called = "Type embedding" in Go. 2.Type definitions[2] =E2=80=94 A typedef would allow developers to = define constrained versions of existing types, such as `FiveStarRating` = which could only contain 1, 2, 3, 4 or 5, or types that identify a = signature, for example as `ConvertToString` which could require a = closure that implements `func(mixed):string`. In Go you can compose = other types to create new types, but I'm not sure if those other type of = types could apply to PHP, at least as it currently exists, and = especially because it is not a compiled language. 3. Structural typing[3] =E2=80=94 Basically interfaces that can be = implemented implicitly rather than explicitly. For example, if I wanted = to implement a Stringable interface that requires a ToString():string = method then structural typing would allow me to implement that interface = simply by adding a ToString() method instead of requiring me to also add = "implements Stringable" to the class definition.=20 Those three features are all killer language features and would make = great additions to PHP. IMO, of course. #fwiw -Mike [0] https://stackify.com/solid-design-open-closed-principle/ [1] https://travix.io/type-embedding-in-go-ba40dd4264df [2] https://go101.org/article/type-system-overview.html [3] = https://blog.carbonfive.com/structural-typing-compile-time-duck-typing/ = = --Apple-Mail=_FEC780C2-16CC-4D70-BA36-64CB35B93988--