rfc:closures:object-extension
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
rfc:closures:object-extension [2009/11/16 19:11] – updated rfc cseiler | rfc:closures:object-extension [2017/09/22 13:28] (current) – external edit 127.0.0.1 | ||
---|---|---|---|
Line 1: | Line 1: | ||
====== Closures: Object extension ====== | ====== Closures: Object extension ====== | ||
+ | * Date: 2009-01-22 | ||
+ | * Author: Unknown | ||
+ | * Status: Implemented in PHP 5.4 | ||
+ | |||
+ | ===== Introduction ===== | ||
Efforts were made to go beyond the scope of the original Closures proposal ([[rfc/ | Efforts were made to go beyond the scope of the original Closures proposal ([[rfc/ | ||
Line 44: | Line 49: | ||
===== History ===== | ===== History ===== | ||
- | For PHP 5.3 $this support for Closures was removed because no consensus could be reached how to implement it in a sane fashion. This RFC describes the possible roads that can be taken to implement it in PHP 6. | + | For PHP 5.3 $this support for Closures was removed because no consensus could be reached how to implement it in a sane fashion. This RFC describes the possible roads that can be taken to implement it in the next PHP version. |
===== Approaches to $this binding ===== | ===== Approaches to $this binding ===== | ||
Line 358: | Line 363: | ||
==== Object cloning ==== | ==== Object cloning ==== | ||
- | When an object containing closure methods is cloned, the closure methods should **not** be automatically rebound but rather should the object have to do it itself in the __clone method. With this behaviour, the user has the control over whether to rebind the closures on cloning or not. | + | When an object containing closure methods is cloned, the closure methods should **not** be automatically rebound but rather should the object have to do it itself in the %%__clone%% method. With this behaviour, the user has the control over whether to rebind the closures on cloning or not. |
This may still be subject to discussion. | This may still be subject to discussion. | ||
+ | ===== Status as of August 10 2010 ===== | ||
+ | |||
+ | In April 2010 was [[http:// | ||
+ | |||
+ | ==== Calling methods ==== | ||
+ | |||
+ | An implementation of this feature has not been commited. One cannot call closures stored in properties as if they were methods. | ||
+ | |||
+ | This [[http:// | ||
+ | |||
+ | The following two graphs explain which combinations are allowed of staticness of method calls, properties where the closures are stored and the closures themselves, as implemented in the patch (green means " | ||
+ | |||
+ | {{: | ||
+ | |||
+ | {{: | ||
+ | |||
+ | Other issues: | ||
+ | |||
+ | - Do we really want to make closures-as-methods have priority over '' | ||
+ | - Properties are not case-sensitive, | ||
+ | - What to do with properties with no visibility? Ignore them (and let fall back to '' | ||
+ | - What to do with properties used as methods that are not closures? Ignore them or raise an error (the implementation raises an error). | ||
+ | - Should we throw an exception when calling a closure-as-instance method that's stored as a static method? Usually, accessing a static property in a non static context raises an E_STRICT, but I think may very well be very useful, because we can swap instance method implementations on a class basis instead of only an instance basis. | ||
+ | |||
+ | Note that, contrary to what the proposal says, this will not work at all (fatal error, not warning): | ||
+ | |||
+ | <code php> | ||
+ | // $obj is not $this | ||
+ | $obj-> | ||
+ | //this is a method call, we need an instance or a static closure! | ||
+ | $obj-> | ||
+ | //only if the closure is actually bound to some other object (as opposed to not be bound at all), will we get_ | ||
+ | // WARNING: Closure called as method but bound object differs from containing object. | ||
+ | </ | ||
+ | |||
+ | ==== Private/ | ||
+ | |||
+ | The currently implemented handling of scope for class closures is: | ||
+ | |||
+ | - They initially inherit the (calling) scope of the class they were created in. | ||
+ | - After that, always use the class scope of the object that bindTo() is called for ([[# | ||
+ | - If there' | ||
+ | |||
+ | The implementation of option #2 has serious drawbacks. Consider the following code: | ||
+ | |||
+ | <code php> | ||
+ | class foo { | ||
+ | private $field = " | ||
+ | function getClosure() { | ||
+ | return function () { | ||
+ | echo $this-> | ||
+ | }; | ||
+ | } | ||
+ | } | ||
+ | class subFoo extends foo {} | ||
+ | |||
+ | $f = new subFoo(); | ||
+ | $g = new subFoo(); | ||
+ | $c = $f-> | ||
+ | $c(); //foo | ||
+ | $c = $c-> | ||
+ | $c(); //fails | ||
+ | </ | ||
+ | |||
+ | Since it's always taking the class of the bound object as scope, this means we have no way to keep the original scope of the closure without binding an instance of exactly the same class. It's against the basic principles of OOP to have something that works when passed A, but not when passed a subclass of A. | ||
+ | |||
+ | There' | ||
+ | |||
+ | Therefore, I propose an implementation ([[http:// | ||
+ | |||
+ | < | ||
+ | Closure Closure:: | ||
+ | </ | ||
+ | |||
+ | If the last argument is not given, or if " | ||
+ | |||
+ | The patch preserves these invariants: | ||
+ | - A static closure, being scoped or not, cannot have any bound instance. | ||
+ | - A non static closure has a bound instance iif it is scoped. | ||
+ | |||
+ | To preserve these invariants, there are these additional rules: | ||
+ | - If a non static closure is given a scope (or it already has a scope, but the scope parameter is not specified) and a NULL instance, it's made static. | ||
+ | - If a static closure is given an instance, the instance is ignored and an '' | ||
+ | - If a non static non scoped (and therefore non bound) instance is given no scope and a non NULL instance, it's given a dummy scope (currently the " | ||
+ | |||
+ | Example: | ||
+ | |||
+ | <code php> | ||
+ | class A { | ||
+ | private $x; | ||
+ | |||
+ | public function __construct($v) { | ||
+ | $this-> | ||
+ | } | ||
+ | |||
+ | public function getIncrementor() { | ||
+ | return function() { return ++$this-> | ||
+ | } | ||
+ | } | ||
+ | class B extends A { | ||
+ | private $x; | ||
+ | public function __construct($v) { | ||
+ | parent:: | ||
+ | $this-> | ||
+ | } | ||
+ | } | ||
+ | |||
+ | $a = new A(0); | ||
+ | $b = new B(10); | ||
+ | |||
+ | $ca = $a-> | ||
+ | var_dump($ca()); | ||
+ | |||
+ | echo " | ||
+ | |||
+ | $cb = $ca-> | ||
+ | $cb2 = Closure:: | ||
+ | var_dump($cb()); | ||
+ | var_dump($cb2()); | ||
+ | |||
+ | echo " | ||
+ | |||
+ | $cb = $ca-> | ||
+ | $cb2 = Closure:: | ||
+ | var_dump($cb()); | ||
+ | var_dump($cb2()); | ||
+ | |||
+ | $cb = $ca-> | ||
+ | var_dump($cb()); | ||
+ | </ |
rfc/closures/object-extension.1258398662.txt.gz · Last modified: 2017/09/22 13:28 (external edit)