rfc:closure_apply

Differences

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

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
rfc:closure_apply [2014/08/06 13:33]
ajf Nicely formatted perf test cases
rfc:closure_apply [2017/09/22 13:28] (current)
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.
  
 We use two test scripts, ''a.php'' using bindTo and ''b.php'' using call. We use two test scripts, ''a.php'' using bindTo and ''b.php'' using call.
Line 56: Line 54:
 } }
 $foobar = new FooBar; $foobar = new FooBar;
-for ($i = 0; $i < 100000; $i++) {+for ($i = 0; $i < 1000000; $i++) {
     $x = $a->bindTo($foobar, "FooBar");     $x = $a->bindTo($foobar, "FooBar");
     $x();     $x();
Line 66: Line 64:
     return $this->x;     return $this->x;
 }; };
-$a = $a->bindTo(NULL, "FooBar", true); 
 class FooBar { class FooBar {
     private $x = 3;     private $x = 3;
 } }
 $foobar = new FooBar; $foobar = new FooBar;
-for ($i = 0; $i < 100000; $i++) {+for ($i = 0; $i < 1000000; $i++) {
     $a->call($foobar);     $a->call($foobar);
 } }
 </file> </file>
  
-When run for 100000 iterations, ''b.php'' shows a 2x improvement over ''a.php'':+''b.php'' shows a 2.18x improvement over ''a.php'':
  
 <code> <code>
 andreas-air:php-src ajf$ time sapi/cli/php a.php andreas-air:php-src ajf$ time sapi/cli/php a.php
-  
-real 0m0.259s 
-user 0m0.208s 
-sys 0m0.012s 
-  
-andreas-air:php-src ajf$ time sapi/cli/php b.php 
-  
-real 0m0.100s 
-user 0m0.094s 
-sys 0m0.005s 
-</code> 
-  
-If we up the iterations by 10x, the result is the same: 
  
-<code> +real 0m1.877s 
-andreas-air:php-src ajf$ time sapi/cli/php a.php +user 0m1.835s 
-  +sys 0m0.025s 
-real 0m1.966s +
-user 0m1.959s +
-sys 0m0.005s +
- +
 andreas-air:php-src ajf$ time sapi/cli/php b.php andreas-air:php-src ajf$ time sapi/cli/php b.php
-  + 
-real 0m0.962s +real 0m0.859s 
-user 0m0.897s +user 0m0.826s 
-sys 0m0.015s+sys 0m0.018s
 </code> </code>
  
Line 114: 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 120: 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.1407331993.txt.gz · Last modified: 2017/09/22 13:28 (external edit)