rfc:closures

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:closures [2008/06/21 11:35] – Fugbix typo. sebastianrfc:closures [2017/09/22 13:28] (current) – external edit 127.0.0.1
Line 1: Line 1:
 ====== Request for Comments: Lambda functions and closures ====== ====== Request for Comments: Lambda functions and closures ======
-  * Version: 1.0 +  * Version: 1.2 
-  * Date: 2008-06-16 +  * Date: 2008-07-01 
-  * Author: Christian Seiler <chris_se@gmx.net> +  * Author: Christian Seiler <chris_se@gmx.net>, Dmitry Stogov <dmitry@zend.com
-  * Status: Under Discussion+  * Status: Implemented
  
 This RFC discusses the introduction of compile-time lambda functions and closures in PHP. This RFC discusses the introduction of compile-time lambda functions and closures in PHP.
Line 10: Line 10:
  
 End of 2007 a patch was proposed that would add lambda functions (but without closures) to PHP. During the discussion on the mailing list, several people suggested that without support for closures, lambda functions are not useful enough to add them to PHP. This proposal describes a viable method of adding lambda functions with closure support to PHP. End of 2007 a patch was proposed that would add lambda functions (but without closures) to PHP. During the discussion on the mailing list, several people suggested that without support for closures, lambda functions are not useful enough to add them to PHP. This proposal describes a viable method of adding lambda functions with closure support to PHP.
 +
 +The initial posting of this proposal has created quite a bit of discussion on the list. This updated proposal including an updated patch intends to incorporate the result of that discussion. A lot of changes to the original patch by Christian Seiler were made by Dmitry Stogov.
  
 ===== Why do we need closures and lambda functions? ===== ===== Why do we need closures and lambda functions? =====
  
 Closures and lambda functions can make programming much easier in several ways: Closures and lambda functions can make programming much easier in several ways:
- 
 ==== Lambda Functions ==== ==== Lambda Functions ====
  
-Lambda functions allow the quick definition of throw-away functions that are not used elsewhere. Imaging for example a piece of code that needs to call preg_replace_callback(). Currently, there are three possibilities to achieve this: +Lambda functions allow the quick definition of throw-away functions that are not used elsewhere. Imagine for example a piece of code that needs to call preg_replace_callback(). Currently, there are three possibilities to achieve this: 
- +  - Define the callback function elsewhere. This distributes code that belongs together throughout the file and decreases readability. 
-   - Define the callback function elsewhere. This distributes code that belongs together throughout the file and decreases readability. +  - Define the callback function in-place (but with a name). In that case one has to use function_exists() to make sure the function is only defined once. Here, the additional if() around the function definition makes the source code difficult to read. Example code:<code php>
- +
-   - Define the callback function in-place (but with a name). In that case one has to use function_exists() to make sure the function is only defined once. Here, the additional if() around the function definition makes the source code difficult to read. Example code: +
- +
-<code php>+
    function replace_spaces ($text) {    function replace_spaces ($text) {
      if (!function_exists ('replace_spaces_helper')) {      if (!function_exists ('replace_spaces_helper')) {
Line 32: Line 29:
      return preg_replace_callback ('/( +) /', 'replace_spaces_helper', $text);      return preg_replace_callback ('/( +) /', 'replace_spaces_helper', $text);
    }    }
- </code> +</code> 
- +  - Use the present create_function() in order to create a function at runtime. This approach has several disadvantages: First of all, syntax highlighting does not work because a string is passed to the function. It also compiles the function at run time and not at compile time so opcode caches can't cache the function.
-   - Use the present create_function() in order to create a function at runtime. This approach has several disadvantages: First of all, syntax highlighting does not work because a string is passed to the function. It also compiles the function at run time and not at compile time so opcode caches can't cache the function. +
 ==== Closures ==== ==== Closures ====
  
-Closures provide a very useful tool in order to make lambda functions even more useful. Just imagine you want to replace 'hello' through 'goodbye' in all elements of an array. PHP provides the array_map() function which accepts a callback. If you don'wan'to hard-code 'hello' and 'goodbye' into your sourcecode, you have only four choices: +Closures provide a very useful tool in order to make lambda functions even more useful. Just imagine you want to replace 'hello' through 'goodbye' in all elements of an array. PHP provides the array_map() function which accepts a callback. If you don'want to hard-code 'hello' and 'goodbye' into your sourcecode, you have only four choices: 
- +  - Use create_function(). But then you may only pass literal values (strings, integers, floats) into the function, objects at best as clones (if var_export() allows for it) and resources not at all. And you have to worry about escaping everything correctly. Especially when handling user input this can lead to all sorts of security issues. 
-   - Use create_function(). But then you may only pass literal values (strings, integers, floats) into the function, objects at best as clones (if var_export() allows for it) and resources not at all. And you have to worry about escaping everything correctly. Especially when handling user input this can lead to all sorts of security issues. +  - Write a function that uses global variables. This is ugly, non-reentrant and bad style. 
- +  - Create an entire class, instantiate it and pass the member function as a callback. This is perhaps the cleanest solution for this problem with current PHP but just think about it: Creating an entire class for this extremely simple purpose and nothing else seems overkill. 
-   - Write a function that uses global variables. This is ugly, non-reentrant and bad style. +  - Don't use array_map() but simply do it manually (foreach). In this simple case it may not be that much of an issue (because one simply wants to iterate over an array) but there are cases where doing something manually that a function with a callback as parameter does for you is quite tedious.
- +
-   - Create an entire class, instantiate it and pass the member function as a callback. This is perhaps the cleanest solution for this problem with current PHP but just think about it: Creating an entire class for this extremely simple purpose and nothing else seems overkill. +
- +
-   - Don't use array_map() but simply do it manually (foreach). In this simple case it may not be that much of an issue (because one simply wants to iterate over an array) but there are cases where doing something manually that a function with a callback as parameter does for you is quite tedious.+
  
 Note: str_replace also accepts arrays as a third parameter so this example may be a bit useless. But imagine you want to do a more complex operation than simple search and replace. Note: str_replace also accepts arrays as a third parameter so this example may be a bit useless. But imagine you want to do a more complex operation than simple search and replace.
- 
 ===== Common misconceptions ===== ===== Common misconceptions =====
  
-?+   - Lambda functions / closures are **not** a way of dynamically extending classes by additional methods at runtime. There are several other possibilities to do this, including the already present _ _call semantic. 
 +   - PHP's notion of scope is quite different than the notion of scope other languages define. Combine this with variable variables ($$var) and it becomes clear that automatically detecting which variables from the outer scope are referenced inside are closure is impossible. Also, since for example global variables are not visible inside functions either by default, automatically making the parent scope available would break with the current language concept PHP follows.
  
 ===== Proposal and Patch ===== ===== Proposal and Patch =====
  
 The following proposal and patch implement compile-time lambda functions and closures for PHP while keeping the patch as simple as possible. The following proposal and patch implement compile-time lambda functions and closures for PHP while keeping the patch as simple as possible.
- 
 ==== Userland perspective ==== ==== Userland perspective ====
  
Line 64: Line 54:
 The patch adds the following syntax as a valid expression: The patch adds the following syntax as a valid expression:
  
- <code php> +<code php> 
-   function & (parameters) { body } +   function & (parameters) use (lexical vars) { body } 
- </code>+</code>
  
-(The & is optional and indicates - just as with normal functions - that the anonymous function returns a reference instead of a value)+The & is optional and indicates that the function should return a reference. The use followed by the parentheses is optional and indicates that several variables from the current scope should be imported into the closure.
  
 Example usage: Example usage:
  
- <code php>+<code php>
    $lambda = function () { echo "Hello World!\n"; };    $lambda = function () { echo "Hello World!\n"; };
- </code>+</code>
  
 The variable $lambda then contains a callable resource that may be called through different means: The variable $lambda then contains a callable resource that may be called through different means:
  
- <code php>+<code php>
    $lambda ();    $lambda ();
    call_user_func ($lambda);    call_user_func ($lambda);
    call_user_func_array ($lambda, array ());    call_user_func_array ($lambda, array ());
- </code>+</code>
  
 This allows for simple lambda functions, for example: This allows for simple lambda functions, for example:
  
- <code php>+<code php>
    function replace_spaces ($text) {    function replace_spaces ($text) {
      $replacement = function ($matches) {      $replacement = function ($matches) {
Line 93: Line 83:
      return preg_replace_callback ('/( +) /', $replacement, $text);      return preg_replace_callback ('/( +) /', $replacement, $text);
    }    }
- </code>+</code>
  
-=== Closure support via ''lexical'' keyword ===+You can even put the lambda function inline, for example:
  
-The patch implements closures by defining an additional keyword 'lexicalthat allows an lambda function (and *only* an lambda functionto import a variable from the "parent scope" to the lambda function scopeExample:+<code php> 
 +  function replace_spaces ($text) { 
 +    return preg_replace_callback ('/( +) /'
 +      function ($matches) { 
 +        return str_replace ($matches[1], ' ', '&nbsp;').' '; 
 +      }, $text); 
 +  } 
 +</code>
  
- <code php>+=== Closure support === 
 + 
 +In order to make use of variables defined in the parent scope, this patch proposes the following syntax to import variables from the parent scope into the closure scope: 
 + 
 +<code php> 
 +  function (normal parameters) use ($var1, $var2, &$refvar) {} 
 +</code> 
 + 
 +The variables $var1, $var2 and $refvar defined in the parent scope will be visible inside the lambda function. For the behaviour with regard to references, see below. 
 + 
 +Simple example: 
 + 
 +<code php>
    function replace_in_array ($search, $replacement, $array) {    function replace_in_array ($search, $replacement, $array) {
-     $map = function ($text) +     $map = function ($text) use ($search, $replacement) {
-       lexical $search, $replacement;+
        if (strpos ($text, $search) > 50) {        if (strpos ($text, $search) > 50) {
          return str_replace ($search, $replacement, $text);          return str_replace ($search, $replacement, $text);
Line 109: Line 117:
        }        }
      };      };
-     return array_map ($map, array);+     return array_map ($map, $array);
    }    }
- </code>+</code> 
 + 
 +The variables $search and $replacement are variables in the scope of the function replace_in_array() and they are imported into the scope of the closure upon creation of the closure.  
 + 
 +=== Closure lifetime === 
 + 
 +Closures may live longer as the methods that declared them. It is perfectly possible to have something like this: 
 + 
 +<code php> 
 +   function getAdder($x) { 
 +     return function ($y) use ($x) { 
 +       // or: lexical $x; 
 +       return $x + $y; 
 +     }; 
 +   } 
 +</code> 
 + 
 +=== References vs. Copies === 
 + 
 +By default, all imported variables are copied as values into the closure. This makes it impossible for a closure to modify the variable in the parent scope. By prepending an & in front of the variable name in the use declaration, the variable is imported as a reference instead. In that case, changes to the variable inside the closure will affect the outside scope. 
 + 
 +Example: 
 + 
 +<code php> 
 +  $x = 1; 
 +  $lambda1 = function () use ($x) { 
 +    $x *= 2; 
 +  }; 
 +  $lambda2 = function () use (&$x) { 
 +    $x *= 3; 
 +  }; 
 +  $lambda1 (); 
 +  var_dump ($x); // gives: 1 
 +  $lambda2 (); 
 +  var_dump ($x); // gives: 3 
 +</code>
  
-The variables $search and $replacement are variables in the scope of the function replace_in_array(and the lexical keyword imports these variables into the scope of the closure. The variables are imported as reference, so any change in the closure will result in a change in the variable of the function itself.+Support for references are necessary in order to achieve true closures (like in Javascript, where a variable originating in parent scope can be modified by closureswhile copying per default fits best with the current semantics of PHP and does not cause headaches in loops (for example, when importing a loop index into a closure).
  
 === Interaction with OOP === === Interaction with OOP ===
  
-If a closure is defined inside an object, the closure has full access to the current object through $this (without the need to use 'lexical' to import it seperately) and all private and protected methods of that class. This also applies to nested closures. Example:+$this support has been removed, see [[rfc/closures/removal-of-this|removal of this]]
  
- <code php>+If a closure is defined inside an object, the closure has full access to the current object through $this (without the need to import it explicitly) and all private and protected methods of that class. This also applies to nested closures. Example: 
 + 
 +<code php>
      class Example {      class Example {
        private $search;        private $search;
Line 132: Line 177:
  
        public function getReplacer ($replacement) {        public function getReplacer ($replacement) {
-         return function ($text) {+         return function ($text) use ($replacement) {
            return str_replace ($this->search, $replacement, $text);            return str_replace ($this->search, $replacement, $text);
          };          };
Line 141: Line 186:
      $replacer = $example->getReplacer ('goodbye');      $replacer = $example->getReplacer ('goodbye');
      echo $replacer ('hello world'); // goodbye world      echo $replacer ('hello world'); // goodbye world
-     $replacer->setSearch ('world');+     $example->setSearch ('world');
      echo $replacer ('hello world'); // hello goodbye      echo $replacer ('hello world'); // hello goodbye
- </code>+</code>
  
-As one can see, defining a closure inside a class method does not change the semantics at all - it simply does not matter if a closure is defined in global scope, within a function or within a class method. The only small difference is that closures defined in class methods may also access the class and the current object via $this.+As one can see, defining a closure inside a class method does not change the semantics at all - it simply does not matter if a closure is defined in global scope, within a function or within a class method. The only small difference is that closures defined in class methods may also access the class and the current object via $this. Since $this is saved "within the closure" the corresponding object will live at least as long as the closure.
  
-=== Closure lifetime ===+Because not all closures defined in class methods need $this, it is possible to declare a lambda function to be static:
  
-Closures may live longer as the methods that declared them. It is perfectly possible to have something like this:+<code php> 
 +     class Example { 
 +       public function doSomething () { 
 +         $x = 4; 
 +         $closure = static function ($y) use ($x) { 
 +           return $x + $y; 
 +         }; 
 +         return $closure (6); 
 +       } 
 +     } 
 +</code>
  
- <code php> +In this case, $this is not available inside the closure. This may save a lot of memory if saves many closures that originated in longer needed objects. 
-   function getAdder($x) { + 
-     return function ($y) { + 
-       lexical $x; +==== Additional goody: _ _invoke ==== 
-       return $x $y+ 
-     }; +Since closures implement a new type of variable that may be called dynamically (i.e. objects), the idea came up that generic callable could also be implemented. This patch adds an additional magic method _ _invoke that may be defined in arbitrary classes. If defined, the object itself is callable and the new special method will be invoked instead of the object. Example: 
-   + 
- </code>+<code php> 
 +class Example { 
 +  public function __invoke () { 
 +    echo "Hello World!\n"; 
 +  } 
 +
 +$foo = new Example; 
 +$foo ()
 +</code> 
 + 
 +==== Interaction with reflection (1) ==== 
 + 
 +Since closures are anonymous, they do **not** appear in reflection. 
 + 
 +However, a new method was added to the ReflectionMethod and ReflectionFunction classes: getClosure. This method returns a dynamically created closure for the specified function. Example: 
 + 
 +<code php> 
 +class Example 
 +  static function printer () { echo "Hello World!\n";
 +
 + 
 +$class = new ReflectionClass ('Example')
 +$method = $class->getMethod ('printer'); 
 +$closure = $method->getClosure (); 
 +$closure (); 
 +</code> 
 + 
 +This example dynamically creates a callable object of the static method "printer" of the "Example" class. Calling that closure is like calling the method directly. This also works for non-static methods - here getClosure expects a single parameter for the $this pointer: 
 + 
 +<code php> 
 +class Example 
 +  public $x = 4
 +  function printer () { echo "Hello World: $this->x!\n"; } 
 +
 + 
 +$class = new ReflectionClass ('Example')
 +$method = $class->getMethod ('printer'); 
 + 
 +$object = new Example; 
 +$closure = $method->getClosure ($object); 
 +$closure (); 
 +$object->x = 5; 
 +$closure (); 
 +</code> 
 + 
 +==== Interaction with reflection (2) ==== 
 + 
 +In addition to the previous patch, reflection support was augmented to support reflecting closure objects and returning the correct function pointer. 
 + 
 +<code php> 
 +$closure = function ($a, &$b, $c = null) { }; 
 +$m = new ReflectionMethod ($closure, '__invoke'); 
 +Reflection::export ($m); 
 +</code> 
 + 
 +This will yield: 
 + 
 +<code> 
 +Method [ <internal> public method __invoke ] { 
 + 
 +  - Parameters [3] { 
 +    Parameter #0 [ <required> $a ] 
 +    Parameter #1 [ <required> &$b ] 
 +    Parameter #2 [ <optional> $c ] 
 +  } 
 +
 +</code> 
 + 
 +The following will also work (invoke is implied if no method name is specified): 
 + 
 +<code php> 
 +$m = new ReflectionMethod ($closure); 
 +$p = new ReflectionParameter ($closure, 0); 
 +$p = new ReflectionParameter ($closure, 'a'); 
 +$p = new ReflectionParameter (array ($closure, '__invoke'), 0); 
 +</code>
  
 ==== Zend internal perspective ==== ==== Zend internal perspective ====
Line 164: Line 294:
 The patch basically changes the following in the Zend engine: The patch basically changes the following in the Zend engine:
  
-When the compiler reaches a lambda function, it creates unique name for that function (''"\0__compiled_lambda_FILENAME_N"'' where FILENAME is the name of the file currently processed and N is a per-file counter). The use of the filename in the function name ensures compability with opcode caches. The lambda function is then immediately added to the function table (either the global function table or that of the current class if declared inside a class method). Instead of a normal ZEND_DECLARE_FUNCTION opcode the new ZEND_DECLARE_LAMBDA_FUNC is used as an opcode at this point. The op_array of the new function is initialized with is_lambda = 1 and is_closure = 0.+When the compiler reaches a lambda function, except for details in the grammar, new function zend_do_begin_lambda_function_declaration is called - which itself calls zend_do_begin_function_declaration with "lambdaas a predefined function name. Immediately hereafter, the ZEND_DECLARE_FUNCTION opcode is replaced with new ZEND_DECLARE_LAMBDA_FUNCTION opcode, early binding will therefore never occur for lambda functions (and traditional function binding at runtime neither, since the ZEND_DECLARE_LAMBDA_FUNCTION opcode does something else, see below). The closure has an additional flag ZEND_ACC_CLOSURE.
  
-When parsing a 'lexical' declaration inside an anonymous function the parser saves the name of the variable that is to be imported in an array stored as a member of the op_array structure (lexical_names).+Lexical variables are done via static variables: For each lexical variable an entry in the static variables hash table is added. The entry is default NULL but an additional IS_LEXICAL or IS_LEXICAL_REF is XORed to the zval type.
  
-The opcode handler for ZEND_DECLARE_LAMBDA_FUNC does the following: First of all it creates a new op_array and copies the entire memory structure of the lambda function into it (the opcodes themselves are not copied since they are only referenced in the op_array structure). Then it sets is_closure = 1 on the new op_array, and for each lexical variable name that the compiler added to the original op_array it creates a reference to that variable from the current scope into a HashTable member in the new op_array. It also saves the current object pointer ($this) as a member of the op_array in order to allow for the closure to access $this. Finally it registers the new op_array as a resource and returns that resource.+An additional internal class "Closure" is added which will be used for saving closures.
  
-The opcode handler of the 'lexical' construct simply fetches the variable from that HashTable and imports it into local scope of the inner function (just like with 'global' only with different hash table).+The ZEND_DECLARE_LAMBDA_FUNCTION opcode looks up the function in the function table (it still has its runtime function key the compiler gave it and is thus cacheable by any opcode cache), creates a new object of the Closure type and stores copy of the op_array inside. It correctly sets the scope of the copied op_array to be the current class scope and makes sure all lexical variables are imported from the parent scope into the copied hash table of the new op_array. It also creates a reference to the current $this object. It returns the newly created object.
  
-Some hooks were added that allow the 'lambda functionresource to be called. Also, there are several checks in place that make sure the lambda function is not called directlyi.eif someone explicitely tries to use the internal function name instead of using the resource return value of the declaration.+Some hooks were added to the opcode handlers, zend_call_function and zend_is_callable_ex that allow the 'Closureobject to be called. 
 + 
 +In order to make code changes as clean as possiblethis logic was mainly abstracted into zend_closures.c which defines two main methods: zend_create_closure and zend_get_closurezend_create_closure creates a new closure, zend_get_closure retrieves the associated op_array, scope and this pointer from the closure. If some logic needs to be changed (due to design decisions or - for example - a bug), no binary incompatible change will take place but rather those two methods need to be changed. 
 + 
 +==== Tests ==== 
 + 
 +The patch contains additional phpt tests that make sure closures work as designed.
  
 ==== The patch ==== ==== The patch ====
  
-The patch for PHP 5.3 is available here:+**Note:** The patches were already applied to PHP_5_3 and HEAD (with some minor modifications and fixes).
  
-   * [[http://www.christian-seiler.de/temp/closures-php-5.3-2008-06-17-3.diff]] (Revised patch) +Current patches:
-   * [[http://www.christian-seiler.de/temp/closures-php-5.3-2008-06-16-1.diff]]+
  
-A patch for PHP (HEAD) will be added as soon as the unicode_semantics removal is complete.+   * [[http://www.christian-seiler.de/temp/closures-php-5.3-2008-07-01-1.diff|Patch against PHP_5_3]] 
 +   * [[http://www.christian-seiler.de/temp/closures-php-6.0-2008-07-01-1.diff|Patch against HEAD]] 
 + 
 +Older patches for completeness: 
 + 
 +   * [[http://www.christian-seiler.de/temp/closures-php-5.3-2008-06-26-use.diff]] 
 +   * [[http://www.christian-seiler.de/temp/closures-php-5.3-2008-06-26-use-this.diff]] 
 +   * [[http://www.christian-seiler.de/temp/closures-php-5.3-2008-06-26-lexical-this.diff]] 
 +   * [[http://www.christian-seiler.de/temp/closures-php-6.0-2008-06-26-use.diff]] 
 +   * [[http://www.christian-seiler.de/temp/closures-php-6.0-2008-06-26-use-this.diff]] 
 +   * [[http://www.christian-seiler.de/temp/closures-php-6.0-2008-06-26-lexical-this.diff]] 
 +   * [[http://www.christian-seiler.de/temp/closures-php-5.3-2008-06-17-3.diff]] 
 +   * [[http://www.christian-seiler.de/temp/closures-php-5.3-2008-06-16-1.diff]]
  
 **Note** The patch does not contain the diff for ''zend_language_scanner.c'' since that file can easily be regenerated from ''zend_language_scanner.l''. **Note** The patch does not contain the diff for ''zend_language_scanner.c'' since that file can easily be regenerated from ''zend_language_scanner.l''.
Line 187: Line 334:
 ==== BC breaks ==== ==== BC breaks ====
  
-   Introduction of a new keyword 'lexical'But note that it is very improbable that someone should use it as a function, method, class or property name. +   Creates an additional class named "Closure" that may break existing codeApparently classes by this name [[http://google.com/codesearch?hl=en&lr=&q=%22class+Closure%22+lang%3Aphp&sbtn=Search|are used to emulate closures]] in current PHP versions
-   **No** tests are broken by the patch.+   None otherwise (no new keywords)
  
 ==== Caveats / possible WTFs ==== ==== Caveats / possible WTFs ====
Line 194: Line 341:
 === Trailing '';'' === === Trailing '';'' ===
  
-On writing ''$func = function () { };'' there is a semicolon necessary. If left out it will produce a compile error. Since any attempt to remove that necessity would unecessarily bloat the grammar, I suggest we simply keep it the way it is. Also, Lukas Kahwe Smith pointed out that a single trailing semicolon after a closing brace already exists: ''do { } while ();''+On writing ''$func = function () { };'' there is a semicolon necessary. If left out it will produce a compile error. Since any attempt to remove that necessity would unnecessarily bloat the grammar, I suggest we simply keep it the way it is. Also, Lukas Kahwe Smith pointed out that a single trailing semicolon after a closing brace already exists: ''do { } while ();''
  
-=== References ===+=== Misinterpretations of the goal of closures ===
  
-The fact that 'lexical' creates references may cause certain WTFs:+As the discussion on the mailing list showed, there were quite a few misconceptions on what closures may or may not achieve. One often used suggestion was to use closures in order to extend classes by additional methods at run time. This is **not** the goal of closures **and** it can already be achieved without closures just by using _ _call, see for example [[http://phpfi.com/328105]].
  
- <code php> +===== Example code =====
-   for ($i 0; $i < 10; $i++) { +
-     $arr[$i] function () { lexical $i; return $i; }; +
-   } +
- </code>+
  
-This will not work as expected since $i is a reference and thus all created closures would reference the same variable. In order to get this right one has to do: +The example code in this document is available [[http://www.christian-seiler.de/temp/php-5.3-test-closures.txt|here]].
- +
- <code php> +
-   for ($i = 0; $i < 10; $i++) { +
-     $loopIndex = $i; +
-     $arr[$i] = function () { lexical $loopIndex; return $loopIndex; }; +
-     unset ($loopIndex); +
-   } +
- </code> +
- +
-This can be a WTF for people that don't expect lexical to create an actual referenceOn the other hand, global and static both DO create references so that behaviour is consistent with current PHP **and** (as pointed out on the mailing list) other languages such as JavaScript also behave the same way, so we really should stay consistent. +
- +
-=== ''lexical'' keyword itself === +
- +
-The fact that 'lexical' is needed at all may cause WTFsOther languages such as JavaScript implicitely have the entire scope visible to child functionsBut since PHP does the same thing with global variables, I find a keyword like 'lexical' much more consistent than importing the entire scope (and always importing the entire scope costs unnecessary performance).+
  
 ===== Changelog ==== ===== Changelog ====
  
 +   * 2008-08-11 Christian Seiler: Documented additional reflection improvements (see php-internals)
 +   * 2008-07-15 Christian Seiler: Updated status of this RFC
 +   * 2008-07-01 Christian Seiler: Updated patch yet again
 +   * 2008-06-26 Christian Seiler: Revised patch, using objects instead of resources, added tests
    * 2008-06-18 Christian Seiler: OOP clarifications    * 2008-06-18 Christian Seiler: OOP clarifications
    * 2008-06-17 Christian Seiler: Updated patch    * 2008-06-17 Christian Seiler: Updated patch
Line 229: Line 362:
    * 2008-06-16 Christian Seiler: Small changes    * 2008-06-16 Christian Seiler: Small changes
    * 2008-06-16 Christian Seiler: Initial creation    * 2008-06-16 Christian Seiler: Initial creation
- 
rfc/closures.txt · Last modified: 2017/09/22 13:28 by 127.0.0.1