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
11 changes: 0 additions & 11 deletions Zend/tests/class_name_of_var.phpt

This file was deleted.

10 changes: 10 additions & 0 deletions Zend/tests/class_on_constant_evaluated_expression.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
--TEST--
An error should be generated when using ::class on a constant evaluated expression
--FILE--
<?php

(1+1)::class;

?>
--EXPECTF--
Fatal error: Cannot use ::class on value of type int in %s on line %d
10 changes: 10 additions & 0 deletions Zend/tests/class_on_expression_in_constant_expression.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
--TEST--
::class on an expression cannot be used inside constant expressions
--FILE--
<?php

const A = [0]::class;

?>
--EXPECTF--
Fatal error: (expression)::class cannot be used in constant expressions in %s on line %d
28 changes: 28 additions & 0 deletions Zend/tests/class_on_object.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
--TEST--
Using ::class on an object
--FILE--
<?php

$obj = new stdClass;
var_dump($obj::class);
$ref =& $obj;
var_dump($ref::class);
var_dump((new stdClass)::class);

// Placed in a function to check that opcache doesn't perform incorrect constprop.
function test() {
$other = null;
var_dump($other::class);
}
try {
test();
} catch (TypeError $e) {
echo $e->getMessage(), "\n";
}

?>
--EXPECT--
string(8) "stdClass"
string(8) "stdClass"
string(8) "stdClass"
Cannot use ::class on value of type null
25 changes: 21 additions & 4 deletions Zend/zend_compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -1520,7 +1520,7 @@ static zend_bool zend_try_compile_const_expr_resolve_class_name(zval *zv, zend_a
zval *class_name;

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

class_name = zend_ast_get_zval(class_ast);
Expand Down Expand Up @@ -8228,15 +8228,27 @@ void zend_compile_class_const(znode *result, zend_ast *ast) /* {{{ */
void zend_compile_class_name(znode *result, zend_ast *ast) /* {{{ */
{
zend_ast *class_ast = ast->child[0];
zend_op *opline;

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

opline = zend_emit_op_tmp(result, ZEND_FETCH_CLASS_NAME, NULL, NULL);
opline->op1.num = zend_get_class_fetch_type(zend_ast_get_str(class_ast));
if (class_ast->kind == ZEND_AST_ZVAL) {
zend_op *opline = zend_emit_op_tmp(result, ZEND_FETCH_CLASS_NAME, NULL, NULL);
opline->op1.num = zend_get_class_fetch_type(zend_ast_get_str(class_ast));
} else {
znode expr_node;
zend_compile_expr(&expr_node, class_ast);
if (expr_node.op_type == IS_CONST) {
/* Unlikely case that happen if class_ast is constant folded.
* Handle it here, to avoid needing a CONST specialization in the VM. */
zend_error_noreturn(E_COMPILE_ERROR, "Cannot use ::class on value of type %s",
zend_get_type_by_const(Z_TYPE(expr_node.u.constant)));
}

zend_emit_op_tmp(result, ZEND_FETCH_CLASS_NAME, &expr_node, NULL);
}
}
/* }}} */

Expand Down Expand Up @@ -8473,6 +8485,11 @@ void zend_compile_const_expr_class_name(zend_ast **ast_ptr) /* {{{ */
{
zend_ast *ast = *ast_ptr;
zend_ast *class_ast = ast->child[0];
if (class_ast->kind != ZEND_AST_ZVAL) {
zend_error_noreturn(E_COMPILE_ERROR,
"(expression)::class cannot be used in constant expressions");
}

zend_string *class_name = zend_ast_get_str(class_ast);
uint32_t fetch_type = zend_get_class_fetch_type(class_name);

Expand Down
22 changes: 20 additions & 2 deletions Zend/zend_vm_def.h
Original file line number Diff line number Diff line change
Expand Up @@ -7989,14 +7989,32 @@ ZEND_VM_HANDLER(151, ZEND_ASSERT_CHECK, ANY, JMP_ADDR)
}
}

ZEND_VM_HANDLER(157, ZEND_FETCH_CLASS_NAME, UNUSED|CLASS_FETCH, ANY)
ZEND_VM_HANDLER(157, ZEND_FETCH_CLASS_NAME, CV|TMPVAR|UNUSED|CLASS_FETCH, ANY)
{
uint32_t fetch_type;
zend_class_entry *called_scope, *scope;
USE_OPLINE

fetch_type = opline->op1.num;
if (OP1_TYPE != IS_UNUSED) {
zval *op = GET_OP1_ZVAL_PTR(BP_VAR_R);
SAVE_OPLINE();
if (UNEXPECTED(Z_TYPE_P(op) != IS_OBJECT)) {
ZVAL_DEREF(op);
if (Z_TYPE_P(op) != IS_OBJECT) {
zend_type_error("Cannot use ::class on value of type %s",
zend_get_type_by_const(Z_TYPE_P(op)));
ZVAL_UNDEF(EX_VAR(opline->result.var));
FREE_OP1();
HANDLE_EXCEPTION();
}
}

ZVAL_STR_COPY(EX_VAR(opline->result.var), Z_OBJCE_P(op)->name);
FREE_OP1();
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}

fetch_type = opline->op1.num;
scope = EX(func)->op_array.scope;
if (UNEXPECTED(scope == NULL)) {
SAVE_OPLINE();
Expand Down
Loading