Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:110076 Return-Path: Delivered-To: mailing list internals@lists.php.net Received: (qmail 47928 invoked from network); 8 May 2020 01:17:22 -0000 Received: from unknown (HELO php-smtp4.php.net) (45.112.84.5) by pb1.pair.com with SMTP; 8 May 2020 01:17:22 -0000 Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 24E3B1804C2 for ; Thu, 7 May 2020 16:52:55 -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-ASN: AS15169 209.85.128.0/17 X-Spam-Virus: No X-Envelope-From: Received: from mail-oi1-f179.google.com (mail-oi1-f179.google.com [209.85.167.179]) (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 ; Thu, 7 May 2020 16:52:54 -0700 (PDT) Received: by mail-oi1-f179.google.com with SMTP id r25so6874357oij.4 for ; Thu, 07 May 2020 16:52:54 -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=U+lHKDsaIhpiXnSelYg6fLqwCzhI3hUuWnHH9H+NR8I=; b=oNlrVKPOSulypKWwEvWpFGEr3G3kRbjbV8IsRFfQw/vZETwbFaTJBt6m+H0jdTz4qZ s2h9U7UD3fKn0NgxFy+WJGegyA1JYsEVp01ktav4+YB3YunnkwHjcQTGHaafu++PN5q1 DQgLSLfIcoh2TitJPC3fDaHIkl5xjZZmDZG/SBxLvpQUUth2s1mEvrbk67pC/ogoX274 Ad9LQJqcTTWdPzC31hwvzszvhQtYmngYMcLrPWxKvEz9WiN3MadcJcf0hX1WCVG8FSGK JzTil1dWbgYUZxaj/9BT3p6grj2PcPdkPAuFp+EmKofeNerdq9MAjqDbdONyShkwxcR0 0APA== 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=U+lHKDsaIhpiXnSelYg6fLqwCzhI3hUuWnHH9H+NR8I=; b=OXU90XWg0tLIyE59RERxl8Hy1PRMt2un8GHFSWCRIyIGnnukDq2UEVYtPhU2bguiI1 6aNBwJ7MpyuaQkKeXrdQOIbrIF9pTp3HdAxYMsAUUfKU3AzsnUZNLur+fACT6t1SWzJS 1ZEsg3vBWn26acYYWU7O0A3FRohHK2C07UMaIea/gao5yiXQFJqVwfFc2Odu/9ziEZCP VJLXgvz3x4/rJtZJZ1+H4x0zYBKZ1w1yi96adPNkGRtFnC4KYSDxuQh/qA4DAw3IMouh 8FJpSknustfcm+amEarv7rXWSPr2RjDgwcy+tmrpHieHnh6sVn+g+fyCpiLRsPVwbBWs +wfg== X-Gm-Message-State: AGi0PubrBtRdhlTma73sh8RqkxrSchSs0/u//G0qYQTo9AP7P4tyoV3/ Spx2qLoKp5+YaNj98kubJNRMj+bJ1LjakfgI39sG5HC3UoM= X-Google-Smtp-Source: APiQypJdBbzXr9FxiaBRvrJxdcqkiT+neXkjTKJFRagcFI92XgJjYNCewx4QBq9e8Ht88wBeh+9uT1QoCqtO6EaydRI= X-Received: by 2002:aca:4d5:: with SMTP id 204mr8023117oie.120.1588895573475; Thu, 07 May 2020 16:52:53 -0700 (PDT) MIME-Version: 1.0 References: In-Reply-To: Date: Fri, 8 May 2020 01:52:41 +0200 Message-ID: To: Benjamin Eberlei Cc: PHP Internals Content-Type: multipart/alternative; boundary="0000000000003e081505a5179470" Subject: Re: [PHP-DEV] [RFC] <> Attribute From: nicolas.grekas+php@gmail.com (Nicolas Grekas) --0000000000003e081505a5179470 Content-Type: text/plain; charset="UTF-8" > > https://wiki.php.net/rfc/deprecated_attribute Thanks for the RFC. Why didn't you mention support for deprecating classes/interfaces/traits themselves? If there is a reason, it should be in the RFC at least to me. > > It feels complicated to me, because there are so many entry points to a > class: 0. declaring/loading it 1. extending it, but not always because the > replacement could extend it 2. calling it statically 3. instantiating it 4. > i probably forget a bunch. I think this requires spreading the code to > handle this around into many places and is not easy to do without overhead > for classes that are *not* themselves deprecated. So I decided to keep this > out for now. > It is. But the other cases are equally complex. That's the point I tried to make in my previous answer and that I will continue to make by sharing my experience on the topic. That's why I'm pretty sure that runtime side-effect will severely limit the usefulness of this attribute. I very much wish we would adopt the tag, but without any runtime side effect (which means in practice that php-internals doesn't need to define it, this could be left to userland.) - all other languages with Attributes/Annotations support have it as well >>> >> >> Did you research what runtime side effects the other languages provide? >> > > They mostly warn or fail on compile and don't have runtime effects. The > same goes for any other deprecation that PHP triggers though, they are all > at runtime. So I don't think this can be compared, similar to how the error > stack of PHP cannot be compared to any other language. > OK thanks. > I'm concerned about runtime side-effects. Attributes should lead to none >> of them to me. The moment an attribute leads to runtime side effects, it >> should be turned into code instead, because it looses its "declarative" >> property. >> > > To me Symfony using Annotations at the moment is also all about side > effects based on declared annotations that are then automatically turned > into code. This is the same happening here. > That's somewhat more subtle. Most annotations don't lead to any direct notice. We also double these annotations with calls to trigger_error() to notify end users about them. Both ways are required to fit several kinds of reporting tools (runtime collectors and static analyzers). This works well when calling a method or when instantiating a class. In other situations, annotations are not helpful, because the deprecations map to e.g. a specific value of an argument. That's something that no static analyzer can spot and only inline trigger_error() can report. When extending a class, in debug mode only, we have a special autoloader that uses reflection to check whether the parent class or one of its method are deprecated. It triggers a deprecation notice when it detects this situation. Before triggering the notice, it also checks the namespace of the parent class: if it matches the namespace of the child class, no deprecations are reported. This allows the very common pattern of ("new API" extends "legacy API"), which is required to make the "new API" pass the old type hints that we have to keep in place for BC. This aspect is unaddressed in your proposal (and doesn't need to be actually, pure declarations are fine, others can build or reuse the same logic in userland). We also have another strategy to report deprecations related to inheritance, which is purely local (no need for the autoloader I just described). By using reflection, we sometimes check inline whether the currently called method is being overridden, and we trigger a deprecation if yes (or e.g. if the signature of the child method misses an argument that we want to add in the future). > From my experience, e.g. a method can be declared as deprecated but can >> *still* be called internally without any issue. This should not be reported >> to anyone as its internal. Preserving BC often requires this, and only >> runtime logic can decide when a deprecation should be reported to the >> end-user. Note that this is different from configuring the global error >> reporting level: deciding *locally* that some declaration should not be >> reported is the important part of this. >> > > Can I ask how you are handling this use-case in Symfony at the moment? If > you catch that in the error handler by looking at the backtrace, then this > is exactly possible with this declarative approach as well, in fact the > generated code is just calling trigger_error() and then triggering user > error handlers. > There is no cooperation ever between the error handler and the decision to locally report a deprecation. All deprecation triggering sites in the code decide on their own. Also, the backtrace is never ever used to make such a decision. In order to *not* trigger a deprecation when calling a deprecated method, we call the method with an extra argument that we read via func_get_arg(). There is no other more accurate way. The variety of strategies that we use is quite high because the variety of situations that exists in real apps is also quite high, so we had to find a way for each of them. I invite you to check the source code of Symfony, branch 3.4 or 4.4, to have an example of each of them. I would be happy to guide you (or anyone reading) through this if you want to take the journey, that's quite useful for the discussion if we want to get deeper into it. > That's also why I very much favor @trigger_error() and why static analysis >> works only for the simpler cases. >> > >> If one wants to save the duplicate "declaration" (attribute + explicit >> call inline), then this should be opt-in to me, e.g. <>. >> > > If you only want the static analysis effect, developers are still free to > use /** @deprecated */ instead, which I don't expect either static analysis > tools nor IDEs will drop support for. > That'd be a quite unsatisfactory outcome... > As an additional step, I very much wish that the attribute could accept >> two arguments: the package and the version of it. From my experience, >> making deprecations actionable is critical to help ppl to fix them. And >> making them actionable starts with allowing ppl to quickly identify which >> package cannot be upgraded to the next major yet. >> > >> This is what make us work on symfony/deprecation-contracts >> , which provide this >> single trigger_deprecation(string $package, string $version, string $ >> message, ...$args); function. From the example in the readme: >> >> trigger_deprecation('symfony/blockchain', '8.9', 'Using "%s" is deprecated, use "%s" instead.', 'bitcoin', 'fabcoin'); >> >> Generates: >> Since symfony/blockchain 8.9: Using "bitcoin" is deprecated, use "fabcoin" instead. >> >> Joke aside, this is very useful and I wish the attribute could borrow from the experience we gathered on the topic. >> >> This would be nice: >> <> >> >> The Deprecated attribute accepts a message that gets appended to the > default message, so you could do < 8.9, use fabcoin() instead")>>, which would turn the message into: > > Deprecated: Function bitcoin() is deprecated, since symfony/blockchain > 8.9, use fabcoin() instead in /path/to/file:123 > > A free-form text is more flexible for use-cases where there are no package > and versions, we can't expect to assume every deprecation will be triggered > from a versioned composer package only. > A free-form text is what we're using since years yes. It works, but it makes it hard if not impossible to report structured info about which package block and cannot be updated to their next major. Providing this info in a structured way is useful and that'd what I'm trying to convey here. I think we *can* assume that every deprecation will be able to report some package+version info (it doesn't have to be a composer package, what matters is having the fields easily identifiable) Nicolas > --0000000000003e081505a5179470--