Skip to content

Commit d5ef2f4

Browse files
committed
Added support for lambda functions and closures
1 parent d233423 commit d5ef2f4

35 files changed

+1126
-12
lines changed

NEWS

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ PHP NEWS
1717
- Changed mhash to be a wrapper layer around the hash extension. (Scott)
1818

1919
- Improved PHP syntax and semantics:
20+
. Added lambda functions and closures (Christian Seiler, Dmitry)
2021
. Added "jump label" operator (limited "goto"). (Dmitry, Sara)
2122
. Added NOWDOC syntax. (Gwynne Raskind, Stas, Dmitry)
2223
. Added HEREDOC syntax with double quotes. (Lars Strojny, Felipe)

Zend/Makefile.am

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ libZend_la_SOURCES=\
1717
zend_objects_API.c zend_ts_hash.c zend_stream.c \
1818
zend_default_classes.c \
1919
zend_iterators.c zend_interfaces.c zend_exceptions.c \
20-
zend_strtod.c
20+
zend_strtod.c zend_closures.c
2121

2222
libZend_la_LDFLAGS =
2323
libZend_la_LIBADD = @ZEND_EXTRA_LIBS@

Zend/Zend.dsp

+4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Zend/ZendTS.dsp

+4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Zend/tests/closure_001.phpt

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
--TEST--
2+
Closure 001: Lambda without lexical variables
3+
--FILE--
4+
<?php
5+
6+
$lambda1 = function () {
7+
echo "Hello World!\n";
8+
};
9+
10+
$lambda2 = function ($x) {
11+
echo "Hello $x!\n";
12+
};
13+
14+
var_dump(is_callable($lambda1));
15+
var_dump(is_callable($lambda2));
16+
$lambda1();
17+
$lambda2("Universe");
18+
call_user_func($lambda1);
19+
call_user_func($lambda2, "Universe");
20+
21+
echo "Done\n";
22+
?>
23+
--EXPECT--
24+
bool(true)
25+
bool(true)
26+
Hello World!
27+
Hello Universe!
28+
Hello World!
29+
Hello Universe!
30+
Done

Zend/tests/closure_002.phpt

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
--TEST--
2+
Closure 002: Lambda with lexical variables (global scope)
3+
--FILE--
4+
<?php
5+
6+
$x = 4;
7+
8+
$lambda1 = function () use ($x) {
9+
echo "$x\n";
10+
};
11+
12+
$lambda2 = function () use (&$x) {
13+
echo "$x\n";
14+
};
15+
16+
$lambda1();
17+
$lambda2();
18+
$x++;
19+
$lambda1();
20+
$lambda2();
21+
22+
echo "Done\n";
23+
?>
24+
--EXPECT--
25+
4
26+
4
27+
4
28+
5
29+
Done

Zend/tests/closure_003.phpt

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
--TEST--
2+
Closure 003: Lambda with lexical variables (local scope)
3+
--FILE--
4+
<?php
5+
6+
function run () {
7+
$x = 4;
8+
9+
$lambda1 = function () use ($x) {
10+
echo "$x\n";
11+
};
12+
13+
$lambda2 = function () use (&$x) {
14+
echo "$x\n";
15+
};
16+
17+
$lambda1();
18+
$lambda2();
19+
$x++;
20+
$lambda1();
21+
$lambda2();
22+
}
23+
24+
run();
25+
26+
echo "Done\n";
27+
?>
28+
--EXPECT--
29+
4
30+
4
31+
4
32+
5
33+
Done

Zend/tests/closure_004.phpt

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
--TEST--
2+
Closure 004: Lambda with lexical variables (scope lifetime)
3+
--FILE--
4+
<?php
5+
6+
function run () {
7+
$x = 4;
8+
9+
$lambda1 = function () use ($x) {
10+
echo "$x\n";
11+
};
12+
13+
$lambda2 = function () use (&$x) {
14+
echo "$x\n";
15+
$x++;
16+
};
17+
18+
return array($lambda1, $lambda2);
19+
}
20+
21+
list ($lambda1, $lambda2) = run();
22+
23+
$lambda1();
24+
$lambda2();
25+
$lambda1();
26+
$lambda2();
27+
28+
echo "Done\n";
29+
?>
30+
--EXPECT--
31+
4
32+
4
33+
4
34+
5
35+
Done

Zend/tests/closure_005.phpt

+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
--TEST--
2+
Closure 005: Lambda inside class, lifetime of $this
3+
--FILE--
4+
<?php
5+
6+
class A {
7+
private $x;
8+
9+
function __construct($x) {
10+
$this->x = $x;
11+
}
12+
13+
function __destruct() {
14+
echo "Destroyed\n";
15+
}
16+
17+
function getIncer($val) {
18+
return function() use ($val) {
19+
$this->x += $val;
20+
};
21+
}
22+
23+
function getPrinter() {
24+
return function() {
25+
echo $this->x."\n";
26+
};
27+
}
28+
29+
function getError() {
30+
return static function() {
31+
echo $this->x."\n";
32+
};
33+
}
34+
35+
function printX() {
36+
echo $this->x."\n";
37+
}
38+
}
39+
40+
$a = new A(3);
41+
$incer = $a->getIncer(2);
42+
$printer = $a->getPrinter();
43+
$error = $a->getError();
44+
45+
$a->printX();
46+
$printer();
47+
$incer();
48+
$a->printX();
49+
$printer();
50+
51+
unset($a);
52+
53+
$incer();
54+
$printer();
55+
56+
unset($incer);
57+
$printer();
58+
59+
unset($printer);
60+
61+
$error();
62+
63+
echo "Done\n";
64+
?>
65+
--EXPECTF--
66+
3
67+
3
68+
5
69+
5
70+
7
71+
7
72+
Destroyed
73+
74+
Fatal error: Using $this when not in object context in %sclosure_005.php on line 28

Zend/tests/closure_006.phpt

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
--TEST--
2+
Closure 006: Nested lambdas
3+
--FILE--
4+
<?php
5+
6+
$getClosure = function ($v) {
7+
return function () use ($v) {
8+
echo "Hello World: $v!\n";
9+
};
10+
};
11+
12+
$closure = $getClosure (2);
13+
$closure ();
14+
15+
echo "Done\n";
16+
?>
17+
--EXPECT--
18+
Hello World: 2!
19+
Done

Zend/tests/closure_007.phpt

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
--TEST--
2+
Closure 007: Nested lambdas in classes
3+
--FILE--
4+
<?php
5+
6+
class A {
7+
private $x = 0;
8+
9+
function getClosureGetter () {
10+
return function () {
11+
return function () {
12+
$this->x++;
13+
};
14+
};
15+
}
16+
17+
function printX () {
18+
echo $this->x."\n";
19+
}
20+
}
21+
22+
$a = new A;
23+
$a->printX();
24+
$getClosure = $a->getClosureGetter();
25+
$a->printX();
26+
$closure = $getClosure();
27+
$a->printX();
28+
$closure();
29+
$a->printX();
30+
31+
echo "Done\n";
32+
?>
33+
--EXPECT--
34+
0
35+
0
36+
0
37+
1
38+
Done

Zend/tests/closure_008.phpt

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
--TEST--
2+
Closure 008: Use in preg_replace()
3+
--FILE--
4+
<?php
5+
6+
function replace_spaces($text) {
7+
$lambda = function ($matches) {
8+
return str_replace(' ', '&nbsp;', $matches[1]).' ';
9+
};
10+
return preg_replace_callback('/( +) /', $lambda, $text);
11+
}
12+
13+
echo replace_spaces("1 2 3\n");
14+
echo replace_spaces("1 2 3\n");
15+
echo replace_spaces("1 2 3\n");
16+
echo "Done\n";
17+
?>
18+
--EXPECT--
19+
1 2 3
20+
1&nbsp; 2&nbsp; 3
21+
1&nbsp;&nbsp; 2&nbsp;&nbsp; 3
22+
Done

Zend/tests/closure_009.phpt

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
--TEST--
2+
Closure 009: Use in preg_replace()
3+
--FILE--
4+
<?php
5+
$a = 1;
6+
$x = function ($x) use ($a) {
7+
static $n = 0;
8+
$n++;
9+
$a = $n.':'.$a;
10+
echo $x.':'.$a."\n";
11+
};
12+
$y = function ($x) use (&$a) {
13+
static $n = 0;
14+
$n++;
15+
$a = $n.':'.$a;
16+
echo $x.':'.$a."\n";
17+
};
18+
$x(1);
19+
$x(2);
20+
$x(3);
21+
$y(4);
22+
$y(5);
23+
$y(6);
24+
?>
25+
--EXPECT--
26+
1:1:1
27+
2:2:1
28+
3:3:1
29+
4:1:1
30+
5:2:1:1
31+
6:3:2:1:1

Zend/tests/closure_010.phpt

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
--TEST--
2+
Closure 010: Closure calls itself
3+
--FILE--
4+
<?php
5+
$i = 3;
6+
$lambda = function ($lambda) use (&$i) {
7+
if ($i==0) return;
8+
echo $i--."\n";
9+
$lambda($lambda);
10+
};
11+
$lambda($lambda);
12+
echo "$i\n";
13+
?>
14+
--EXPECT--
15+
3
16+
2
17+
1
18+
0

Zend/tests/closure_011.phpt

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
--TEST--
2+
Closure 011: Lexical copies not static in closure
3+
--FILE--
4+
<?php
5+
$i = 1;
6+
$lambda = function () use ($i) {
7+
return ++$i;
8+
};
9+
$lambda();
10+
echo $lambda()."\n";
11+
//early prototypes gave 3 here because $i was static in $lambda
12+
?>
13+
--EXPECT--
14+
2

0 commit comments

Comments
 (0)