8000 New in initializers (reduced version) · php/php-src@e11e76b · GitHub
[go: up one dir, main page]

Skip to content

Commit e11e76b

Browse files
committed
New in initializers (reduced version)
1 parent b642138 commit e11e76b

19 files changed

+589
-22
lines changed

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

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
@@ -4790,7 +4790,7 @@ static zend_result get_default_via_ast(zval *default_value_zval, const char *def
47904790
/* Disable constant substitution, to make getDefaultValueConstant() work. */
47914791
CG(compiler_options) |= ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION | ZEND_COMPILE_NO_PERSISTENT_CONSTANT_SUBSTITUTION;
47924792
zend_file_context_begin(&original_file_context);
4793-
zend_const_expr_to_zval(default_value_zval, const_expr_ast_ptr);
4793+
zend_const_expr_to_zval(default_value_zval, const_expr_ast_ptr, /* allow_dynamic */ true);
47944794
CG(ast_arena) = original_ast_arena;
47954795
CG(compiler_options) = original_compiler_options;
47964796
zend_file_context_end(&original_file_context);

0 commit comments

Comments
 (0)
0