Skip to content
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
6 changes: 4 additions & 2 deletions Zend/Optimizer/compact_literals.c
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,9 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx
if (opline->op1_type == IS_CONST) {
LITERAL_INFO(opline->op1.constant, 2);
}
LITERAL_INFO(opline->op2.constant, 1);
if (opline->op2_type == IS_CONST) {
LITERAL_INFO(opline->op2.constant, 1);
}
break;
case ZEND_ASSIGN_STATIC_PROP:
case ZEND_ASSIGN_STATIC_PROP_REF:
Expand Down Expand Up @@ -668,7 +670,7 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx
}
break;
case ZEND_FETCH_CLASS_CONSTANT:
if (opline->op1_type == IS_CONST) {
if (opline->op1_type == IS_CONST && opline->op2_type == IS_CONST) {
// op1/op2 class_const
opline->extended_value = add_static_slot(&hash, op_array,
opline->op1.constant,
Expand Down
64 changes: 64 additions & 0 deletions Zend/tests/dynamic_class_const_fetch.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
--TEST--
Dynamic class constant fetch
--FILE--
<?php

class Foo {
public const BAR = 'bar';
}

function test($code) {
try {
var_dump(eval($code));
} catch (Throwable $e) {
echo $e->getMessage(), "\n";
}
}

$const_names = [
['', '"BAR"'],
['$bar = "BAR";', '$bar'],
['$ba = "BA"; $r = "R";', '$ba . $r'],
['', 'strtoupper("bar")'],
['', '$barr'],
['$bar = "BAR"; $barRef = &$bar;', '$barRef'],
['', 'strtolower("CLASS")'],
['', '42'],
['$bar = 42;', '$bar'],
['', '[]'],
['$bar = [];', '$bar'],
];

foreach ($const_names as [$prolog, $const_name]) {
test("$prolog return Foo::{{$const_name}};");
test("\$foo = 'Foo'; $prolog return \$foo::{{$const_name}};");
}

?>
--EXPECTF--
string(3) "bar"
string(3) "bar"
string(3) "bar"
string(3) "bar"
string(3) "bar"
string(3) "bar"
string(3) "bar"
string(3) "bar"

Warning: Undefined variable $barr in %s : eval()'d code on line %d
Cannot use value of type null as class constant name

Warning: Undefined variable $barr in %s : eval()'d code on line %d
Cannot use value of type null as class constant name
string(3) "bar"
string(3) "bar"
string(3) "Foo"
string(3) "Foo"
Cannot use value of type int as class constant name
Cannot use value of type int as class constant name
Cannot use value of type int as class constant name
Cannot use value of type int as class constant name
Cannot use value of type array as class constant name
Cannot use value of type array as class constant name
Cannot use value of type array as class constant name
Cannot use value of type array as class constant name
61 changes: 61 additions & 0 deletions Zend/tests/dynamic_class_const_fetch_cache_slot.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
--TEST--
Dynamic class constant fetch
--FILE--
<?php

class FooParent {
public const BAR = 'bar';
public const BAZ = 'baz';
}

class Foo extends FooParent {
public const BAZ = 'baz child';
}

class BarParent {
public const BAR = 'bar 2';
public const BAZ = 'baz 2';
}

class Bar extends BarParent {
public const BAZ = 'baz 2 child';
}

function test($const) {
echo Foo::{$const}, "\n";
$foo = 'Foo';
echo $foo::{$const}, "\n";
}

test('BAR');
test('BAZ');

$c = function ($const) {
echo self::{$const}, "\n";
echo static::{$const}, "\n";
echo parent::{$const}, "\n";
};

$c->bindTo(null, Foo::class)('BAR');
$c->bindTo(null, Bar::class)('BAZ');
$c->bindTo(null, Foo::class)('class');
$c->bindTo(null, Bar::class)('class');

?>
--EXPECT--
bar
bar
baz child
baz child
bar
bar
bar
baz 2 child
baz 2 child
baz 2
Foo
Foo
FooParent
Bar
Bar
BarParent
24 changes: 24 additions & 0 deletions Zend/tests/dynamic_class_const_fetch_const_expr.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
--TEST--
Dynamic class constant fetch in constant expressions
--FILE--
<?php

class Foo {
public const BAR = 'bar';
public const BA = 'BA';
public const R = 'R';
public const CLASS_ = 'class';
public const A = self::{'BAR'};
public const B = self::{'BA' . 'R'};
public const C = self::{self::BA . self::R};
}

var_dump(Foo::A);
var_dump(Foo::B);
var_dump(Foo::C);

?>
--EXPECT--
string(3) "bar"
string(3) "bar"
string(3) "bar"
37 changes: 37 additions & 0 deletions Zend/tests/dynamic_class_const_fetch_order.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
--TEST--
Dynamic class constant fetch DIM order
--FILE--
<?php

class Foo {
public const FOO = 'Foo';
}

function foo() {
echo "foo()\n";
return 'FOO';
}

function bar() {
echo "bar()\n";
return 'BAR';
}

function test($c) {
try {
echo $c(), "\n";
} catch (Throwable $e) {
echo $e->getMessage(), "\n";
}
}

test(fn() => Foo::{foo()}::{bar()});
test(fn() => Foo::{bar()}::{foo()});

?>
--EXPECT--
foo()
bar()
Undefined constant Foo::BAR
bar()
Undefined constant Foo::BAR
22 changes: 13 additions & 9 deletions Zend/zend_compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -9669,16 +9669,18 @@ static void zend_compile_class_const(znode *result, zend_ast *ast) /* {{{ */
class_ast = ast->child[0];
const_ast = ast->child[1];

if (class_ast->kind == ZEND_AST_ZVAL) {
zend_string *resolved_name;

resolved_name = zend_resolve_class_name_ast(class_ast);
if (const_ast->kind == ZEND_AST_ZVAL && zend_try_ct_eval_class_const(&result->u.constant, resolved_name, zend_ast_get_str(const_ast))) {
result->op_type = IS_CONST;
if (class_ast->kind == ZEND_AST_ZVAL && const_ast->kind == ZEND_AST_ZVAL) {
zval *const_zv = zend_ast_get_zval(const_ast);
if (Z_TYPE_P(const_zv) == IS_STRING) {
zend_string *const_str = Z_STR_P(const_zv);
zend_string *resolved_name = zend_resolve_class_name_ast(class_ast);
if (zend_try_ct_eval_class_const(&result->u.constant, resolved_name, const_str)) {
result->op_type = IS_CONST;
zend_string_release_ex(resolved_name, 0);
return;
}
zend_string_release_ex(resolved_name, 0);
return;
}
zend_string_release_ex(resolved_name, 0);
}

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

zend_set_class_name_op1(opline, &class_node);

opline->extended_value = zend_alloc_cache_slots(2);
if (opline->op1_type == IS_CONST || opline->op2_type == IS_CONST) {
opline->extended_value = zend_alloc_cache_slots(2);
}
}
/* }}} */

Expand Down
15 changes: 15 additions & 0 deletions Zend/zend_execute.c
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,21 @@ static zend_always_inline zval *_get_zval_ptr_cv_BP_VAR_W(uint32_t var EXECUTE_D
return ret;
}

static zend_always_inline zval *_get_zval_ptr_tmpvarcv(int op_type, znode_op node, int type EXECUTE_DATA_DC)
{
if (op_type & (IS_TMP_VAR|IS_VAR)) {
if (op_type == IS_TMP_VAR) {
return _get_zval_ptr_tmp(node.var EXECUTE_DATA_CC);
} else {
ZEND_ASSERT(op_type == IS_VAR);
return _get_zval_ptr_var_deref(node.var EXECUTE_DATA_CC);
}
} else {
ZEND_ASSERT(op_type == IS_CV);
return _get_zval_ptr_cv_deref(node.var, type EXECUTE_DATA_CC);
}
}

static zend_always_inline zval *_get_zval_ptr(int op_type, znode_op node, int type EXECUTE_DATA_DC OPLINE_DC)
{
if (op_type & (IS_TMP_VAR|IS_VAR)) {
Expand Down
4 changes: 4 additions & 0 deletions Zend/zend_language_parser.y
Original file line number Diff line number Diff line change
Expand Up @@ -1379,6 +1379,10 @@ class_constant:
{ $$ = zend_ast_create_class_const_or_name($1, $3); }
| variable_class_name T_PAAMAYIM_NEKUDOTAYIM identifier
{ $$ = zend_ast_create_class_const_or_name($1, $3); }
| class_name T_PAAMAYIM_NEKUDOTAYIM '{' expr '}'
{ $$ = zend_ast_create(ZEND_AST_CLASS_CONST, $1, $4); }
| variable_class_name T_PAAMAYIM_NEKUDOTAYIM '{' expr '}'
{ $$ = zend_ast_create(ZEND_AST_CLASS_CONST, $1, $4); }
;

optional_expr:
Expand Down
Loading