rfc:final_anonymous_classes

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
rfc:final_anonymous_classes [2023/11/15 19:45] – Clarify tie-breaker proposal danogrfc:final_anonymous_classes [2023/12/23 15:04] (current) danog
Line 2: Line 2:
   * Date: 2023-11-15   * Date: 2023-11-15
   * Author: Daniil Gentili <daniil@daniil.it>   * Author: Daniil Gentili <daniil@daniil.it>
-  * Status: Under Discussion+  * Status: Declined
  
 ===== Introduction ===== ===== Introduction =====
  
-This RFC proposes to add support for final anonymous classes, //or// make all anonymous classes final by default, with or without the possibility to make them non-final+This RFC proposes to add support for final anonymous classes. 
  
 ===== Proposal ===== ===== Proposal =====
  
-This RFC proposes to either add support for final anonymous classes, //or// make all anonymous classes final by default, with or without the possibility to make them non-final+This RFC proposes to add support for final anonymous classes. 
  
 This should also allow some additional opcache optimizations, such as any JIT logic gated behind a check on ZEND_ACC_FINAL, i.e. https://github.com/php/php-src/blob/master/ext/opcache/jit/zend_jit_trace.c#L4507. This should also allow some additional opcache optimizations, such as any JIT logic gated behind a check on ZEND_ACC_FINAL, i.e. https://github.com/php/php-src/blob/master/ext/opcache/jit/zend_jit_trace.c#L4507.
  
-Personally, I would have instead preferred the much cleaner approach of making all anonymous classes final by default, (preferrably) without offering the option to make them non-final.+Example syntax:
  
-However, I understand that this might be a little bit too restrictive for something that may have some valid usecases, even if extending anonymous classes currently requires some hack-ish workarounds with class_alias.+<code php> 
 +$x = new final class {}; 
 +</code>
  
-Thus, this RFC includes three mutually exclusive proposals:+Extending a final anonymous class throws an error:
  
-  - Add support for final anonymous classes (''new final class {}'' syntax, no breaking changes+<code php> 
-  //OR// Make all anonymous classes final by default, without the option to make them final (breaking change) +$x = new final class {}
-  - //OR// Make all anonymous classes final by default, provide an optional ''open'' keyword to make them non-final (like in Kotlin, ''new open class {}'', breaking changes).+class_alias($x::class, 'alias'); 
 +class aliasExtends extends alias {} 
 +</code> 
 + 
 +'' 
 +Fatal error: Class aliasExtends cannot extend final class class@anonymous in %s on line %d 
 +''
  
-As an extra proposal related to the last two options, possibly requiring a separate RFC, in the last two cases, it might be a good idea to also disallow the use of ''class_alias'' altogether for final anonymous classes (suggested by nikolas-grekas in https://github.com/php/php-src/pull/11126#issuecomment-1522042841). 
  
 ===== Backward Incompatible Changes ===== ===== Backward Incompatible Changes =====
  
-In case proposal 2 or 3 are accepted, it will break existing code which extends from anonymous classes using ''class_alias''+None.
- +
-In case proposal 2 or 3 are accepted //and// ''class_alias'' is disallowed for final anonymous classes, it will break existing code that uses ''class_alias'' to give a name to anonymous classes, not necessarily to extend them.+
  
 ===== Proposed PHP Version(s) ===== ===== Proposed PHP Version(s) =====
Line 39: Line 44:
  
 See Backward Incompatible Changes. See Backward Incompatible Changes.
- 
-===== Future Scope ===== 
- 
-As an extra proposal related to the last two options, possibly requiring a separate RFC, in the last two cases, it might be a good idea to also disallow the use of ''class_alias'' altogether for final anonymous classes (suggested by nikolas-grekas in https://github.com/php/php-src/pull/11126#issuecomment-1522042841). 
  
 ===== Proposed Voting Choices ===== ===== Proposed Voting Choices =====
  
-All three options are mutually exclusive, but since some people (myself included) may be OK with more than one of the following options and may vote in favor of more than one option, I would propose a tie-breaker second vote in case more than one of the options finish with the same number of favorable votes.   +2/3 required to accept. Voting started on 2023-12-03 and will end on 2023-12-18 00:00 GMT
-In this second, eventual tie-breaker vote, voting favorably for only one of the options would be allowed.+
  
-2/3 required to accept. +<doodle title="Add support for final anonymous classes?" auth="danog" voteType="single" closed="true" closeon="2023-12-18T00:00:00Z">
- +
-<doodle title="Add support for final anonymous classes?" auth="danog" voteType="single" closed="true">+
    * Yes    * Yes
    * No    * No
 </doodle> </doodle>
  
 +===== Patches and Tests =====
  
-2/3 required to accept.+Final anonymous classes implementation: https://github.com/php/php-src/pull/11126
  
-<doodle title="Make all anonymous classes final by default, without the option to make them non-final?" auth="danog" voteType="single" closed="true"> +===== References =====
-   * Yes +
-   * No +
-</doodle>+
  
 +  * Pull request discussion: https://github.com/php/php-src/pull/11126
 +  * Internals discussion: https://externals.io/message/121356, https://externals.io/message/121685
  
-2/3 required to accept. 
  
-<doodle title="Make all anonymous classes final by default, with the option to make them non-final with an open keyword?" auth="danog" voteType="single" closed="true"> +===== Rejected Features =====
-   * Yes +
-   * No +
-</doodle>+
  
 +After feedback received from Nikolas Grekas in the last [RFC] discussion thread (https://externals.io/message/121685), I moved here a large chunk of the rationale and removed basically all the alternative polls I had initially planned to propose:
  
-===== Patches and Tests =====+<blockquote> 
 +Hi Daniil,
  
-Final anonymous classes implementation: https://github.com/php/php-src/pull/11126+    >> While I'm open to Proposal 1, which introduces final anonymous classes 
 +    >> without breaking BC, Proposals 2 and 3 are a different story. 
 +    >> In summary, I advocate for the RFC to focus on the non-BC-breaking option. 
 +    >> Let's maintain our commitment to stability and gradual evolution in PHP. 
 +    >> Cheers, 
 +    >> Nicolas
  
-===== References =====+    > Agree with your points, just adding final anonymous classes seems the best solution to me, but given the interest in alternative solutions both in the pull request discussion, and in the previous mailing list thread, I think I'll leave the other options in the RFC, to see how the votes will go (I'm actually curious myself :). 
 +    > Regards, 
 +    > Daniil Gentili
  
-  * Pull request discussion: https://github.com/php/php-src/pull/11126 +I think this is a dangerous gameBreaking BC shouldn't be proposed unless absolutely needed IMHO.
-  * Internals discussion: https://externals.io/message/121356+
  
 + Nicolas
 +</blockquote>
 +
 +To be entirely honest, I'm a bit on the fence: on one hand, looking at code like ''new class {}'', you would assume that since the class apparently has no name, it should not be impossible to extend it, but on the other hand, there are valid usecases for extending even anonymous classes (comments in the PR (https://github.com/php/php-src/pull/11126) referenced proxying, I can think of phpunit mocking to a much, much lesser extent given that you should realistically (hopefully) never have to mock an anonymous class that does not already implement an interface), and completely precluding the possibility of extending a class that
 +
 +  - Has a name (even if it's not immediately obvious)
 +  - Can be referenced to using its name (''class_exists'', ''new ReflectionClass'', ''new $clazz'', and yes, even ''class_alias'')
 +
 +seems a tad bit too restrictive...
 +
 +On the other hand, I also really don't like non-final classes, in all of my projects, I use CS rules that force all classes to either be abstract or final, because unfortunately, I've had to work with a lot of code that very frequently violates encapsulation by using inheritance.
 +
 +Still, there are some useful patterns, mainly regarding testing and mocking, for example I use https://github.com/dg/bypass-finals as a dev dependency to make all final classes non-final at runtime to allow mocking in phpunit, but it works by installing a custom default stream contexts that intercepts requires, tokenizes files and removes final keywords from classes; this approach would break for anonymous classes if they were rendered final by default without an option to make them non-final. 
 +
 +This is why I remain ambivalent about the options, as seen both in my emails and in the original text of the RFC:
 +
 +Personally, I would have instead preferred the much cleaner approach of making all anonymous classes final by default, (preferrably) without offering the option to make them non-final.
 +
 +However, I understand that this might be a little bit too restrictive for something that may have some valid usecases, even if extending anonymous classes currently requires some hack-ish workarounds with class_alias.
 +
 +Thus, this RFC initially included three mutually exclusive proposals:
 +
 +  - Add support for final anonymous classes (''new final class {}'' syntax, no breaking changes)
 +  - //OR// Make all anonymous classes final by default, without the option to make them final (breaking change)
 +  - //OR// Make all anonymous classes final by default, provide an optional ''open'' keyword to make them non-final (like in Kotlin, ''new open class {}'', breaking changes).
 +
 +As an extra proposal related to the last two options, possibly requiring a separate RFC, in the last two cases, it might be a good idea to also disallow the use of ''class_alias'' altogether for final anonymous classes (suggested by nikolas-grekas in https://github.com/php/php-src/pull/11126#issuecomment-1522042841).
rfc/final_anonymous_classes.1700077521.txt.gz · Last modified: 2023/11/15 19:45 by danog