Skip to content

Commit 87ae40c

Browse files
committed
Support $obj::class
1 parent d5f26de commit 87ae40c

File tree

9 files changed

+920
-701
lines changed

9 files changed

+920
-701
lines changed

Zend/tests/class_name_of_var.phpt

Lines changed: 0 additions & 11 deletions
This file was deleted.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--TEST--
2+
::class on an expression cannot be used inside constant expressions
3+
--FILE--
4+
<?php
5+
6+
const A = [0]::class;
7+
8+
?>
9+
--EXPECTF--
10+
Fatal error: (expression)::class cannot be used in constant expressions in %s on line %d

Zend/tests/class_on_object.phpt

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
--TEST--
2+
Using ::class on an object
3+
--FILE--
4+
<?php
5+
6+
$obj = new stdClass;
7+
var_dump($obj::class);
8+
$ref =& $obj;
9+
var_dump($ref::class);
10+
var_dump((new stdClass)::class);
11+
12+
// Placed in a function to check that opcache doesn't perform incorrect constprop.
13+
function test() {
14+
$other = null;
15+
var_dump($other::class);
16+
}
17+
try {
18+
test();
19+
} catch (TypeError $e) {
20+
echo $e->getMessage(), "\n";
21+
}
22+
23+
?>
24+
--EXPECT--
25+
string(8) "stdClass"
26+
string(8) "stdClass"
27+
string(8) "stdClass"
28+
Cannot use ::class on value of type null

Zend/zend_compile.c

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1520,7 +1520,7 @@ static zend_bool zend_try_compile_const_expr_resolve_class_name(zval *zv, zend_a
15201520
zval *class_name;
15211521

15221522
if (class_ast->kind != ZEND_AST_ZVAL) {
1523-
zend_error_noreturn(E_COMPILE_ERROR, "Cannot use ::class with dynamic class name");
1523+
return 0;
15241524
}
15251525

15261526
class_name = zend_ast_get_zval(class_ast);
@@ -8228,15 +8228,20 @@ void zend_compile_class_const(znode *result, zend_ast *ast) /* {{{ */
82288228
void zend_compile_class_name(znode *result, zend_ast *ast) /* {{{ */
82298229
{
82308230
zend_ast *class_ast = ast->child[0];
8231-
zend_op *opline;
82328231

82338232
if (zend_try_compile_const_expr_resolve_class_name(&result->u.constant, class_ast)) {
82348233
result->op_type = IS_CONST;
82358234
return;
82368235
}
82378236

8238-
opline = zend_emit_op_tmp(result, ZEND_FETCH_CLASS_NAME, NULL, NULL);
8239-
opline->op1.num = zend_get_class_fetch_type(zend_ast_get_str(class_ast));
8237+
if (class_ast->kind == ZEND_AST_ZVAL) {
8238+
zend_op *opline = zend_emit_op_tmp(result, ZEND_FETCH_CLASS_NAME, NULL, NULL);
8239+
opline->op1.num = zend_get_class_fetch_type(zend_ast_get_str(class_ast));
8240+
} else {
8241+
znode expr_node;
8242+
zend_compile_expr(&expr_node, class_ast);
8243+
zend_emit_op_tmp(result, ZEND_FETCH_CLASS_NAME, &expr_node, NULL);
8244+
}
82408245
}
82418246
/* }}} */
82428247

@@ -8473,6 +8478,11 @@ void zend_compile_const_expr_class_name(zend_ast **ast_ptr) /* {{{ */
84738478
{
84748479
zend_ast *ast = *ast_ptr;
84758480
zend_ast *class_ast = ast->child[0];
8481+
if (class_ast->kind != ZEND_AST_ZVAL) {
8482+
zend_error_noreturn(E_COMPILE_ERROR,
8483+
"(expression)::class cannot be used in constant expressions");
8484+
}
8485+
84768486
zend_string *class_name = zend_ast_get_str(class_ast);
84778487
uint32_t fetch_type = zend_get_class_fetch_type(class_name);
84788488

Zend/zend_vm_def.h

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7989,14 +7989,32 @@ ZEND_VM_HANDLER(151, ZEND_ASSERT_CHECK, ANY, JMP_ADDR)
79897989
}
79907990
}
79917991

7992-
ZEND_VM_HANDLER(157, ZEND_FETCH_CLASS_NAME, UNUSED|CLASS_FETCH, ANY)
7992+
ZEND_VM_HANDLER(157, ZEND_FETCH_CLASS_NAME, CV|TMPVAR|UNUSED|CLASS_FETCH, ANY)
79937993
{
79947994
uint32_t fetch_type;
79957995
zend_class_entry *called_scope, *scope;
79967996
USE_OPLINE
79977997

7998-
fetch_type = opline->op1.num;
7998+
if (OP1_TYPE != IS_UNUSED) {
7999+
zval *op = GET_OP1_ZVAL_PTR(BP_VAR_R);
8000+
SAVE_OPLINE();
8001+
if (UNEXPECTED(Z_TYPE_P(op) != IS_OBJECT)) {
8002+
ZVAL_DEREF(op);
8003+
if (Z_TYPE_P(op) != IS_OBJECT) {
8004+
zend_type_error("Cannot use ::class on value of type %s",
8005+
zend_get_type_by_const(Z_TYPE_P(op)));
8006+
ZVAL_UNDEF(EX_VAR(opline->result.var));
8007+
FREE_OP1();
8008+
HANDLE_EXCEPTION();
8009+
}
8010+
}
79998011

8012+
ZVAL_STR_COPY(EX_VAR(opline->result.var), Z_OBJCE_P(op)->name);
8013+
FREE_OP1();
8014+
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
8015+
}
8016+
8017+
fetch_type = opline->op1.num;
80008018
scope = EX(func)->op_array.scope;
80018019
if (UNEXPECTED(scope == NULL)) {
80028020
SAVE_OPLINE();

0 commit comments

Comments
 (0)