This is an old revision of the document!
PHP RFC: Final by default anonymous classes
- Date: 2023-12-23
- Author: Daniil Gentili daniil@daniil.it
- Status: Voting
Introduction
This RFC proposes to make anonymous classes final by default.
Proposal
This RFC proposes to make anonymous classes final by default.
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.
Example, extending an anonymous class throws an error:
$x = new class {}; class_alias($x::class, 'alias'); class aliasExtends extends alias {}
Fatal error: Class aliasExtends cannot extend final class class@anonymous in %s on line %d
Optionally, an open
keyword can be introduced to optionally make anonymous classes non-final.
Example, extending an open anonymous class does not throw an error:
$x = new open class {}; class_alias($x::class, 'alias'); class aliasExtends extends alias {}
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, evenclass_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.
Thus, I also propose the additional of an optional open
keyword that can be used to make anonymous classes non-final.
Backward Incompatible Changes
Anonymous classes will be rendered final by default.
Proposed PHP Version(s)
Next PHP 8.4
RFC Impact
See Backward Incompatible Changes.
Proposed Voting Choices
2/3 required to accept.
2/3 required to accept.
References
- Previous optionally final anonymous classes RFC: https://wiki.php.net/rfc/final_anonymous_classes