rfc:closures
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 [2008/07/03 09:47] – Typos rquadling | rfc:closures [2009/12/15 22:41] – Fix list numbering rquadling | ||
---|---|---|---|
Line 3: | Line 3: | ||
* Date: 2008-07-01 | * Date: 2008-07-01 | ||
* Author: Christian Seiler < | * Author: Christian Seiler < | ||
- | * Status: | + | * Status: |
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 16: | Line 16: | ||
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. | + | Lambda functions allow the quick definition of throw-away functions that are not used elsewhere. |
- | + | - 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:< |
- | + | ||
- | - 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> | + | |
| | ||
if (!function_exists (' | if (!function_exists (' | ||
Line 35: | Line 30: | ||
} | } | ||
</ | </ | ||
- | + | - Use the present create_function() in order to create a function at runtime. This approach has several disadvantages: | |
- | - Use the present create_function() in order to create a function at runtime. This approach has several disadvantages: | + | |
==== Closures ==== | ==== Closures ==== | ||
- | Closures provide a very useful tool in order to make lambda functions even more useful. Just imagine you want to replace ' | + | Closures provide a very useful tool in order to make lambda functions even more useful. Just imagine you want to replace ' |
- | + | - 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 | + | - 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 |
- | + | | |
- | | + | |
===== 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 72: | Line 58: | ||
</ | </ | ||
- | The & is optional and indicates that the function should return a refernce. The use followed by the paranthesis | + | The & is optional and indicates that the function should return a reference. The use followed by the parentheses |
Example usage: | Example usage: | ||
Line 97: | Line 83: | ||
| | ||
} | } | ||
+ | </ | ||
+ | |||
+ | You can even put the lambda function inline, for example: | ||
+ | |||
+ | <code php> | ||
+ | function replace_spaces ($text) { | ||
+ | return preg_replace_callback ('/( +) /', | ||
+ | function ($matches) { | ||
+ | return str_replace ($matches[1], | ||
+ | }, $text); | ||
+ | } | ||
</ | </ | ||
Line 120: | Line 117: | ||
} | } | ||
}; | }; | ||
- | | + | |
} | } | ||
</ | </ | ||
Line 139: | Line 136: | ||
</ | </ | ||
- | === Refernces | + | === References |
- | 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, | + | 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, |
Example: | Example: | ||
Line 163: | Line 160: | ||
=== 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 import it explicitely) and all private and protected methods of that class. This also applies to nested closures. Example: | + | $this support has been removed, see [[rfc/ |
+ | |||
+ | 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> | <code php> | ||
Line 200: | Line 199: | ||
$x = 4; | $x = 4; | ||
| | ||
- | | + | |
}; | }; | ||
| | ||
Line 208: | Line 207: | ||
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. | 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. | ||
+ | |||
==== Additional goody: _ _invoke ==== | ==== Additional goody: _ _invoke ==== | ||
Line 223: | Line 223: | ||
</ | </ | ||
- | ==== Interaction with reflection ==== | + | ==== Interaction with reflection |
Since closures are anonymous, they do **not** appear in reflection. | Since closures are anonymous, they do **not** appear in reflection. | ||
Line 256: | Line 256: | ||
$object-> | $object-> | ||
$closure (); | $closure (); | ||
+ | </ | ||
+ | |||
+ | ==== 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, ' | ||
+ | Reflection:: | ||
+ | </ | ||
+ | |||
+ | This will yield: | ||
+ | |||
+ | < | ||
+ | Method [ < | ||
+ | |||
+ | - Parameters [3] { | ||
+ | Parameter #0 [ < | ||
+ | Parameter #1 [ < | ||
+ | Parameter #2 [ < | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | 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, ' | ||
+ | $p = new ReflectionParameter (array ($closure, ' | ||
</ | </ | ||
Line 279: | Line 311: | ||
==== The patch ==== | ==== The patch ==== | ||
+ | |||
+ | **Note:** The patches were already applied to PHP_5_3 and HEAD (with some minor modifications and fixes). | ||
Current patches: | Current patches: | ||
Line 300: | Line 334: | ||
==== BC breaks ==== | ==== BC breaks ==== | ||
- | | + | |
+ | * None otherwise (no new keywords) | ||
==== Caveats / possible WTFs ==== | ==== Caveats / possible WTFs ==== | ||
Line 306: | Line 341: | ||
=== Trailing '';'' | === Trailing '';'' | ||
- | On writing '' | + | On writing '' |
=== Misinterpretations of the goal of closures === | === Misinterpretations of the goal of closures === | ||
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:// | 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:// | ||
+ | |||
+ | ===== Example code ===== | ||
+ | |||
+ | The example code in this document is available [[http:// | ||
===== 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-07-01 Christian Seiler: Updated patch yet again | ||
* 2008-06-26 Christian Seiler: Revised patch, using objects instead of resources, added tests | * 2008-06-26 Christian Seiler: Revised patch, using objects instead of resources, added tests | ||
Line 321: | 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