rfc:anonymous_classes
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revisionLast revisionBoth sides next revision | ||
rfc:anonymous_classes [2015/03/13 19:11] – Added serialization note philstu | rfc:anonymous_classes [2015/04/27 14:33] – implemented nikic | ||
---|---|---|---|
Line 1: | Line 1: | ||
====== PHP RFC: Anonymous Classes ====== | ====== PHP RFC: Anonymous Classes ====== | ||
- | * Version: 0.5.2 | + | * Version: 0.6 |
* Date: 2013-09-22 | * Date: 2013-09-22 | ||
* Author: Joe Watkins < | * Author: Joe Watkins < | ||
- | * Status: | + | * Status: |
* First Published at: http:// | * First Published at: http:// | ||
Line 27: | Line 27: | ||
}); | }); | ||
</ | </ | ||
+ | |||
+ | ===== Syntax and Examples ===== | ||
+ | |||
+ | new class (arguments) {definition} | ||
+ | |||
+ | Note: in a previous version of this RFC, the arguments were after the definition, this has been changed to reflect the feedback during the last discussion. | ||
+ | |||
+ | <code php> | ||
+ | <?php | ||
+ | /* implementing an anonymous console object from your framework maybe */ | ||
+ | (new class extends ConsoleProgram { | ||
+ | public function main() { | ||
+ | /* ... */ | ||
+ | } | ||
+ | })-> | ||
+ | |||
+ | /* return an anonymous implementation of a Page for your MVC framework */ | ||
+ | return new class($controller) implements Page { | ||
+ | public function __construct($controller) { | ||
+ | /* ... */ | ||
+ | } | ||
+ | /* ... */ | ||
+ | }; | ||
+ | |||
+ | /* vs */ | ||
+ | class MyPage implements Page { | ||
+ | public function __construct($controller) { | ||
+ | /* ... */ | ||
+ | } | ||
+ | /* ... */ | ||
+ | } | ||
+ | return new MyPage($controller); | ||
+ | |||
+ | /* return an anonymous extension of the DirectoryIterator class */ | ||
+ | return new class($path) extends DirectoryIterator { | ||
+ | /* ... */ | ||
+ | }; | ||
+ | |||
+ | /* vs */ | ||
+ | class MyDirectoryIterator { | ||
+ | /* .. */ | ||
+ | } | ||
+ | return new MyDirectoryIterator($path); | ||
+ | |||
+ | /* return an anon class from within another class (introduces the first kind of nested class in PHP) */ | ||
+ | class MyObject extends MyStuff { | ||
+ | public function getInterface() { | ||
+ | return new class implements MyInterface { | ||
+ | /* ... */ | ||
+ | }; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | |||
+ | /* return a private object implementing an interface */ | ||
+ | class MyObject extends MyStuff { | ||
+ | /* suitable ctor */ | ||
+ | | ||
+ | private function getInterface() { | ||
+ | return new class(/* suitable ctor args */) extends MyObject implements MyInterface { | ||
+ | /* ... */ | ||
+ | }; | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Note: the ability to declare and use a constructor in an anonymous class is necessary where control over construction must be exercised. | ||
+ | |||
+ | ===== Inheritance/ | ||
+ | |||
+ | Extending classes works just as you'd expect. | ||
+ | |||
+ | <code php> | ||
+ | <?php | ||
+ | |||
+ | class Foo {} | ||
+ | |||
+ | $child = new class extends Foo {}; | ||
+ | |||
+ | var_dump($child instanceof Foo); // true | ||
+ | </ | ||
+ | |||
+ | Traits work identically as in named class definitions too. | ||
+ | |||
+ | <code php> | ||
+ | <?php | ||
+ | |||
+ | trait Foo { | ||
+ | public function someMethod() { | ||
+ | return " | ||
+ | } | ||
+ | } | ||
+ | |||
+ | $anonClass = new class { | ||
+ | use Foo; | ||
+ | }; | ||
+ | |||
+ | var_dump($anonClass-> | ||
+ | </ | ||
+ | |||
+ | ===== Reflection ===== | ||
+ | |||
+ | The only change to reflection is to add ReflectionClass:: | ||
+ | |||
+ | ===== Serialization ===== | ||
+ | |||
+ | Serialization is not supported, and will error just as anonymous functions do. | ||
+ | |||
+ | ===== Internal Class Naming ===== | ||
+ | |||
+ | The internal name of an anonymous class is generated with a unique reference based on its address. | ||
+ | |||
+ | <code php> | ||
+ | function my_factory_function(){ | ||
+ | return new class{}; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | get_class(my_factory_function()) would return " | ||
+ | |||
+ | <code php> | ||
+ | class mine {} | ||
+ | |||
+ | new class extends mine {}; | ||
+ | </ | ||
+ | |||
+ | This class name will be " | ||
+ | |||
+ | Multiple anonymous classes created in the same position (say, a loop) can be compared with `==`, but those created elsewhere will not match as they will have a different name. | ||
+ | |||
+ | <code php> | ||
+ | $identicalAnonClasses = []; | ||
+ | |||
+ | for ($i = 0; $i < 2; $i++) { | ||
+ | $identicalAnonClasses[$i] = new class(99) { | ||
+ | public $i; | ||
+ | public function __construct($i) { | ||
+ | $this->i = $i; | ||
+ | } | ||
+ | }; | ||
+ | } | ||
+ | |||
+ | var_dump($identicalAnonClasses[0] == $identicalAnonClasses[1]); | ||
+ | |||
+ | $identicalAnonClasses[2] = new class(99) { | ||
+ | public $i; | ||
+ | public function __construct($i) { | ||
+ | $this->i = $i; | ||
+ | } | ||
+ | }; | ||
+ | |||
+ | var_dump($identicalAnonClasses[0] == $identicalAnonClasses[2]); | ||
+ | </ | ||
+ | |||
+ | Both classes where identical in every way, other than their generated name. | ||
===== Use Cases ===== | ===== Use Cases ===== | ||
Line 65: | Line 220: | ||
$subject-> | $subject-> | ||
function update(SplSubject $s) { | function update(SplSubject $s) { | ||
- | printf(" | + | printf(" |
} | } | ||
}); | }); | ||
Line 140: | Line 295: | ||
How you choose to do it for any specific application, | How you choose to do it for any specific application, | ||
- | Various use cases have been suggested on the mailing list: http:// | + | Various use cases have been suggested on the mailing list: http:// |
- | + | ||
- | ===== Use Cases from the Community ===== | + | |
- | Below are some excerpts of the discussion on internals mailing list including some use cases that the community see: | + | |
---- | ---- | ||
+ | |||
The use case is one-time usage of an " | The use case is one-time usage of an " | ||
probably pass callbacks into a " | probably pass callbacks into a " | ||
Line 166: | Line 319: | ||
would need several callbacks passed to the constructor. | would need several callbacks passed to the constructor. | ||
Also ' | Also ' | ||
- | |||
- | ---- | ||
- | There are many use cases where anonymous classes are useful, even in the | ||
- | presence of lambdas. I use them quite often when dealing with graphical | ||
- | interfaces and templates. Here is an example: | ||
- | |||
- | <code php> | ||
- | abstract class MyFancyHtmlListView extends UI { | ||
- | protected function IsHeaderVisible(){ return true; } | ||
- | protected function GetListItemMenu(){ return null; } | ||
- | protected function OnItemClick( $item ){ } | ||
- | protected abstract function RenderListItem( $item ); | ||
- | public function Render(){ | ||
- | // echo ... | ||
- | } | ||
- | } | ||
- | </ | ||
- | With anonymous classes we could do something like this: | ||
- | |||
- | <code php> | ||
- | <?= new class extends HTMLView { | ||
- | protected function IsHeaderVisible(){ | ||
- | return false; | ||
- | } | ||
- | protected function RenderListItem( $item ){ | ||
- | // echo ... | ||
- | } | ||
- | } | ||
- | </ | ||
- | The biggest advantage is that a missing RenderListItem could be statically | ||
- | verified. | ||
- | |||
- | It is just a pattern that follows a different way of thinking: Instead of | ||
- | having a list of parameters (including lambdas), we have standard methods | ||
- | that take advantage of all the nice properties of OOP such as abstraction, | ||
- | inheritance and polymorphism. | ||
- | |||
- | ---- | ||
Overriding a specific method in a class is one handy use. Instead of creating | Overriding a specific method in a class is one handy use. Instead of creating | ||
Line 259: | Line 374: | ||
The changes made by this patch mean named nested classes are easier to implement (by a tiny bit). | The changes made by this patch mean named nested classes are easier to implement (by a tiny bit). | ||
- | ===== Proposed Voting Choices | + | ===== References |
- | Straight forward, we should have this, we should not have this. | + | PHP 7 Discussion: http://marc.info/? |
- | ===== Syntax and Examples | + | ===== Proposed Voting Choices |
- | new class (arguments) {definition} | + | The voting choices are yes (in favor for accepting this RFC for PHP 7) or no (against it). |
- | Note: in a previous version of this RFC, the arguments were after the definition, this has been changed to reflect the feedback during the last discussion. | + | ===== Vote ===== |
- | <code php> | + | Vote starts on March 13th, and will end two weeks later, on March 27th. |
- | <?php | + | |
- | /* implementing an anonymous console object from your framework maybe */ | + | |
- | (new class extends ConsoleProgram { | + | |
- | public function main() { | + | |
- | / | + | |
- | } | + | |
- | })-> | + | |
- | /* return an anonymous implementation of a Page for your MVC framework */ | + | This RFC requires |
- | return new class($controller) implements Page { | + | |
- | public function __construct($controller) { | + | |
- | /* ... */ | + | |
- | } | + | |
- | /* ... */ | + | |
- | }; | + | |
- | /* vs */ | + | <doodle title="Anonymous Classes" |
- | class MyPage implements Page { | + | * Yes |
- | public function __construct($controller) { | + | * No |
- | /* ... */ | + | </doodle> |
- | } | + | |
- | /* ... */ | + | |
- | } | + | |
- | return new MyPage($controller); | + | |
- | + | ||
- | /* return an anonymous extension of the DirectoryIterator class */ | + | |
- | return new class($path) extends DirectoryIterator { | + | |
- | /* ... */ | + | |
- | }; | + | |
- | + | ||
- | /* vs */ | + | |
- | class MyDirectoryIterator { | + | |
- | /* .. */ | + | |
- | } | + | |
- | return new MyDirectoryIterator($path); | + | |
- | + | ||
- | /* return an anon class from within another class (introduces the first kind of nested class in PHP) */ | + | |
- | class MyObject extends MyStuff { | + | |
- | public function getInterface() { | + | |
- | return new class implements MyInterface { | + | |
- | /* ... */ | + | |
- | }; | + | |
- | } | + | |
- | } | + | |
- | + | ||
- | + | ||
- | /* return a private object implementing an interface */ | + | |
- | class MyObject extends MyStuff { | + | |
- | /* suitable ctor */ | + | |
- | + | ||
- | private function getInterface() { | + | |
- | return new class(/* suitable ctor args */) extends MyObject implements MyInterface { | + | |
- | /* ... */ | + | |
- | }; | + | |
- | } | + | |
- | } | + | |
- | </code> | + | |
- | + | ||
- | Note: the ability to declare and use a constructor in an anonymous class is necessary where control over construction must be exercised. | + | |
- | + | ||
- | ===== Inheritance/ | + | |
- | + | ||
- | Extending classes works just as you'd expect. | + | |
- | + | ||
- | <code php> | + | |
- | <?php | + | |
- | + | ||
- | class Foo {} | + | |
- | + | ||
- | $child = new class extends Foo {}; | + | |
- | + | ||
- | var_dump($child instanceof Foo); // true | + | |
- | </ | + | |
- | + | ||
- | Traits work identically as in named class definitions too. | + | |
- | + | ||
- | <code php> | + | |
- | <?php | + | |
- | + | ||
- | trait Foo { | + | |
- | public function someMethod() { | + | |
- | return | + | |
- | } | + | |
- | } | + | |
- | + | ||
- | $anonClass | + | |
- | use Foo; | + | |
- | }; | + | |
- | + | ||
- | var_dump($anonClass-> | + | |
- | </ | + | |
- | + | ||
- | ===== Reflection ===== | + | |
- | + | ||
- | The only change to reflection is to add ReflectionClass:: | + | |
- | + | ||
- | ===== Serialization ===== | + | |
- | + | ||
- | Serialization is not supported, and will error just as anonymous functions do. | + | |
- | + | ||
- | ===== Code Paths ===== | + | |
- | + | ||
- | Code such as: | + | |
- | + | ||
- | <code php> | + | |
- | while ($i++< | + | |
- | class myNamedClass { | + | |
- | /* ... */ | + | |
- | } | + | |
- | } | + | |
- | </ | + | |
- | + | ||
- | will fail to execute, however code such as: | + | |
- | + | ||
- | <code php> | + | |
- | while ($i++< | + | |
- | new class {}; | + | |
- | } | + | |
- | </ | + | |
- | + | ||
- | will work as expected: the definition will be re-used, creating a new object. | + | |
- | + | ||
- | ===== Internal Class Naming ===== | + | |
- | + | ||
- | The internal name of an anonymous class is generated with a unique reference based on the scope which created it, such that: | + | |
- | + | ||
- | <code php> | + | |
- | function my_factory_function(){ | + | |
- | return new class{}; | + | |
- | } | + | |
- | </ | + | |
- | + | ||
- | get_class(my_factory_function()) would return | + | |
- | + | ||
- | Multiple anonymous classes created in the same position (say, a loop) can be compared with `==`, but those created elsewhere will not match as they will have a different name. | + | |
- | + | ||
- | <code php> | + | |
- | <?php | + | |
- | + | ||
- | $identicalAnonClasses = []; | + | |
- | + | ||
- | for ($i = 0; $i < 2; $i++) { | + | |
- | $identicalAnonClasses[$i] = new class(99) { | + | |
- | public $i; | + | |
- | public function __construct($i) { | + | |
- | $this->i = $i; | + | |
- | } | + | |
- | }; | + | |
- | } | + | |
- | + | ||
- | var_dump($identicalAnonClasses[0] == $identicalAnonClasses[1]); | + | |
- | + | ||
- | $identicalAnonClasses[2] = new class(99) { | + | |
- | public $i; | + | |
- | public function __construct($i) { | + | |
- | $this->i = $i; | + | |
- | } | + | |
- | }; | + | |
- | + | ||
- | var_dump($identicalAnonClasses[0] == $identicalAnonClasses[2]); | + | |
- | </code> | + | |
- | + | ||
- | Both classes where identical in every way, other than their generated name. | + | |
- | + | ||
- | + | ||
- | ===== Implementation ===== | + | |
- | + | ||
- | https:// | + | |
- | + | ||
- | ===== References ===== | + | |
- | + | ||
- | PHP 7 Discussion: http:// | + | |
===== Changelog ===== | ===== Changelog ===== | ||
Line 452: | Line 402: | ||
* v0.3: ReflectionClass:: | * v0.3: ReflectionClass:: | ||
* v0.2: Brought back for discussion | * v0.2: Brought back for discussion | ||
- | * v0.1: Initial | + | * v0.1: Initial |
+ | |||
+ | ===== Implementation ===== | ||
+ | |||
+ | https:// |
rfc/anonymous_classes.txt · Last modified: 2017/09/22 13:28 by 127.0.0.1