Skip to content

Auto-capturing multi-statement closures #6246

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 58 additions & 10 deletions Zend/tests/arrow_functions/001.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -3,43 +3,91 @@ Basic arrow function functionality check
--FILE--
<?php

// No return value
$foo = fn() {};
var_dump($foo());

// Return value
$foo = fn() => 1;
var_dump($foo());

$foo = fn() { return 2; };
var_dump($foo());

$foo = fn($x) => $x;
var_dump($foo(2));
var_dump($foo(3));

$foo = fn($x) { return $x; };
var_dump($foo(4));

$foo = fn($x, $y) => $x + $y;
var_dump($foo(1, 2));
var_dump($foo(4, 1));

$foo = fn($x, $y) { return $x + $y; };
var_dump($foo(5, 1));

// Closing over $var
$var = 4;
$var = 7;
$foo = fn() => $var;
var_dump($foo());

$var = 8;
$foo = fn() { return $var; };
var_dump($foo());

// Not closing over $var, it's a parameter
$foo = fn($var) => $var;
var_dump($foo(5));
var_dump($foo(9));

$foo = fn($var) { return $var; };
var_dump($foo(10));

// Close over $var by-value, not by-reference
$var = 5;
$var = 11;
$foo = fn() => ++$var;
var_dump($var);
var_dump($foo());
var_dump($var);

// Nested arrow functions closing over variable
$var = 6;
$var = 13;
$foo = fn() { return ++$var; };
var_dump($var);
var_dump($foo());
var_dump($var);

// Nested arrow functions
$var = 14;
var_dump((fn() => fn() => $var)()());

$var = 15;
var_dump((fn() => function() use($var) { return $var; })()());

$var = 16;
var_dump((fn() { return fn() { return $var; }; })()());

// Nested arrow functions with null return value
var_dump((fn() { return fn() {}; })()());

?>
--EXPECT--
NULL
int(1)
int(2)
int(3)
int(4)
int(5)
int(6)
int(5)
int(6)
int(6)
int(7)
int(8)
int(9)
int(10)
int(11)
int(12)
int(11)
int(13)
int(14)
int(13)
int(14)
int(15)
int(16)
NULL
6 changes: 6 additions & 0 deletions Zend/tests/arrow_functions/002.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,13 @@ $b = 1;

var_dump((fn() => $b + $c)());

$d = 2;
var_dump((fn() { return $d + $e; } )());

?>
--EXPECTF--
Warning: Undefined variable $c in %s on line %d
int(1)

Warning: Undefined variable $e in %s on line %d
int(2)
17 changes: 16 additions & 1 deletion Zend/tests/arrow_functions/003.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,29 @@ $var = "a";
$fn = fn() => $$var;
var_dump($fn());

${5} = 2;
$b = 2;
$var = "b";
$fn = fn() { return $$var; };
var_dump($fn());

${5} = 3;
$fn = fn() => ${5};
var_dump($fn());

${6} = 4;
$fn = fn() { return ${6}; };
var_dump($fn());

?>
--EXPECTF--
Warning: Undefined variable $a in %s on line %d
NULL

Warning: Undefined variable $b in %s on line %d
NULL

Warning: Undefined variable $5 in %s on line %d
NULL

Warning: Undefined variable $6 in %s on line %d
NULL
5 changes: 5 additions & 0 deletions Zend/tests/arrow_functions/004.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ $a = 123;
$fn = fn() => $GLOBALS['a'];
var_dump($fn());

$a = 456;
$fn = fn() { return $GLOBALS['a']; };
var_dump($fn());

?>
--EXPECT--
int(123)
int(456)
34 changes: 34 additions & 0 deletions Zend/tests/arrow_functions/005.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,47 @@ class Test {
$r = new ReflectionFunction($fn);
var_dump($r->getClosureThis());

$fn = fn() {};
$r = new ReflectionFunction($fn);
var_dump($r->getClosureThis());

$fn = fn() => $this;
var_dump($fn());

$fn = fn() { return $this; };
var_dump($fn());

$fn = fn() => Test::method2();
$fn();

$fn = fn() { return Test::method2(); };
$fn();

$fn = fn() => call_user_func('Test::method2');
$fn();

$fn = fn() { return call_user_func('Test::method2'); };
$fn();

$thisName = "this";
$fn = fn() => $$thisName;
var_dump($fn());

$fn = fn() { return $$thisName; };
var_dump($fn());

$fn = fn() => self::class;
var_dump($fn());

$fn = fn() { return self::class; };
var_dump($fn());

// static can be used to unbind $this
$fn = static fn() => isset($this);
var_dump($fn());

$fn = static fn() { return isset($this); };
var_dump($fn());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add one more static test and check with reflection that $this is really not bound

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mvorisek Gonna wait for the RFC decision before updating that.

}

public function method2() {
Expand All @@ -50,5 +72,17 @@ object(Test)#1 (0) {
}
object(Test)#1 (0) {
}
object(Test)#1 (0) {
}
object(Test)#1 (0) {
}
object(Test)#1 (0) {
}
object(Test)#1 (0) {
}
object(Test)#1 (0) {
}
string(4) "Test"
string(4) "Test"
bool(false)
bool(false)
34 changes: 34 additions & 0 deletions Zend/tests/arrow_functions/006.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ $ref =& $id($var);
$ref++;
var_dump($var);

$id = fn&(&$x) { return $x; };
$ref =& $id($var);
$ref++;
var_dump($var);

// int argument and return type
$var = 10;
$int_fn = fn(int $x): int => $x;
Expand All @@ -20,6 +25,15 @@ try {
echo $e->getMessage(), "\n";
}

$var = 11;
$int_fn = fn(int $x): int { return $x; };
var_dump($int_fn($var));
try {
$int_fn("foo");
} catch (TypeError $e) {
echo $e->getMessage(), "\n";
}

$varargs = fn(?int... $args): array => $args;
var_dump($varargs(20, null, 30));
try {
Expand All @@ -28,11 +42,22 @@ try {
echo $e->getMessage(), "\n";
}

$varargs = fn(?int... $args): array { return $args; };
var_dump($varargs(40, null, 50));
try {
$varargs(60, "foo");
} catch (TypeError $e) {
echo $e->getMessage(), "\n";
}

?>
--EXPECTF--
int(2)
int(3)
int(10)
{closure}(): Argument #1 ($x) must be of type int, string given, called in %s on line %d
int(11)
{closure}(): Argument #1 ($x) must be of type int, string given, called in %s on line %d
array(3) {
[0]=>
int(20)
Expand All @@ -42,3 +67,12 @@ array(3) {
int(30)
}
{closure}(): Argument #2 must be of type ?int, string given, called in %s on line %d
array(3) {
[0]=>
int(40)
[1]=>
NULL
[2]=>
int(50)
}
{closure}(): Argument #2 must be of type ?int, string given, called in %s on line %d
13 changes: 13 additions & 0 deletions Zend/tests/arrow_functions/007.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,23 @@ assert.exception=0

// TODO We're missing parentheses for the direct call
assert((fn() => false)());
assert((fn() { return false; })());

assert((fn&(int... $args): ?bool => $args[0])(false));
assert((fn&(int... $args): ?bool { return $args[0]; })(false));

?>
--EXPECTF--
Warning: assert(): assert(fn() => false()) failed in %s on line %d

Warning: assert(): assert(fn() {
return false;
}
()) failed in %s on line %d

Warning: assert(): assert(fn&(int ...$args): ?bool => $args[0](false)) failed in %s on line %d

Warning: assert(): assert(fn&(int ...$args): ?bool {
return $args[0];
}
(false)) failed in %s on line %d
33 changes: 26 additions & 7 deletions Zend/tests/arrow_functions/008.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,43 @@ Yield inside arrow functions

// This doesn't make terribly much sense, but it works...

$fn = fn() => yield 123;
$fn = fn() => yield 1;
foreach ($fn() as $val) {
var_dump($val);
}

$fn = fn() => yield from [456, 789];
$fn = fn() { return yield 2; };
foreach ($fn() as $val) {
var_dump($val);
}

$fn = fn() => fn() => yield 987;
$fn = fn() => yield from [3, 4];
foreach ($fn() as $val) {
var_dump($val);
}

$fn = fn() { yield from [5, 6]; };
foreach ($fn() as $val) {
var_dump($val);
}

$fn = fn() => fn() => yield 7;
foreach ($fn()() as $val) {
var_dump($val);
}

$fn = fn() => fn() { yield 8; };
foreach ($fn()() as $val) {
var_dump($val);
}

?>
--EXPECT--
int(123)
int(456)
int(789)
int(987)
int(1)
int(2)
int(3)
int(4)
int(5)
int(6)
int(7)
int(8)
Loading