rfc:anonymous_classes
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
rfc:anonymous_classes [2015/03/13 19:26] – Initiated vote philstu | rfc:anonymous_classes [2017/09/22 13:28] (current) – external edit 127.0.0.1 | ||
---|---|---|---|
Line 4: | Line 4: | ||
* 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 218: | Line 373: | ||
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 ===== | ||
- | |||
- | Straight forward, we should have this, we should not have this. | ||
- | |||
- | ===== 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. | ||
- | |||
- | ===== 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 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. | ||
- | |||
- | |||
- | ===== Implementation ===== | ||
- | |||
- | https:// | ||
===== References ===== | ===== References ===== | ||
Line 409: | Line 378: | ||
PHP 7 Discussion: http:// | PHP 7 Discussion: http:// | ||
- | ===== Vote ===== | + | ===== Proposed Voting Choices |
The voting choices are yes (in favor for accepting this RFC for PHP 7) or no (against it). | The voting choices are yes (in favor for accepting this RFC for PHP 7) or no (against it). | ||
- | This RFC requires a 2/3 majority. | + | ===== Vote ===== |
Vote starts on March 13th, and will end two weeks later, on March 27th. | Vote starts on March 13th, and will end two weeks later, on March 27th. | ||
- | <doodle title=" | + | This RFC requires a 2/3 majority. |
+ | |||
+ | <doodle title=" | ||
* Yes | * Yes | ||
* No | * No | ||
Line 431: | 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.1426274787.txt.gz · Last modified: 2017/09/22 13:28 (external edit)