Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:114052 Return-Path: Delivered-To: mailing list internals@lists.php.net Received: (qmail 2966 invoked from network); 14 Apr 2021 13:29:38 -0000 Received: from unknown (HELO php-smtp4.php.net) (45.112.84.5) by pb1.pair.com with SMTP; 14 Apr 2021 13:29:38 -0000 Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 603E71804DD for ; Wed, 14 Apr 2021 06:30:40 -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=-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_H2,SPF_HELO_NONE,SPF_PASS autolearn=no autolearn_force=no version=3.4.2 X-Spam-Virus: No X-Envelope-From: Received: from mail-lf1-f49.google.com (mail-lf1-f49.google.com [209.85.167.49]) (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 ; Wed, 14 Apr 2021 06:30:40 -0700 (PDT) Received: by mail-lf1-f49.google.com with SMTP id f17so26162687lfu.7 for ; Wed, 14 Apr 2021 06:30:40 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=mime-version:references:in-reply-to:from:date:message-id:subject:to :cc; bh=4gBAuNWMONpg6CI7oYc0+IIuv04eHPTV2eDwvMgmCUY=; b=Rq5HfE6/nsVzUKi5E3XROqGsY7Co81mDlFXyf7TUu5iBdfPXm9X3tdk8CnFNFlmBy6 h/j5Xe7SAgkzO/XnCEI4dOXb7FeWD+eiuhOE0/Iv7pjY8uK12VoKUKTHXWuQmLCaa4Fk b1ZsLQLaIFq/cNF5ffWNeohfKmsfP+TFJljad4Pjwy+ANIHTZTwhotWn6lMWydmiK5j6 YzHMZRwAz+BC7pm/F9jSUu8WSEmtfc4swJN+64UBsU0Bh4f3wz60STbfb4UytP/4u/+n eVERpi6McICx8NjcutyDJVaYdUGcFPDlGyJIHZvGeSz+jsaQaX6EFjgS4uy/ExMKNr3K 3QSA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:references:in-reply-to:from:date :message-id:subject:to:cc; bh=4gBAuNWMONpg6CI7oYc0+IIuv04eHPTV2eDwvMgmCUY=; b=KbnPFZgILfl6QsdFLlgpNYONsjwdI6wd9zat5bnVr+jCh+Q4CYfW6ZWBBGyszcs2qV uhLdT9xHWE9uZRi2cip+Gz/wtICsggHx81i0sdITX+H3wrgB6oMlu2O3WdKEUycFFHKr AnDUgeyauf+tpo5r4hVk6lcP2PapgYe2a7rJNfGyloP1t9qW3213QpPpYh7f5h3xlJoI 6UAKIduGaVujZj7vCvALdmq50ialkZobOSmEzNxirM6c7A8/zXWWIkidudSL5tUBHs0A +Gs0Q1KuBgIk9CJrNL6RRwB7I48fe6HwmfV9Pd5UFBTCcw+18fV+GQRkAij3AHimOzZc C/3w== X-Gm-Message-State: AOAM533TVRJUD0WS/0YOrnjwfPmJu6tm/5MQsGxdVkuKBhpKRK4Wjar/ 9tPbH84F0KCFw3APnegwfKXdyXQ+u2mnNk+Xxfc= X-Google-Smtp-Source: ABdhPJzxCsXkivhPL63tSpLf3Y0FQs+2EWtuCv0KSCRxL+1epyo53HwSIP6ZKSCqDzGBUEzo84wn+u3DVN32PNmlYdU= X-Received: by 2002:a19:f60e:: with SMTP id x14mr27003765lfe.159.1618407037229; Wed, 14 Apr 2021 06:30:37 -0700 (PDT) MIME-Version: 1.0 References: In-Reply-To: Date: Wed, 14 Apr 2021 15:30:21 +0200 Message-ID: To: =?UTF-8?B?TcOhdMOpIEtvY3Npcw==?= Cc: PHP Internals List Content-Type: multipart/alternative; boundary="0000000000008e5ebd05bfeec01a" Subject: Re: [PHP-DEV] [RFC] [Discussion] Adding return types to internal methods From: nikita.ppv@gmail.com (Nikita Popov) --0000000000008e5ebd05bfeec01a Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable On Sat, Mar 6, 2021 at 11:56 PM M=C3=A1t=C3=A9 Kocsis wrote: > Hi Internals, > > I've just finished the first iteration of the RFC ( > https://wiki.php.net/rfc/internal_method_return_types) as well as the > implementation based on the feedback Nikita and Nicolas provided. Thus, I= 'd > like to start the discussion phase. > > Regards: > M=C3=A1t=C3=A9 > Thanks M=C3=A1t=C3=A9 for the RFC, and thanks Nicolas for your comments. I think it's worth noting that this RFC tries to address a specific case of a more general problem: PHP's strict LSP checks make it hard to change method signatures, in ways that may be perceived as harmless. I think the two important ones are: 1. The one addressed by the RFC is addition of a return type (or more generally, restriction of a return type). This is generally done when the method was already guaranteed to return that type, and this fact was not explicitly declared previously. Direct users of the API are not affected by the change. Overriding methods can be affected in two ways: They made use of the lose API contract and returned a different type, in which case the implementation needs to be changed. Or, and this is almost always the case, they already adhere to the stricter API contract, but now need to explicitly declare this fact. 2. The second one not addressed here is the addition of optional parameters. Take for example the recent change of mysqli::execute() to mysqli::execute(?array $params =3D null). Once again, direct users of the A= PI are not affected by the change. Overriding methods are affected because they need to pass through the new parameter. As this is an addition of a new optional feature, not passing through the optional parameter only becomes a problem if the new optional feature is actually used. Whether not passing it is "harmless" depends on whether the API declarer and the API user are the same. The commonality is that both of these changes have no impact on direct API consumers, only on inheritors (to give a counter example: Adding a new required parameter breaks direct consumers). In both cases it is technically easy to adjust the overriding APIs to comply with the new signature -- the problem is purely one of backwards compatibility guarantees provided to downstream consumers. Having said all that, I don't really see a solution for the general problem, short of allowing methods to opt-out of LSP checks entirely. I think doing so would be a very bad idea, because the functionality would certainly get misused for other purposes. As such, let's get back to the return types... I think as far as the original motivation is concerned (adding return types to internal methods), the RFC as proposed does well. The only thing I would change is to add the ReflectionMethod::getTentativeReturnType() part independently of the userland aspect. I believe it's important that this is exposed in reflection, even if the more general userland functionality is not provided. For example, if you have a mock generator, you'll probably want to either add the tentative return type as a real return type to mocks, or at least automatically add the #[SuppressReturnTypeNotice] attribute. I'm more ambivalent about the userland side of this proposal. As Nicolas has already pointed out, the RFC as proposed requires the library to have a PHP 8.1 minimum version requirement to make use of this functionality. And libraries requiring PHP 8.1 are likely not the target audience for this feature -- at least not when it comes to adding return types at all, there might still be a use when it comes to changing them in the future, as typesystem capabilities are expanded. I don't think it makes sense to introduce any solution in this space that requires PHP 8.1. If we introduce something for the userland side of things, then it should be compatible with older versions. Nicolas' suggestion of declaring the new return type inside the attribute #[TentativeReturnType("Foo|Bar")] would certainly work, and allow us to provide the same degree of functionality as the internal variant. That is, we could perform a variance check against the new tentative signature and throw a deprecation notice on mismatch. However, just like M=C3=A1t=C3=A9, I don't really look forward to parsing i= nformation out of type strings. It's manageable now, but keeping in mind future extensions of the typesystem, dealing with #[TentativeReturnType("callable(Foo &...)")] will be significantly less pleasant. That leaves Nicolas' last suggestion of not even trying to specify the type in a way that is understood by the engine -- just indicate that the type is going to change, and leave the actual type specification to @return. In this case the attribute would not have any actual effect from the engine perspective (apart from the interaction with the internal case). Which begs the question, why does this need to be part of PHP core at all? Is it just a matter of standardizing on a particular attribute? If we go down that route, I would suggest not to call this #[TentativeReturnType], but something like #[ReturnTypeWillChange]. I also don't think it's necessary to restrict this to methods that don't currently declare a return type, as it may be necessary to narrow a declared return type at a later time (e.g. due to typesystem improvements). For example: class A { /** @return int[] */ #[ReturnTypeWillChange] public function method(): array {} } class B extends A { // Accepted, static analyzers happy. public function method(): int[] {} } class C extends A { // Accepted, but static analyzers will warn. public function method(): array {} } class D extends A { // Not accepted, as it violates the proper array type. public function method() {} } So tl;dr I would either go with only the internal part of the proposal, or follow Nicolas' last suggestion, or some variant thereon. The version that's currently RFC doesn't seem particularly useful due to the version requirement. Regards, Nikita --0000000000008e5ebd05bfeec01a--