Skip to content

[RFC] Implement Property Capture for Anonymous Classes #11123

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

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 83 additions & 0 deletions Zend/tests/property_capture/basic.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
--TEST--
Property capture, with renaming and types
--FILE--
<?php
$a = $b = $c = $d = $e = $f = 42;
$g = $h = new stdClass;

$foo = new class use (
$a,
$b as $b_renamed,
$c as int,
$d as int $d_renamed,
$e as string|int,
$f as string|int $f_renamed,
$g as stdClass,
$h as stdClass $h_renamed,
) {};

var_dump($foo);

echo (new ReflectionClass($foo));
?>
--EXPECTF--
object(class@anonymous)#%d (8) {
["a"]=>
int(42)
["b_renamed"]=>
int(42)
["c"]=>
int(42)
["d_renamed"]=>
int(42)
["e"]=>
int(42)
["f_renamed"]=>
int(42)
["g"]=>
object(stdClass)#%d (0) {
}
["h_renamed"]=>
object(stdClass)#%d (0) {
}
}
Class [ <user> class class@anonymous ] {
@@ %s 5-14

- Constants [0] {
}

- Static properties [0] {
}

- Static methods [0] {
}

- Properties [8] {
Property [ public $a = NULL ]
Property [ public $b_renamed = NULL ]
Property [ public int $c ]
Property [ public int $d_renamed ]
Property [ public string|int $e ]
Property [ public string|int $f_renamed ]
Property [ public stdClass $g ]
Property [ public stdClass $h_renamed ]
}

- Methods [1] {
Method [ <user, ctor> public method __construct ] {
@@ %s 6 - 13

- Parameters [8] {
Parameter #0 [ <required> $a ]
Parameter #1 [ <required> $b_renamed ]
Parameter #2 [ <required> int $c ]
Parameter #3 [ <required> int $d_renamed ]
Parameter #4 [ <required> string|int $e ]
Parameter #5 [ <required> string|int $f_renamed ]
Parameter #6 [ <required> stdClass $g ]
Parameter #7 [ <required> stdClass $h_renamed ]
}
}
}
}
60 changes: 60 additions & 0 deletions Zend/tests/property_capture/by_ref.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
--TEST--
Capturing properties by reference
--FILE--
<?php
$a = $b = $c = $d = $e = $f = $g = $h = 42;

$foo = new class use (
&$a,
&$b as $b_name,
&$c as private,
&$d as private $d_name,
&$e as int,
&$f as int $f_name,
&$g as private int,
&$h as private int $h_name,
) {};

var_dump($foo);

$a = 1; $b = 2; $c = 3; $d = 4; $e = 5; $f = 6; $g = 7; $h = 8;

var_dump($foo);
?>
--EXPECTF--
object(class@anonymous)#1 (8) {
["a"]=>
&int(42)
["b_name"]=>
&int(42)
["c":"class@anonymous":private]=>
&int(42)
["d_name":"class@anonymous":private]=>
&int(42)
["e"]=>
&int(42)
["f_name"]=>
&int(42)
["g":"class@anonymous":private]=>
&int(42)
["h_name":"class@anonymous":private]=>
&int(42)
}
object(class@anonymous)#1 (8) {
["a"]=>
&int(1)
["b_name"]=>
&int(2)
["c":"class@anonymous":private]=>
&int(3)
["d_name":"class@anonymous":private]=>
&int(4)
["e"]=>
&int(5)
["f_name"]=>
&int(6)
["g":"class@anonymous":private]=>
&int(7)
["h_name":"class@anonymous":private]=>
&int(8)
}
15 changes: 15 additions & 0 deletions Zend/tests/property_capture/duplicate_argument_renamed.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
--TEST--
Can capture the same variable twice if renamed
--FILE--
<?php
$a = 42;
$foo = new class use ($a as $prop1, $a as $prop2) {};
var_dump($foo);
?>
--EXPECT--
object(class@anonymous)#1 (2) {
["prop1"]=>
int(42)
["prop2"]=>
int(42)
}
11 changes: 11 additions & 0 deletions Zend/tests/property_capture/duplicate_constructor.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
--TEST--
Cannot use both property capture and custom constructor
--FILE--
<?php
$a = 42;
$foo = new class use ($a) {
public function __construct() {}
}
?>
--EXPECTF--
Fatal error: Cannot declare custom constructor for anonymous class with captured properties in %s on line %d
10 changes: 10 additions & 0 deletions Zend/tests/property_capture/duplicate_constructor_args.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
--TEST--
Cannot use property capture and explicitly pass parameters to constructor
--FILE--
<?php
$a = 42;
$foo = new class(101) use ($a) {
};
?>
--EXPECTF--
Fatal error: Cannot pass constructor arguments to anonymous class with captured properties in %s on line %d
11 changes: 11 additions & 0 deletions Zend/tests/property_capture/duplicate_property_in_body.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
--TEST--
Error if captured property has same name as declared property
--FILE--
<?php
$a = 42;
$foo = new class use ($a) {
private $a;
};
?>
--EXPECTF--
Fatal error: Captured property $a conflicts with existing property in %s on line %d
9 changes: 9 additions & 0 deletions Zend/tests/property_capture/duplicate_property_in_list.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
--TEST--
Cannot capture the same variable twice without renaming
--FILE--
<?php
$a = 42;
$foo = new class use ($a, $a) {};
?>
--EXPECTF--
Fatal error: Redefinition of captured property $a in %s on line %d
10 changes: 10 additions & 0 deletions Zend/tests/property_capture/duplicate_property_renaming.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
--TEST--
Cannot rename two variables to the same captured property name
--FILE--
<?php
$a = 42;
$b = -1;
$foo = new class use ($a as $myProp, $b as $myProp) {};
?>
--EXPECTF--
Fatal error: Redefinition of captured property $myProp in %s on line %d
40 changes: 40 additions & 0 deletions Zend/tests/property_capture/extends_implements.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
--TEST--
Property capture with parent class and interface
--FILE--
<?php

class Base {
public $baseProperty = 1;
public function baseMethod() {
return 2;
}
}

interface Face {
public function requiredMethod();
}

$captured = 3;
$anon = new class use($captured) extends Base implements Face {
public function requiredMethod() {
return 4;
}
};

var_dump($anon->baseProperty);
var_dump($anon->baseMethod());
var_dump($anon->captured);
var_dump($anon->requiredMethod());

var_dump($anon instanceof Base);
var_dump($anon instanceof Face);


?>
--EXPECT--
int(1)
int(2)
int(3)
int(4)
bool(true)
bool(true)
21 changes: 21 additions & 0 deletions Zend/tests/property_capture/multiple_instances.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
--TEST--
Capture properties with different values in multiple instances of the same class
--FILE--
<?php

function make_anon_class(int $foo) {
return new class use ($foo as int) {};
}

$one = make_anon_class(1);
$two = make_anon_class(2);

var_dump($one->foo);
var_dump($two->foo);
var_dump($one instanceof $two);

?>
--EXPECT--
int(1)
int(2)
bool(true)
22 changes: 22 additions & 0 deletions Zend/tests/property_capture/parent_constructor_allowed.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
--TEST--
Property capture allowed with parent constructor
--FILE--
<?php

class Base {
public $baseProperty = 1;
public function __construct() {
echo 'This will not be called';
}
}

$captured = 2;
$anon = new class use($captured) extends Base {};

var_dump($anon->baseProperty);
var_dump($anon->captured);

?>
--EXPECT--
int(1)
int(2)
26 changes: 26 additions & 0 deletions Zend/tests/property_capture/pretty_print.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
--TEST--
Pretty print of anon class with property capture
--INI--
zend.assertions=1
assert.exception=0
assert.warning=1
--FILE--
<?php
$a = $b = $c = $d = $e = $f = $g = $h = 42;

assert(false && new class use (
$a,
$b as $b_renamed,
$c as int,
$d as int $d_renamed,
$e as private int,
$f as private int $f_renamed,
$g as readonly int,
$h as readonly int $h_renamed,
) {});

?>
--EXPECTF--
Warning: assert(): assert(false && new class($a, $b, $c, $d, $e, $f, $g, $h) {
public function __construct(public $a, public $b_renamed, public int $c, public int $d_renamed, private int $e, private int $f_renamed, public readonly int $g, public readonly int $h_renamed) {}
}) failed in %s on line %d
Loading