rfc:closure_apply

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
rfc:closure_apply [2014/08/06 13:29] – Stretch code over multiple lines ajfrfc:closure_apply [2017/09/22 13:28] (current) – external edit 127.0.0.1
Line 1: Line 1:
 ====== PHP RFC: Closure::call ====== ====== PHP RFC: Closure::call ======
-  * Version: 0.2.1 +  * Version: 0.3 
-  * Date: 2014-07-29, put to internals 2014-08-03, latest 2014-08-06+  * Date: 2014-07-29, put to internals 2014-08-03, latest 2014-08-19
   * Author: Andrea Faulds, ajf@ajf.me   * Author: Andrea Faulds, ajf@ajf.me
-  * Status: Under Discussion+  * Status: Accepted
   * First Published at: http://wiki.php.net/rfc/closure_apply   * First Published at: http://wiki.php.net/rfc/closure_apply
  
Line 18: Line 18:
 </code> </code>
  
-It calls the closure with the given parameters and returns the result, with ''$this'' bound to the given object ''$to'', using the closure's current scope. Like the ''bind''(''To'') methods, a static class cannot be bound (using ''->call'' will fail).+It calls the closure with the given parameters and returns the result, with ''$this'' bound to the given object ''$to'', using the scope of the given object. Like the ''bind''(''To'') methods, a static class cannot be bound (using ''->call'' will fail).
  
 It can be used like so: It can be used like so:
Line 29: Line 29:
 </code> </code>
  
-Because the ''->call'' method, unlike ''bind''(''To''), does not take a scope parameter (as scope is internally a property of the function)then if you wish to use a different scope, you must manually create a new closure with the desired scope prior to application. However, closures that are scoped must currently either be static (cannot be bound or used with ''->call'') or bound. This would mean that you would have to bind the closure to some dummy object in order to produce a closure with the desired scope for use with ''->call''This is inconvenient at best. To solve this, we relax the current invariant of scoped closures having to be bound, and add a new parameter to ''bind''(''To'') to produce an unbound, scoped closure, like so:+The ''->call'' method, unlike ''bind''(''To''), does not take a scope parameter. Insteadit will always use the class of the object as its scope. Thus:
  
 <code php> <code php>
 class Foo { private $x = 3; } class Foo { private $x = 3; }
 $foo = new Foo; $foo = new Foo;
-$foobar = function () { var_dump($this->bar); }+$foobar = function () { var_dump($this->x); };
-// without the last parameter (optional, defaults to false), we'd get a static, not unbound closure +
-$foobar = $foobar->bindTo(null, 'FooBar', true);+
 $foobar->call($foo); // prints int(3) $foobar->call($foo); // prints int(3)
 </code> </code>
  
-My [[rfc:function_referencing|Function Referencing as Closures]] proposal similarly relaxes the requirement for a scoped closure to be bound or static out of necessity, and ''->call'' would be highly useful to that proposal, so this RFC can be considered a prerequisite to it.+''call'' would be useful in many cases where ''bindTo'' is used (e.g. [[https://github.com/search?l=php&p=34&q=bindTo&ref=cmdform&type=Code|search of GitHub for bindTo]]). A search on GitHub reveals [[https://github.com/search?q=bindTo+call_user_func&type=Code&ref=searchresults|many using bindTo and immediately calling with call_user_func]], which would now not be necessary as they could just use ''call''.
  
 ===== Performance ===== ===== Performance =====
  
-While not the sole benefit of this RFC, it can provide a significant performance impact:+While not the sole benefit of this RFC, it can provide a performance improvement in some applications.
  
-<code> +We use two test scripts, ''a.php'' using bindTo and ''b.php'' using call. 
-andreas-air:php-src ajf$ time sapi/cli/php -r '\ + 
-$a = function () { return $this->x; };\ +<file php a.php> 
-class FooBar { private $x = 3; }\ +$a = function () { 
-$foobar = new FooBar;\ +    return $this->x; 
-for ($i = 0; $i < 100000; $i++) {\ +}; 
-    $x = $a->bindTo($foobar, "FooBar");\ +class FooBar { 
-    $x();\ +    private $x = 3; 
-}' +
-  +$foobar = new FooBar; 
-real 0m0.259s +for ($i = 0; $i < 1000000; $i++) { 
-user 0m0.208s +    $x = $a->bindTo($foobar, "FooBar"); 
-sys 0m0.012s +    $x(); 
-  +
-andreas-air:php-src ajf$ time sapi/cli/php -r '\ +</file> 
-$a = function () { return $this->x; };+ 
-$a = $a->bindTo(NULL, "FooBar", true);\ +<file php b.php> 
-class FooBar { private $x = 3; }\ +$a = function () { 
-$foobar = new FooBar;\ +    return $this->x; 
-for ($i = 0; $i < 100000; $i++) {\ +}; 
-    $a->call($foobar);\ +class FooBar { 
-}+    private $x = 3; 
-  +
-real 0m0.100s +$foobar = new FooBar; 
-user 0m0.094s +for ($i = 0; $i < 1000000; $i++) { 
-sys 0m0.005s +    $a->call($foobar); 
-</code+
-  +</file
-If we up the iterations by 10x, the result is the same:+ 
 +''b.php'' shows a 2.18x improvement over ''a.php'':
  
 <code> <code>
-andreas-air:php-src ajf$ time sapi/cli/php -r '\ +andreas-air:php-src ajf$ time sapi/cli/php a.php 
-$= function () { return $this->x; };\ + 
-class FooBar { private $x = 3; }\ +real 0m1.877s 
-$foobar = new FooBar;\ +user 0m1.835s 
-for ($i = 0; $i < 1000000; $i++) {\ +sys 0m0.025s 
-    $x = $a->bindTo($foobar, "FooBar");+ 
-    $x();\ +andreas-air:php-src ajf$ time sapi/cli/php b.php 
-}' + 
-  +real 0m0.859s 
-real 0m1.966s +user 0m0.826s 
-user 0m1.959s +sys 0m0.018s
-sys 0m0.005s +
-  +
-andreas-air:php-src ajf$ time sapi/cli/php -r '\ +
-$a = function () { return $this->x; };\ +
-$a = $a->bindTo(NULL, "FooBar", true);\ +
-class FooBar { private $x = 3; }\ +
-$foobar = new FooBar;\ +
-for ($i = 0; $i < 1000000; $i++) {\ +
-    $a->call($foobar);+
-}' +
-  +
-real 0m0.962s +
-user 0m0.897s +
-sys 0m0.015s+
 </code> </code>
  
Line 110: Line 95:
 ===== Proposed PHP Version(s) ===== ===== Proposed PHP Version(s) =====
  
-This is proposed for the next version of PHP, either the next 5.x or PHP NEXT, whichever comes sooner. The patch is based on master, intended for the next 5.x.+This is proposed for the next version of PHP, either the next 5.x or PHP NEXT, whichever comes sooner. The patch is based on master, intended for PHP 7.
  
 ===== Future Scope ===== ===== Future Scope =====
Line 116: Line 101:
 Partial application (where a new closure is returned that pre-fills the first X arguments) is a possibly worthwhile (though more difficult to implement) addition. Partial application (where a new closure is returned that pre-fills the first X arguments) is a possibly worthwhile (though more difficult to implement) addition.
  
-===== Proposed Voting Choices =====+===== Vote =====
  
 This is not a language change, so a straight 50%+1 Yes/No vote can be held. This is not a language change, so a straight 50%+1 Yes/No vote can be held.
 +
 +Voting started 2014-08-17 but was cancelled the same day due to the removal of unbound scoped closures.
 +
 +Voting started again on 2014-08-20 and ended 2014-08-27.
 +
 +<doodle title="Closure::apply() (Approve RFC and merge into master?)" auth="ajf" voteType="single" closed="true">
 +   * Yes
 +   * No
 +</doodle>
  
 ===== Patches and Tests ===== ===== Patches and Tests =====
  
 A branch which implements this (with a test) based on the current master can be found here: https://github.com/TazeTSchnitzel/php-src/tree/Closure_apply A branch which implements this (with a test) based on the current master can be found here: https://github.com/TazeTSchnitzel/php-src/tree/Closure_apply
 +
 +There is a pull request for review purposes here: https://github.com/php/php-src/pull/775
  
 ===== References ===== ===== References =====
  
-  * As previously mentioned, my [[rfc:function_referencing|Function Referencing as Closures]] RFC has this RFC as a prerequisite+  * My [[rfc:function_referencing|Function Referencing as Closures]] RFC has this RFC as a prerequisite
  
 ===== Changelog ===== ===== Changelog =====
  
 +  * v0.3 - Removed unbound scoped closures, made ''->call'' use class of object as its scope
   * v0.2.1 - Added performance section   * v0.2.1 - Added performance section
   * v0.2 - ''Closure::apply'' renamed to ''Closure::call'' for consistency with JavaScript (former takes an array in JS à la ''call_user_func_array'', latter bare parameters)   * v0.2 - ''Closure::apply'' renamed to ''Closure::call'' for consistency with JavaScript (former takes an array in JS à la ''call_user_func_array'', latter bare parameters)
   * v0.1 - Initial version   * v0.1 - Initial version
rfc/closure_apply.1407331768.txt.gz · Last modified: 2017/09/22 13:28 (external edit)