diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index 9deb6bcef98..a09c13fd89e 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -784,6 +784,19 @@ ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate(zval *result, zend_ast *ast zend_enum_new(result, ce, case_name, case_value_zv); break; } + case ZEND_AST_CLASS_CONST: + { + zend_string *class_name = zend_ast_get_str(ast->child[0]); + zend_string *const_name = zend_ast_get_str(ast->child[1]); + zval *zv = zend_get_class_constant_ex(class_name, const_name, scope, ast->attr); + + if (UNEXPECTED(zv == NULL)) { + ZVAL_UNDEF(result); + return FAILURE; + } + ZVAL_COPY_OR_DUP(result, zv); + break; + } default: zend_throw_error(NULL, "Unsupported constant expression"); ret = FAILURE; diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 8aeca71cd10..0d44f416ce1 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -9462,10 +9462,7 @@ void zend_compile_const_expr_class_const(zend_ast **ast_ptr) /* {{{ */ { zend_ast *ast = *ast_ptr; zend_ast *class_ast = ast->child[0]; - zend_ast *const_ast = ast->child[1]; zend_string *class_name; - zend_string *const_name = zend_ast_get_str(const_ast); - zend_string *name; int fetch_type; if (class_ast->kind != ZEND_AST_ZVAL) { @@ -9482,17 +9479,17 @@ void zend_compile_const_expr_class_const(zend_ast **ast_ptr) /* {{{ */ } if (ZEND_FETCH_CLASS_DEFAULT == fetch_type) { - class_name = zend_resolve_class_name_ast(class_ast); - } else { - zend_string_addref(class_name); + zend_string *tmp = zend_resolve_class_name_ast(class_ast); + + zend_string_release_ex(class_name, 0); + if (tmp != class_name) { + zval *zv = zend_ast_get_zval(class_ast); + + ZVAL_STR(zv, tmp); + } } - name = zend_create_member_string(class_name, const_name); - - zend_ast_destroy(ast); - zend_string_release_ex(class_name, 0); - - *ast_ptr = zend_ast_create_constant(name, fetch_type | ZEND_FETCH_CLASS_EXCEPTION); + ast->attr |= ZEND_FETCH_CLASS_EXCEPTION; } /* }}} */ diff --git a/Zend/zend_constants.c b/Zend/zend_constants.c index 978c57ecd7a..4351de8e35e 100644 --- a/Zend/zend_constants.c +++ b/Zend/zend_constants.c @@ -324,11 +324,87 @@ ZEND_API zval *zend_get_constant(zend_string *name) return NULL; } +ZEND_API zval *zend_get_class_constant_ex(zend_string *class_name, zend_string *constant_name, zend_class_entry *scope, uint32_t flags) +{ + zend_class_entry *ce = NULL; + zend_class_constant *c = NULL; + zval *ret_constant = NULL; + + if (ZSTR_HAS_CE_CACHE(class_name)) { + ce = ZSTR_GET_CE_CACHE(class_name); + if (!ce) { + ce = zend_fetch_class(class_name, flags); + } + } else if (zend_string_equals_literal_ci(class_name, "self")) { + if (UNEXPECTED(!scope)) { + zend_throw_error(NULL, "Cannot access \"self\" when no class scope is active"); + goto failure; + } + ce = scope; + } else if (zend_string_equals_literal_ci(class_name, "parent")) { + if (UNEXPECTED(!scope)) { + zend_throw_error(NULL, "Cannot access \"parent\" when no class scope is active"); + goto failure; + } else if (UNEXPECTED(!scope->parent)) { + zend_throw_error(NULL, "Cannot access \"parent\" when current class scope has no parent"); + goto failure; + } else { + ce = scope->parent; + } + } else if (zend_string_equals_literal_ci(class_name, "static")) { + ce = zend_get_called_scope(EG(current_execute_data)); + if (UNEXPECTED(!ce)) { + zend_throw_error(NULL, "Cannot access \"static\" when no class scope is active"); + goto failure; + } + } else { + ce = zend_fetch_class(class_name, flags); + } + if (ce) { + c = zend_hash_find_ptr(CE_CONSTANTS_TABLE(ce), constant_name); + if (c == NULL) { + if ((flags & ZEND_FETCH_CLASS_SILENT) == 0) { + zend_throw_error(NULL, "Undefined constant %s::%s", ZSTR_VAL(class_name), ZSTR_VAL(constant_name)); + goto failure; + } + ret_constant = NULL; + } else { + if (!zend_verify_const_access(c, scope)) { + if ((flags & ZEND_FETCH_CLASS_SILENT) == 0) { + zend_throw_error(NULL, "Cannot access %s constant %s::%s", zend_visibility_string(Z_ACCESS_FLAGS(c->value)), ZSTR_VAL(class_name), ZSTR_VAL(constant_name)); + } + goto failure; + } + ret_constant = &c->value; + } + } + + if (ret_constant && Z_TYPE_P(ret_constant) == IS_CONSTANT_AST) { + zend_result ret; + + if (IS_CONSTANT_VISITED(ret_constant)) { + zend_throw_error(NULL, "Cannot declare self-referencing constant %s::%s", ZSTR_VAL(class_name), ZSTR_VAL(constant_name)); + ret_constant = NULL; + goto failure; + } + + MARK_CONSTANT_VISITED(ret_constant); + ret = zval_update_constant_ex(ret_constant, c->ce); + RESET_CONSTANT_VISITED(ret_constant); + + if (UNEXPECTED(ret != SUCCESS)) { + ret_constant = NULL; + goto failure; + } + } +failure: + return ret_constant; +} + ZEND_API zval *zend_get_constant_ex(zend_string *cname, zend_class_entry *scope, uint32_t flags) { zend_constant *c; const char *colon; - zend_class_entry *ce = NULL; const char *name = ZSTR_VAL(cname); size_t name_len = ZSTR_LEN(cname); @@ -344,7 +420,14 @@ ZEND_API zval *zend_get_constant_ex(zend_string *cname, zend_class_entry *scope, int class_name_len = colon - name - 1; size_t const_name_len = name_len - class_name_len - 2; zend_string *constant_name = zend_string_init(colon + 1, const_name_len, 0); - zend_string *class_name = zend_string_init(name, class_name_len, 0); + zend_string *class_name = zend_string_init_interned(name, class_name_len, 0); + zval *ret_constant = zend_get_class_constant_ex(class_name, constant_name, scope, flags); + + zend_string_release_ex(class_name, 0); + zend_string_efree(constant_name); + return ret_constant; +/* + zend_class_entry *ce = NULL; zend_class_constant *c = NULL; zval *ret_constant = NULL; @@ -414,6 +497,7 @@ failure: zend_string_release_ex(class_name, 0); zend_string_efree(constant_name); return ret_constant; +*/ } /* non-class constant */ diff --git a/Zend/zend_constants.h b/Zend/zend_constants.h index 07235144470..97a523b1052 100644 --- a/Zend/zend_constants.h +++ b/Zend/zend_constants.h @@ -76,6 +76,7 @@ ZEND_API bool zend_verify_const_access(zend_class_constant *c, zend_class_entry ZEND_API zval *zend_get_constant(zend_string *name); ZEND_API zval *zend_get_constant_str(const char *name, size_t name_len); ZEND_API zval *zend_get_constant_ex(zend_string *name, zend_class_entry *scope, uint32_t flags); +ZEND_API zval *zend_get_class_constant_ex(zend_string *class_name, zend_string *constant_name, zend_class_entry *scope, uint32_t flags); ZEND_API void zend_register_bool_constant(const char *name, size_t name_len, bool bval, int flags, int module_number); ZEND_API void zend_register_null_constant(const char *name, size_t name_len, int flags, int module_number); ZEND_API void zend_register_long_constant(const char *name, size_t name_len, zend_long lval, int flags, int module_number); diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index dff25db31d6..a1f4e4a4136 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -786,6 +786,10 @@ static ZEND_COLD zend_string *zend_get_function_declaration( zend_ast *ast = Z_ASTVAL_P(zv); if (ast->kind == ZEND_AST_CONSTANT) { smart_str_append(&str, zend_ast_get_constant_name(ast)); + } else if (ast->kind == ZEND_AST_CLASS_CONST) { + smart_str_append(&str, zend_ast_get_str(ast->child[0])); + smart_str_appends(&str, "::"); + smart_str_append(&str, zend_ast_get_str(ast->child[1])); } else { smart_str_appends(&str, ""); } diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 0e14f225708..54e3c90bf50 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -2820,7 +2820,9 @@ ZEND_METHOD(ReflectionParameter, isDefaultValueConstant) if (Z_TYPE(default_value) == IS_CONSTANT_AST) { zend_ast *ast = Z_ASTVAL(default_value); - RETVAL_BOOL(ast->kind == ZEND_AST_CONSTANT || ast->kind == ZEND_AST_CONSTANT_CLASS); + RETVAL_BOOL(ast->kind == ZEND_AST_CONSTANT + || ast->kind == ZEND_AST_CONSTANT_CLASS + || ast->kind == ZEND_AST_CLASS_CONST); } else { RETVAL_FALSE; } @@ -2858,6 +2860,13 @@ ZEND_METHOD(ReflectionParameter, getDefaultValueConstantName) RETVAL_STR_COPY(zend_ast_get_constant_name(ast)); } else if (ast->kind == ZEND_AST_CONSTANT_CLASS) { RETVAL_STRINGL("__CLASS__", sizeof("__CLASS__")-1); + } else if (ast->kind == ZEND_AST_CLASS_CONST) { + zend_string *class_name = zend_ast_get_str(ast->child[0]); + zend_string *const_name = zend_ast_get_str(ast->child[1]); + RETVAL_NEW_STR(zend_string_concat3( + ZSTR_VAL(class_name), ZSTR_LEN(class_name), + "::", sizeof("::")-1, + ZSTR_VAL(const_name), ZSTR_LEN(const_name))); } else { RETVAL_NULL(); } diff --git a/sapi/phpdbg/phpdbg_utils.c b/sapi/phpdbg/phpdbg_utils.c index afe8f056325..eca5c31d4de 100644 --- a/sapi/phpdbg/phpdbg_utils.c +++ b/sapi/phpdbg/phpdbg_utils.c @@ -844,7 +844,8 @@ char *phpdbg_short_zval_print(zval *zv, int maxlen) /* {{{ */ zend_ast *ast = Z_ASTVAL_P(zv); if (ast->kind == ZEND_AST_CONSTANT - || ast->kind == ZEND_AST_CONSTANT_CLASS) { + || ast->kind == ZEND_AST_CONSTANT_CLASS + || ast->kind == ZEND_AST_CLASS_CONST) { decode = estrdup(""); } else { decode = estrdup("");