rfc:closures:object-extension
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:closures:object-extension [2010/08/10 22:49] – closures-as-methods cataphract | rfc:closures:object-extension [2013/10/29 17:38] – -> Implemented nikic | ||
---|---|---|---|
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 397: | Line 402: | ||
</ | </ | ||
+ | ==== 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.txt · Last modified: 2017/09/22 13:28 by 127.0.0.1