Skip to content

Commit 4901fd6

Browse files
Merge branch 'master' of https://github.com/marcosmarcolin/php-src into array-illegal-offset-message
2 parents 9416328 + 45c755c commit 4901fd6

25 files changed

+2198
-818
lines changed

UPGRADING

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ PHP 8.3 UPGRADE NOTES
2424
Error when using more than
2525
`zend.max_allowed_stack_size-zend.reserved_stack_size` bytes of stack
2626
(`fiber.stack_size-zend.reserved_stack_size` for fibers).
27+
. Class constants can now be accessed dynamically using the C::{$name} syntax.
28+
RFC: https://wiki.php.net/rfc/dynamic_class_constant_fetch
2729

2830
========================================
2931
2. New Features

Zend/Optimizer/compact_literals.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,9 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx
211211
if (opline->op1_type == IS_CONST) {
212212
LITERAL_INFO(opline->op1.constant, 2);
213213
}
214-
LITERAL_INFO(opline->op2.constant, 1);
214+
if (opline->op2_type == IS_CONST) {
215+
LITERAL_INFO(opline->op2.constant, 1);
216+
}
215217
break;
216218
case ZEND_ASSIGN_STATIC_PROP:
217219
case ZEND_ASSIGN_STATIC_PROP_REF:
@@ -668,7 +670,7 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx
668670
}
669671
break;
670672
case ZEND_FETCH_CLASS_CONSTANT:
671-
if (opline->op1_type == IS_CONST) {
673+
if (opline->op1_type == IS_CONST && opline->op2_type == IS_CONST) {
672674
// op1/op2 class_const
673675
opline->extended_value = add_static_slot(&hash, op_array,
674676
opline->op1.constant,
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
--TEST--
2+
Dynamic class constant fetch
3+
--FILE--
4+
<?php
5+
6+
class Foo {
7+
public const BAR = 'bar';
8+
}
9+
10+
function test($code) {
11+
try {
12+
var_dump(eval($code));
13+
} catch (Throwable $e) {
14+
echo $e->getMessage(), "\n";
15+
}
16+
}
17+
18+
$const_names = [
19+
['', '"BAR"'],
20+
['$bar = "BAR";', '$bar'],
21+
['$ba = "BA"; $r = "R";', '$ba . $r'],
22+
['', 'strtoupper("bar")'],
23+
['', '$barr'],
24+
['$bar = "BAR"; $barRef = &$bar;', '$barRef'],
25+
['', 'strtolower("CLASS")'],
26+
['', '42'],
27+
['$bar = 42;', '$bar'],
28+
['', '[]'],
29+
['$bar = [];', '$bar'],
30+
];
31+
32+
foreach ($const_names as [$prolog, $const_name]) {
33+
test("$prolog return Foo::{{$const_name}};");
34+
test("\$foo = 'Foo'; $prolog return \$foo::{{$const_name}};");
35+
}
36+
37+
?>
38+
--EXPECTF--
39+
string(3) "bar"
40+
string(3) "bar"
41+
string(3) "bar"
42+
string(3) "bar"
43+
string(3) "bar"
44+
string(3) "bar"
45+
string(3) "bar"
46+
string(3) "bar"
47+
48+
Warning: Undefined variable $barr in %s : eval()'d code on line %d
49+
Cannot use value of type null as class constant name
50+
51+
Warning: Undefined variable $barr in %s : eval()'d code on line %d
52+
Cannot use value of type null as class constant name
53+
string(3) "bar"
54+
string(3) "bar"
55+
string(3) "Foo"
56+
string(3) "Foo"
57+
Cannot use value of type int as class constant name
58+
Cannot use value of type int as class constant name
59+
Cannot use value of type int as class constant name
60+
Cannot use value of type int as class constant name
61+
Cannot use value of type array as class constant name
62+
Cannot use value of type array as class constant name
63+
Cannot use value of type array as class constant name
64+
Cannot use value of type array as class constant name
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
--TEST--
2+
Dynamic class constant fetch
3+
--FILE--
4+
<?php
5+
6+
class FooParent {
7+
public const BAR = 'bar';
8+
public const BAZ = 'baz';
9+
}
10+
11+
class Foo extends FooParent {
12+
public const BAZ = 'baz child';
13+
}
14+
15+
class BarParent {
16+
public const BAR = 'bar 2';
17+
public const BAZ = 'baz 2';
18+
}
19+
20+
class Bar extends BarParent {
21+
public const BAZ = 'baz 2 child';
22+
}
23+
24+
function test($const) {
25+
echo Foo::{$const}, "\n";
26+
$foo = 'Foo';
27+
echo $foo::{$const}, "\n";
28+
}
29+
30+
test('BAR');
31+
test('BAZ');
32+
33+
$c = function ($const) {
34+
echo self::{$const}, "\n";
35+
echo static::{$const}, "\n";
36+
echo parent::{$const}, "\n";
37+
};
38+
39+
$c->bindTo(null, Foo::class)('BAR');
40+
$c->bindTo(null, Bar::class)('BAZ');
41+
$c->bindTo(null, Foo::class)('class');
42+
$c->bindTo(null, Bar::class)('class');
43+
44+
?>
45+
--EXPECT--
46+
bar
47+
bar
48+
baz child
49+
baz child
50+
bar
51+
bar
52+
bar
53+
baz 2 child
54+
baz 2 child
55+
baz 2
56+
Foo
57+
Foo
58+
FooParent
59+
Bar
60+
Bar
61+
BarParent
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
--TEST--
2+
Dynamic class constant fetch in constant expressions
3+
--FILE--
4+
<?php
5+
6+
class Foo {
7+
public const BAR = 'bar';
8+
public const BA = 'BA';
9+
public const R = 'R';
10+
public const CLASS_ = 'class';
11+
public const A = self::{'BAR'};
12+
public const B = self::{'BA' . 'R'};
13+
public const C = self::{self::BA . self::R};
14+
}
15+
16+
var_dump(Foo::A);
17+
var_dump(Foo::B);
18+
var_dump(Foo::C);
19+
20+
?>
21+
--EXPECT--
22+
string(3) "bar"
23+
string(3) "bar"
24+
string(3) "bar"
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
--TEST--
2+
Dynamic class constant fetch DIM order
3+
--FILE--
4+
<?php
5+
6+
class Foo {
7+
public const FOO = 'Foo';
8+
}
9+
10+
function foo() {
11+
echo "foo()\n";
12+
return 'FOO';
13+
}
14+
15+
function bar() {
16+
echo "bar()\n";
17+
return 'BAR';
18+
}
19+
20+
function test($c) {
21+
try {
22+
echo $c(), "\n";
23+
} catch (Throwable $e) {
24+
echo $e->getMessage(), "\n";
25+
}
26+
}
27+
28+
test(fn() => Foo::{foo()}::{bar()});
29+
test(fn() => Foo::{bar()}::{foo()});
30+
31+
?>
32+
--EXPECT--
33+
foo()
34+
bar()
35+
Undefined constant Foo::BAR
36+
bar()
37+
Undefined constant Foo::BAR

Zend/tests/fibers/gh10340-001.phpt

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
--TEST--
2+
Bug GH-10340 001 (Assertion in zend_fiber_object_gc())
3+
--FILE--
4+
<?php
5+
function f() {
6+
$$y = Fiber::getCurrent();
7+
Fiber::suspend();
8+
}
9+
$fiber = new Fiber(function() {
10+
get_defined_vars();
11+
f();
12+
});
13+
$fiber->start();
14+
gc_collect_cycles();
15+
?>
16+
==DONE==
17+
--EXPECTF--
18+
Warning: Undefined variable $y in %s on line %d
19+
==DONE==

Zend/tests/fibers/gh10340-002.phpt

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
--TEST--
2+
Bug GH-10340 002 (Assertion in zend_fiber_object_gc())
3+
--FILE--
4+
<?php
5+
function f() {
6+
$y = 'a';
7+
$$y = Fiber::getCurrent();
8+
Fiber::suspend();
9+
}
10+
$fiber = new Fiber(function() {
11+
get_defined_vars();
12+
f();
13+
});
14+
$fiber->start();
15+
gc_collect_cycles();
16+
?>
17+
==DONE==
18+
--EXPECT--
19+
==DONE==

Zend/tests/fibers/gh10340-003.phpt

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
--TEST--
2+
Bug GH-10340 003 (Assertion in zend_fiber_object_gc())
3+
--FILE--
4+
<?php
5+
6+
class C {
7+
public function __destruct() {
8+
echo __METHOD__, "\n";
9+
}
10+
}
11+
12+
function f() {
13+
$c = new C();
14+
$y = 'a';
15+
$$y = Fiber::getCurrent();
16+
Fiber::suspend();
17+
}
18+
19+
$fiber = new Fiber(function() {
20+
get_defined_vars();
21+
f();
22+
});
23+
24+
$fiber->start();
25+
26+
print "1\n";
27+
28+
$fiber = null;
29+
gc_collect_cycles();
30+
31+
print "2\n";
32+
?>
33+
==DONE==
34+
--EXPECT--
35+
1
36+
C::__destruct
37+
2
38+
==DONE==

Zend/zend_compile.c

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9669,16 +9669,18 @@ static void zend_compile_class_const(znode *result, zend_ast *ast) /* {{{ */
96699669
class_ast = ast->child[0];
96709670
const_ast = ast->child[1];
96719671

9672-
if (class_ast->kind == ZEND_AST_ZVAL) {
9673-
zend_string *resolved_name;
9674-
9675-
resolved_name = zend_resolve_class_name_ast(class_ast);
9676-
if (const_ast->kind == ZEND_AST_ZVAL && zend_try_ct_eval_class_const(&result->u.constant, resolved_name, zend_ast_get_str(const_ast))) {
9677-
result->op_type = IS_CONST;
9672+
if (class_ast->kind == ZEND_AST_ZVAL && const_ast->kind == ZEND_AST_ZVAL) {
9673+
zval *const_zv = zend_ast_get_zval(const_ast);
9674+
if (Z_TYPE_P(const_zv) == IS_STRING) {
9675+
zend_string *const_str = Z_STR_P(const_zv);
9676+
zend_string *resolved_name = zend_resolve_class_name_ast(class_ast);
9677+
if (zend_try_ct_eval_class_const(&result->u.constant, resolved_name, const_str)) {
9678+
result->op_type = IS_CONST;
9679+
zend_string_release_ex(resolved_name, 0);
9680+
return;
9681+
}
96789682
zend_string_release_ex(resolved_name, 0);
9679-
return;
96809683
}
9681-
zend_string_release_ex(resolved_name, 0);
96829684
}
96839685

96849686
zend_compile_class_ref(&class_node, class_ast, ZEND_FETCH_CLASS_EXCEPTION);
@@ -9689,7 +9691,9 @@ static void zend_compile_class_const(znode *result, zend_ast *ast) /* {{{ */
96899691

96909692
zend_set_class_name_op1(opline, &class_node);
96919693

9692-
opline->extended_value = zend_alloc_cache_slots(2);
9694+
if (opline->op1_type == IS_CONST || opline->op2_type == IS_CONST) {
9695+
opline->extended_value = zend_alloc_cache_slots(2);
9696+
}
96939697
}
96949698
/* }}} */
96959699

Zend/zend_compile.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -140,10 +140,10 @@ struct _zend_op {
140140
znode_op result;
141141
uint32_t extended_value;
142142
uint32_t lineno;
143-
zend_uchar opcode;
144-
zend_uchar op1_type;
145-
zend_uchar op2_type;
146-
zend_uchar result_type;
143+
zend_uchar opcode; /* Opcodes defined in Zend/zend_vm_opcodes.h */
144+
zend_uchar op1_type; /* IS_UNUSED, IS_CONST, IS_TMP_VAR, IS_VAR, IS_CV */
145+
zend_uchar op2_type; /* IS_UNUSED, IS_CONST, IS_TMP_VAR, IS_VAR, IS_CV */
146+
zend_uchar result_type; /* IS_UNUSED, IS_CONST, IS_TMP_VAR, IS_VAR, IS_CV */
147147
};
148148

149149

Zend/zend_execute.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,21 @@ static zend_always_inline zval *_get_zval_ptr_cv_BP_VAR_W(uint32_t var EXECUTE_D
387387
return ret;
388388
}
389389

390+
static zend_always_inline zval *_get_zval_ptr_tmpvarcv(int op_type, znode_op node, int type EXECUTE_DATA_DC)
391+
{
392+
if (op_type & (IS_TMP_VAR|IS_VAR)) {
393+
if (op_type == IS_TMP_VAR) {
394+
return _get_zval_ptr_tmp(node.var EXECUTE_DATA_CC);
395+
} else {
396+
ZEND_ASSERT(op_type == IS_VAR);
397+
return _get_zval_ptr_var_deref(node.var EXECUTE_DATA_CC);
398+
}
399+
} else {
400+
ZEND_ASSERT(op_type == IS_CV);
401+
return _get_zval_ptr_cv_deref(node.var, type EXECUTE_DATA_CC);
402+
}
403+
}
404+
390405
static zend_always_inline zval *_get_zval_ptr(int op_type, znode_op node, int type EXECUTE_DATA_DC OPLINE_DC)
391406
{
392407
if (op_type & (IS_TMP_VAR|IS_VAR)) {

Zend/zend_extensions.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -280,11 +280,11 @@ ZEND_API int zend_get_op_array_extension_handles(const char *module_name, int ha
280280
return handle;
281281
}
282282

283-
ZEND_API size_t zend_internal_run_time_cache_reserved_size() {
283+
ZEND_API size_t zend_internal_run_time_cache_reserved_size(void) {
284284
return zend_op_array_extension_handles * sizeof(void *);
285285
}
286286

287-
ZEND_API void zend_init_internal_run_time_cache() {
287+
ZEND_API void zend_init_internal_run_time_cache(void) {
288288
size_t rt_size = zend_internal_run_time_cache_reserved_size();
289289
if (rt_size) {
290290
size_t functions = zend_hash_num_elements(CG(function_table));

Zend/zend_fibers.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -755,8 +755,10 @@ static HashTable *zend_fiber_object_gc(zend_object *object, zval **table, int *n
755755
if (lastSymTable) {
756756
zval *val;
757757
ZEND_HASH_FOREACH_VAL(lastSymTable, val) {
758-
ZEND_ASSERT(Z_TYPE_P(val) == IS_INDIRECT);
759-
zend_get_gc_buffer_add_zval(buf, Z_INDIRECT_P(val));
758+
if (EXPECTED(Z_TYPE_P(val) == IS_INDIRECT)) {
759+
val = Z_INDIRECT_P(val);
760+
}
761+
zend_get_gc_buffer_add_zval(buf, val);
760762
} ZEND_HASH_FOREACH_END();
761763
}
762764
lastSymTable = symTable;

0 commit comments

Comments
 (0)