Permalink
Browse files

Added support for lambda functions and closures

  • Loading branch information...
1 parent d233423 commit d5ef2f466cb112fd977a71419fa4b67d0aa0a2ac Dmitry Stogov committed Jul 14, 2008
View
1 NEWS
@@ -17,6 +17,7 @@ PHP NEWS
- Changed mhash to be a wrapper layer around the hash extension. (Scott)
- Improved PHP syntax and semantics:
+ . Added lambda functions and closures (Christian Seiler, Dmitry)
. Added "jump label" operator (limited "goto"). (Dmitry, Sara)
. Added NOWDOC syntax. (Gwynne Raskind, Stas, Dmitry)
. Added HEREDOC syntax with double quotes. (Lars Strojny, Felipe)
View
@@ -17,7 +17,7 @@ libZend_la_SOURCES=\
zend_objects_API.c zend_ts_hash.c zend_stream.c \
zend_default_classes.c \
zend_iterators.c zend_interfaces.c zend_exceptions.c \
- zend_strtod.c
+ zend_strtod.c zend_closures.c
libZend_la_LDFLAGS =
libZend_la_LIBADD = @ZEND_EXTRA_LIBS@
View
@@ -123,6 +123,10 @@ SOURCE=.\zend_builtin_functions.c
# End Source File
# Begin Source File
+SOURCE=.\zend_closures.c
+# End Source File
+# Begin Source File
+
SOURCE=.\zend_compile.c
# End Source File
# Begin Source File
View
@@ -148,6 +148,10 @@ SOURCE=.\zend_builtin_functions.c
# End Source File
# Begin Source File
+SOURCE=.\zend_closures.c
+# End Source File
+# Begin Source File
+
SOURCE=.\zend_compile.c
# End Source File
# Begin Source File
@@ -0,0 +1,30 @@
+--TEST--
+Closure 001: Lambda without lexical variables
+--FILE--
+<?php
+
+$lambda1 = function () {
+ echo "Hello World!\n";
+};
+
+$lambda2 = function ($x) {
+ echo "Hello $x!\n";
+};
+
+var_dump(is_callable($lambda1));
+var_dump(is_callable($lambda2));
+$lambda1();
+$lambda2("Universe");
+call_user_func($lambda1);
+call_user_func($lambda2, "Universe");
+
+echo "Done\n";
+?>
+--EXPECT--
+bool(true)
+bool(true)
+Hello World!
+Hello Universe!
+Hello World!
+Hello Universe!
+Done
@@ -0,0 +1,29 @@
+--TEST--
+Closure 002: Lambda with lexical variables (global scope)
+--FILE--
+<?php
+
+$x = 4;
+
+$lambda1 = function () use ($x) {
+ echo "$x\n";
+};
+
+$lambda2 = function () use (&$x) {
+ echo "$x\n";
+};
+
+$lambda1();
+$lambda2();
+$x++;
+$lambda1();
+$lambda2();
+
+echo "Done\n";
+?>
+--EXPECT--
+4
+4
+4
+5
+Done
@@ -0,0 +1,33 @@
+--TEST--
+Closure 003: Lambda with lexical variables (local scope)
+--FILE--
+<?php
+
+function run () {
+ $x = 4;
+
+ $lambda1 = function () use ($x) {
+ echo "$x\n";
+ };
+
+ $lambda2 = function () use (&$x) {
+ echo "$x\n";
+ };
+
+ $lambda1();
+ $lambda2();
+ $x++;
+ $lambda1();
+ $lambda2();
+}
+
+run();
+
+echo "Done\n";
+?>
+--EXPECT--
+4
+4
+4
+5
+Done
@@ -0,0 +1,35 @@
+--TEST--
+Closure 004: Lambda with lexical variables (scope lifetime)
+--FILE--
+<?php
+
+function run () {
+ $x = 4;
+
+ $lambda1 = function () use ($x) {
+ echo "$x\n";
+ };
+
+ $lambda2 = function () use (&$x) {
+ echo "$x\n";
+ $x++;
+ };
+
+ return array($lambda1, $lambda2);
+}
+
+list ($lambda1, $lambda2) = run();
+
+$lambda1();
+$lambda2();
+$lambda1();
+$lambda2();
+
+echo "Done\n";
+?>
+--EXPECT--
+4
+4
+4
+5
+Done
@@ -0,0 +1,74 @@
+--TEST--
+Closure 005: Lambda inside class, lifetime of $this
+--FILE--
+<?php
+
+class A {
+ private $x;
+
+ function __construct($x) {
+ $this->x = $x;
+ }
+
+ function __destruct() {
+ echo "Destroyed\n";
+ }
+
+ function getIncer($val) {
+ return function() use ($val) {
+ $this->x += $val;
+ };
+ }
+
+ function getPrinter() {
+ return function() {
+ echo $this->x."\n";
+ };
+ }
+
+ function getError() {
+ return static function() {
+ echo $this->x."\n";
+ };
+ }
+
+ function printX() {
+ echo $this->x."\n";
+ }
+}
+
+$a = new A(3);
+$incer = $a->getIncer(2);
+$printer = $a->getPrinter();
+$error = $a->getError();
+
+$a->printX();
+$printer();
+$incer();
+$a->printX();
+$printer();
+
+unset($a);
+
+$incer();
+$printer();
+
+unset($incer);
+$printer();
+
+unset($printer);
+
+$error();
+
+echo "Done\n";
+?>
+--EXPECTF--
+3
+3
+5
+5
+7
+7
+Destroyed
+
+Fatal error: Using $this when not in object context in %sclosure_005.php on line 28
@@ -0,0 +1,19 @@
+--TEST--
+Closure 006: Nested lambdas
+--FILE--
+<?php
+
+$getClosure = function ($v) {
+ return function () use ($v) {
+ echo "Hello World: $v!\n";
+ };
+};
+
+$closure = $getClosure (2);
+$closure ();
+
+echo "Done\n";
+?>
+--EXPECT--
+Hello World: 2!
+Done
@@ -0,0 +1,38 @@
+--TEST--
+Closure 007: Nested lambdas in classes
+--FILE--
+<?php
+
+class A {
+ private $x = 0;
+
+ function getClosureGetter () {
+ return function () {
+ return function () {
+ $this->x++;
+ };
+ };
+ }
+
+ function printX () {
+ echo $this->x."\n";
+ }
+}
+
+$a = new A;
+$a->printX();
+$getClosure = $a->getClosureGetter();
+$a->printX();
+$closure = $getClosure();
+$a->printX();
+$closure();
+$a->printX();
+
+echo "Done\n";
+?>
+--EXPECT--
+0
+0
+0
+1
+Done
@@ -0,0 +1,22 @@
+--TEST--
+Closure 008: Use in preg_replace()
+--FILE--
+<?php
+
+function replace_spaces($text) {
+ $lambda = function ($matches) {
+ return str_replace(' ', '&nbsp;', $matches[1]).' ';
+ };
+ return preg_replace_callback('/( +) /', $lambda, $text);
+}
+
+echo replace_spaces("1 2 3\n");
+echo replace_spaces("1 2 3\n");
+echo replace_spaces("1 2 3\n");
+echo "Done\n";
+?>
+--EXPECT--
+1 2 3
+1&nbsp; 2&nbsp; 3
+1&nbsp;&nbsp; 2&nbsp;&nbsp; 3
+Done
@@ -0,0 +1,31 @@
+--TEST--
+Closure 009: Use in preg_replace()
+--FILE--
+<?php
+$a = 1;
+$x = function ($x) use ($a) {
+ static $n = 0;
+ $n++;
+ $a = $n.':'.$a;
+ echo $x.':'.$a."\n";
+};
+$y = function ($x) use (&$a) {
+ static $n = 0;
+ $n++;
+ $a = $n.':'.$a;
+ echo $x.':'.$a."\n";
+};
+$x(1);
+$x(2);
+$x(3);
+$y(4);
+$y(5);
+$y(6);
+?>
+--EXPECT--
+1:1:1
+2:2:1
+3:3:1
+4:1:1
+5:2:1:1
+6:3:2:1:1
@@ -0,0 +1,18 @@
+--TEST--
+Closure 010: Closure calls itself
+--FILE--
+<?php
+$i = 3;
+$lambda = function ($lambda) use (&$i) {
+ if ($i==0) return;
+ echo $i--."\n";
+ $lambda($lambda);
+};
+$lambda($lambda);
+echo "$i\n";
+?>
+--EXPECT--
+3
+2
+1
+0
@@ -0,0 +1,14 @@
+--TEST--
+Closure 011: Lexical copies not static in closure
+--FILE--
+<?php
+$i = 1;
+$lambda = function () use ($i) {
+ return ++$i;
+};
+$lambda();
+echo $lambda()."\n";
+//early prototypes gave 3 here because $i was static in $lambda
+?>
+--EXPECT--
+2
Oops, something went wrong.

0 comments on commit d5ef2f4

Please sign in to comment.