10000 Add limited support for new in initializers · php/php-src@52d3d0d · GitHub
[go: up one dir, main page]

Skip to content

Commit 52d3d0d

Browse files
committed
Add limited support for new in initializers
Add support for new expressions inside parameter default values, static variable initializers, global constant initializers and attribute arguments. RFC: https://wiki.php.net/rfc/new_in_initializers Closes GH-7153.
1 parent cce3165 commit 52d3d0d

21 files changed

+629
-22
lines changed

UPGRADING

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,10 @@ PHP 8.1 UPGRADE NOTES
193193
RFC: https://wiki.php.net/rfc/noreturn_type
194194
. Added support for fibers.
195195
RFC: https://wiki.php.net/rfc/fibers
196+
. It is now possible to use "new ClassName()" expressions as parameter
197+
default values, static variable and global constant initializers, as well
198+
as attribute arguments.
199+
RFC: https://wiki.php.net/rfc/new_in_initializers
196200
. File uploads now provide an additional full_path key, which contains the
197201
full path (rather than just the basename) of the uploaded file. This is
198202
intended for use in conjunction with "upload webkitdirectory".

Zend/tests/constexpr/new.phpt

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
--TEST--
2+
new in constant expressions
3+
--FILE--
4+
<?php
5+
6+
try {
7+
eval('static $a = new DoesNotExist;');
8+
} catch (Error $e) {
9+
echo $e->getMessage(), "\n";
10+
}
11+
12+
static $b = new stdClass;
13+
var_dump($b);
14+
15+
try {
16+
eval('static $c = new stdClass([] + 0);');
17+
} catch (Error $e) {
18+
echo $e->getMessage(), "\n";
19+
}
20+
21+
class Test {
22+
public function __construct(public $a, public $b) {}
23+
}
24+
25+
try {
26+
eval('static $d = new Test(new stdClass, [] + 0);');
27+
} catch (Error $e) {
28+
echo $e->getMessage(), "\n";
29+
}
30+
31+
static $e = new Test(new stdClass, 42);
32+
var_dump($e);
33+
34+
class Test2 {
35+
public function __construct() {
36+
echo "Side-effect\n";
37+
throw new Exception("Failed to construct");
38+
}
39+
}
40+
41+
try {
42+
eval('static $f = new Test2();');
43+
} catch (Exception $e) {
44+
echo $e->getMessage(), "\n";
45+
}
46+
47+
?>
48+
--EXPECT--
49+
Class "DoesNotExist" not found
50+
object(stdClass)#2 (0) {
51+
}
52+
Unsupported operand types: array + int
53+
Unsupported operand types: array + int
54+
object(Test)#4 (2) {
55+
["a"]=>
56+
object(stdClass)#1 (0) {
57+
}
58+
["b"]=>
59+
int(42)
60+
}
61+
Side-effect
62+
Failed to construct

Zend/tests/constexpr/new_allowed.phpt

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
--TEST--
2+
Places where new is allowed
3+
--FILE--
4+
<?php
5+
6+
#[SomeAttribute(new stdClass)]
7+
class Test {
8+
public function __construct(
9+
public $prop = new stdClass,
10+
) {
11+
var_dump($prop);
12+
}
13+
}
14+
15+
function test($param = new stdClass) {
16+
static $var = new stdClass;
17+
var_dump($param, $var);
18+
}
19+
20+
const TEST = new stdClass;
21+
22+
new Test;
23+
test();
24+
var_dump(TEST);
25+
26+
?>
27+
--EXPECT--
28+
object(stdClass)#3 (0) {
29+
}
30+
object(stdClass)#2 (0) {
31+
}
32+
object(stdClass)#3 (0) {
33+
}
34+
object(stdClass)#1 (0) {
35+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--TEST--
2+
New with anonymous class is not supported in constant expressions
3+
--FILE--
4+
<?php
5+
6+
static $x = new class {};
7+
8+
?>
9+
--EXPECTF--
10+
Fatal error: Cannot use anonymous class in constant expression in %s on line %d
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
--TEST--
2+
Check that const exprs are pre-evaluated in new arguments
3+
--FILE--
4+
<?php
5+
6+
class C {
7+
public function __construct(public $x) {}
8+
}
9+
function test(
10+
$a = new C(__CLASS__),
11+
$b = new C(__FUNCTION__),
12+
$c = new C(x: __FILE__),
13+
) {
14+
var_dump($a, $b, $c);
15+
}
16+
test();
17+
18+
// Check that nested new works as well.
19+
function test2($p = new C(new C(__FUNCTION__))) {
20+
var_dump($p);
21+
}
22+
test2();
23+
24+
?>
25+
--EXPECTF--
26+
object(C)#1 (1) {
27+
["x"]=>
28+
string(0) ""
29+
}
30+
object(C)#2 (1) {
31+
["x"]=>
32+
string(4) "test"
33+
}
34+
object(C)#3 (1) {
35+
["x"]=>
36+
string(%d) "%snew_arg_eval.php"
37+
}
38+
object(C)#3 (1) {
39+
["x"]=>
40+
object(C)#2 (1) {
41+
["x"]=>
42+
string(5) "test2"
43+
}
44+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--TEST--
2+
Argument unpacking in new arguments in const expr (not yet supported)
3+
--FILE--
4+
<?php
5+
6+
static $x = new stdClass(...[0]);
7+
8+
?>
9+
--EXPECTF--
10+
Fatal error: Argument unpacking in constant expressions is not supported in %s on line %d
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--TEST--
2+
Dynamic class name in new is not supported
3+
--FILE--
4+
<?php
5+
6+
static $x = new (FOO);
7+
8+
?>
9+
--EXPECTF--
10+
Fatal error: Cannot use dynamic class name in constant expression in %s on line %d
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--TEST--
2+
Invalid operation in new arg in const expr
3+
--FILE--
4+
<?php
5+
6+
static $x = new stdClass($foo);
7+
8+
?>
9+
--EXPECTF--
10+
Fatal error: Constant expression contains invalid operations in %s on line %d
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
--TEST--
2+
Named params in new in const expr
3+
--FILE--
4+
<?php
5+
6+
class Vec {
7+
public function __construct(public float $x, public float $y, public float $z) {}
8+
}
9+
10+
static $a = new Vec(x: 0.0, y: 1.0, z: 2.0);
11+
var_dump($a);
12+
13+
static $b = new Vec(z: 0.0, y: 1.0, x: 2.0);
14+
var_dump($b);
15+
16+
static $c = new Vec(0.0, z: 1.0, y: 2.0);
17+
var_dump($c);
18+
19+
try {
20+
eval('static $d = new Vec(x: 0.0, x: 1.0);');
21+
} catch (Error $e) {
22+
echo $e->getMessage(), "\n";
23+
}
24+
25+
?>
26+
--EXPECT--
27+
object(Vec)#1 (3) {
28+
["x"]=>
29+
float(0)
30+
["y"]=>
31+
float(1)
32+
["z"]=>
33+
float(2)
34+
}
35+
object(Vec)#2 (3) {
36+
["x"]=>
37+
float(2)
38+
["y"]=>
39+
float(1)
40+
["z"]=>
41+
float(0)
42+
}
43+
object(Vec)#3 (3) {
44+
["x"]=>
45+
float(0)
46+
["y"]=>
47+
float(2)
48+
["z"]=>
49+
float(1)
50+
}
51+
Named parameter $x overwrites previous argument
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
--TEST--
2+
New not allowed in class constant
3+
--FILE--
4+
<?php
5+
6+
// New in class constants (and static properties) brings up evaluation order questions: When
7+
// should the (potentially side-effecting) new expression be evaluated? Evaluating it when the
8+
// class is declared would break references to classes that are declared later in the same
9+
// file. On the other hand, the current lazy evaluation of initializers is somewhat ill-defined
10+
// when we start considering side-effecting expressions.
11+
12+
class Test {
13+
const X = new stdClass;
14+
}
15+
16+
?>
17+
--EXPECTF--
18+
Fatal error: New expressions are not supported in this context in %s on line %d
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
--TEST--
2+
New not allowed in property
3+
--FILE--
4+
<?php
5+
6+
// New in (non-static) properties is a particularly tricky case. The initializer needs to be
7+
// evaluated on each object construction. Currently, the places where this can happen is
8+
// during object creation, or as part of the constructor. Doing this during object creation
9+
// can issues for use-cases such as serialization or generally anything that is effectively
10+
// based on newInstanceWithoutConstructor(). Handling this via the constructor is more
11+
// promising, but requires injecting code in the constructor, which may require adding a
12+
// constructor which is not explicitly declared, which may also require a child class to
13+
// call a constructor that has not been explicitly declared, otherwise properties may be
14+
// left uninitialized. A third option is another mechanism between object creation and
15+
// constructor invocation. Overall, the best way to address this is not clear right now.
16+
17+
class Test {
18+
public $prop = new stdClass;
19+
}
20+
21+
?>
22+
--EXPECTF--
23+
Fatal error: New expressions are not supported in this context in %s on line %d
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--TEST--
2+
Positional argument after named argument in new arguments
3+
--FILE--
4+
<?php
5+
6+
static $x = new stdClass(x: 0, 1);
7+
8+
?>
9+
--EXPECTF--
10+
Fatal error: Cannot use positional argument after named argument in %s on line %d
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
--TEST--
2+
new self / new parent in constant expression
3+
--FILE--
4+
<?php
5+
6+
class A {
7+
public static function invalid($x = new parent) {
8+
}
9+
}
10+
class B extends A {
11+
public static function method($x = new self, $y = new parent) {
12+
var_dump($x, $y);
13+
}
14+
}
15+
16+
function invalid($x = new self) {}
17+
18+
B::method();
19+
20+
try {
21+
invalid();
22+
} catch (Error $e) {
23+
echo $e->getMessage(), "\n";
24+
}
25+
26+
try {
27+
B::invalid();
28+
} catch (Error $e) {
29+
echo $e->getMessage(), "\n";
30+
}
31+
32+
?>
33+
--EXPECT--
34+
object(B)#1 (0) {
35+
}
36+
object(A)#2 (0) {
37+
}
38+
Cannot access "self" when no class scope is active
39+
Cannot access "parent" when current class scope has no parent

Zend/tests/constexpr/new_static.phpt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--TEST--
2+
Static in new is not supported
3+
--FILE--
4+
<?php
5+
6+
static $x = new static;
7+
8+
?>
9+
--EXPECTF--
10+
Fatal error: "static" is not allowed in compile-time constants in %s on line %d

Zend/zend_API.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4803,7 +4803,7 @@ static zend_result get_default_via_ast(zval *default_value_zval, const char *def
48034803
/* Disable constant substitution, to make getDefaultValueConstant() work. */
48044804
CG(compiler_options) |= ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION | ZEND_COMPILE_NO_PERSISTENT_CONSTANT_SUBSTITUTION;
48054805
zend_file_context_begin(&original_file_context);
4806-
zend_const_expr_to_zval(default_value_zval, const_expr_ast_ptr);
4806+
zend_const_expr_to_zval(default_value_zval, const_expr_ast_ptr, /* allow_dynamic */ true);
48074807
CG(ast_arena) = original_ast_arena;
48084808
CG(compiler_options) = original_compiler_options;
48094809
zend_file_context_end(&original_file_context);

0 commit comments

Comments
 (0)
0