Skip to content

Commit f3e5bbe

Browse files
nikicmorrisonlevibwoebi
committed
Implement arrow functions
Per RFC: https://wiki.php.net/rfc/arrow_functions_v2 Co-authored-by: Levi Morrison <[email protected]> Co-authored-by: Bob Weinand <[email protected]>
1 parent eaab0a2 commit f3e5bbe

27 files changed

+465
-44
lines changed

UPGRADING

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ PHP 7.4 UPGRADE NOTES
2424
- Core:
2525
. get_declared_classes() no longer returns anonymous classes that haven't
2626
been instantiated yet.
27+
. "fn" is now a reserved keyword. In particular it can no longer be used as a
28+
function or class name. It can still be used as a method or class constant
29+
name.
2730

2831
- Curl:
2932
. Attempting to serialize a CURLFile class will now generate an exception.
@@ -116,6 +119,14 @@ PHP 7.4 UPGRADE NOTES
116119
$user->name can only be assigned strings. For more information see the
117120
RFC: https://wiki.php.net/rfc/typed_properties_v2
118121

122+
. Added support for arrow functions with implicit by-value scope binding.
123+
For example:
124+
125+
$factor = 10;
126+
$nums = array_map(fn($num) => $num * $factor, $nums);
127+
128+
RFC: https://wiki.php.net/rfc/arrow_functions_v2
129+
119130
. Added support for coalesce assign (??=) operator. For example:
120131

121132
$array['key'] ??= computeDefault();

Zend/tests/arg_unpack/many_args.phpt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@ Argument unpacking with many arguments
33
--FILE--
44
<?php
55

6-
function fn(...$args) {
6+
function f(...$args) {
77
var_dump(count($args));
88
}
99

1010
$array = array_fill(0, 10000, 42);
11-
fn(...$array, ...$array);
11+
f(...$array, ...$array);
1212

1313
?>
1414
--EXPECT--

Zend/tests/arrow_functions/001.phpt

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
--TEST--
2+
Basic arrow function functionality check
3+
--FILE--
4+
<?php
5+
6+
$foo = fn() => 1;
7+
var_dump($foo());
8+
9+
$foo = fn($x) => $x;
10+
var_dump($foo(2));
11+
12+
$foo = fn($x, $y) => $x + $y;
13+
var_dump($foo(1, 2));
14+
15+
// Closing over $var
16+
$var = 4;
17+
$foo = fn() => $var;
18+
var_dump($foo());
19+
20+
// Not closing over $var, it's a parameter
21+
$foo = fn($var) => $var;
22+
var_dump($foo(5));
23+
24+
// Close over $var by-value, not by-reference
25+
$var = 5;
26+
$foo = fn() => ++$var;
27+
var_dump($foo());
28+
var_dump($var);
29+
30+
// Nested arrow functions closing over variable
31+
$var = 6;
32+
var_dump((fn() => fn() => $var)()());
33+
var_dump((fn() => function() use($var) { return $var; })()());
34+
35+
?>
36+
--EXPECT--
37+
int(1)
38+
int(2)
39+
int(3)
40+
int(4)
41+
int(5)
42+
int(6)
43+
int(5)
44+
int(6)
45+
int(6)

Zend/tests/arrow_functions/002.phpt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
--TEST--
2+
Arrow functions implicit use must be throwing notices only upon actual use
3+
--FILE--
4+
<?php
5+
6+
$b = 1;
7+
8+
var_dump((fn() => $b + $c)());
9+
10+
?>
11+
--EXPECTF--
12+
Notice: Undefined variable: c in %s on line %d
13+
int(1)

Zend/tests/arrow_functions/003.phpt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
--TEST--
2+
Variable-variables inside arrow functions
3+
--FILE--
4+
<?php
5+
6+
$a = 1;
7+
$var = "a";
8+
$fn = fn() => $$var;
9+
var_dump($fn());
10+
11+
${5} = 2;
12+
$fn = fn() => ${5};
13+
var_dump($fn());
14+
15+
?>
16+
--EXPECTF--
17+
Notice: Undefined variable: a in %s on line %d
18+
NULL
19+
20+
Notice: Undefined variable: 5 in %s on line %d
21+
NULL

Zend/tests/arrow_functions/004.phpt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
--TEST--
2+
Auto-globals in arrow functions
3+
--FILE--
4+
<?php
5+
6+
// This should work, but *not* generate a binding for $GLOBALS
7+
$a = 123;
8+
$fn = fn() => $GLOBALS['a'];
9+
var_dump($fn());
10+
11+
?>
12+
--EXPECT--
13+
int(123)

Zend/tests/arrow_functions/005.phpt

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
--TEST--
2+
Arrow function $this binding
3+
--FILE--
4+
<?php
5+
6+
class Test {
7+
public function method() {
8+
// It would be okay if this is NULL, but the rest should work
9+
$fn = fn() => 42;
10+
$r = new ReflectionFunction($fn);
11+
var_dump($r->getClosureThis());
12+
13+
$fn = fn() => $this;
14+
var_dump($fn());
15+
16+
$fn = fn() => Test::method2();
17+
$fn();
18+
19+
$fn = fn() => call_user_func('Test::method2');
20+
$fn();
21+
22+
$thisName = "this";
23+
$fn = fn() => $$thisName;
24+
var_dump($fn());
25+
26+
$fn = fn() => self::class;
27+
var_dump($fn());
28+
29+
// static can be used to unbind $this
30+
$fn = static fn() => isset($this);
31+
var_dump($fn());
32+
}
33+
34+
public function method2() {
35+
var_dump($this);
36+
}
37+
}
38+
39+
(new Test)->method();
40+
41+
?>
42+
--EXPECT--
43+
object(Test)#1 (0) {
44+
}
45+
object(Test)#1 (0) {
46+
}
47+
object(Test)#1 (0) {
48+
}
49+
object(Test)#1 (0) {
50+
}
51+
object(Test)#1 (0) {
52+
}
53+
string(4) "Test"
54+
bool(false)

Zend/tests/arrow_functions/006.phpt

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
--TEST--
2+
Arrow functions syntax variations
3+
--FILE--
4+
<?php
5+
6+
// By-reference argument and return
7+
$var = 1;
8+
$id = fn&(&$x) => $x;
9+
$ref =& $id($var);
10+
$ref++;
11+
var_dump($var);
12+
13+
// int argument and return type
14+
$var = 10;
15+
$int_fn = fn(int $x): int => $x;
16+
var_dump($int_fn($var));
17+
try {
18+
$int_fn("foo");
19+
} catch (TypeError $e) {
20+
echo $e->getMessage(), "\n";
21+
}
22+
23+
$varargs = fn(?int... $args): array => $args;
24+
var_dump($varargs(20, null, 30));
25+
try {
26+
$varargs(40, "foo");
27+
} catch (TypeError $e) {
28+
echo $e->getMessage(), "\n";
29+
}
30+
31+
?>
32+
--EXPECTF--
33+
int(2)
34+
int(10)
35+
Argument 1 passed to {closure}() must be of the type int, string given, called in %s on line %d
36+
array(3) {
37+
[0]=>
38+
int(20)
39+
[1]=>
40+
NULL
41+
[2]=>
42+
int(30)
43+
}
44+
Argument 2 passed to {closure}() must be of the type int or null, string given, called in %s on line %d

Zend/tests/arrow_functions/007.phpt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
--TEST--
2+
Pretty printing for arrow functions
3+
--FILE--
4+
<?php
5+
6+
// TODO We're missing parentheses for the direct call
7+
assert((fn() => false)());
8+
assert((fn&(int... $args): ?bool => $args[0])(false));
9+
10+
?>
11+
--EXPECTF--
12+
Warning: assert(): assert(fn() => false()) failed in %s on line %d
13+
14+
Warning: assert(): assert(fn&(int ...$args): ?bool => $args[0](false)) failed in %s on line %d

Zend/tests/arrow_functions/008.phpt

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
--TEST--
2+
Yield inside arrow functions
3+
--FILE--
4+
<?php
5+
6+
// This doesn't make terribly much sense, but it works...
7+
8+
$fn = fn() => yield 123;
9+
foreach ($fn() as $val) {
10+
var_dump($val);
11+
}
12+
13+
$fn = fn() => yield from [456, 789];
14+
foreach ($fn() as $val) {
15+
var_dump($val);
16+
}
17+
18+
$fn = fn() => fn() => yield 987;
19+
foreach ($fn()() as $val) {
20+
var_dump($val);
21+
}
22+
23+
?>
24+
--EXPECT--
25+
int(123)
26+
int(456)
27+
int(789)
28+
int(987)

0 commit comments

Comments
 (0)