php-src/Zend/zend_enum.c
Christoph M. Becker bf1cfc0753
Revert GH-10300
Cf. <https://github.com/php/php-src/pull/10220#issuecomment-1383739816>.

This reverts commit 68ada76f9a.
his reverts commit 45384c6e20.
This reverts commit ef7fbfd710.
This reverts commit 9b9ea0d7c6.
This reverts commit f15747c26b.
This reverts commit e883ba93c4.
This reverts commit 7e87551c37.
This reverts commit 921274d2b8.
This reverts commit fc1f528e5e.
This reverts commit 0961715cda.
This reverts commit a93f264526.
This reverts commit 72dd94e1c6.
This reverts commit 29b2dc8964.
This reverts commit 05c7653bba.
This reverts commit 5190e5c260.
This reverts commit 6b55bf228c.
This reverts commit 184b4a12d3.
This reverts commit 4c31b7888a.
This reverts commit d44e9680f0.
This reverts commit 4069a5c43f.
2023-01-16 12:22:54 +01:00

605 lines
20 KiB
C

/*
+----------------------------------------------------------------------+
| Zend Engine |
+----------------------------------------------------------------------+
| Copyright (c) Zend Technologies Ltd. (http://www.zend.com) |
+----------------------------------------------------------------------+
| This source file is subject to version 2.00 of the Zend license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.zend.com/license/2_00.txt. |
| If you did not receive a copy of the Zend license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@zend.com so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Ilija Tovilo <ilutov@php.net> |
+----------------------------------------------------------------------+
*/
#include "zend.h"
#include "zend_API.h"
#include "zend_compile.h"
#include "zend_enum_arginfo.h"
#include "zend_interfaces.h"
#include "zend_enum.h"
#include "zend_extensions.h"
#include "zend_observer.h"
#define ZEND_ENUM_DISALLOW_MAGIC_METHOD(propertyName, methodName) \
do { \
if (ce->propertyName) { \
zend_error_noreturn(E_COMPILE_ERROR, "Enum %s cannot include magic method %s", ZSTR_VAL(ce->name), methodName); \
} \
} while (0);
ZEND_API zend_class_entry *zend_ce_unit_enum;
ZEND_API zend_class_entry *zend_ce_backed_enum;
ZEND_API zend_object_handlers zend_enum_object_handlers;
zend_object *zend_enum_new(zval *result, zend_class_entry *ce, zend_string *case_name, zval *backing_value_zv)
{
zend_object *zobj = zend_objects_new(ce);
ZVAL_OBJ(result, zobj);
ZVAL_STR_COPY(OBJ_PROP_NUM(zobj, 0), case_name);
if (backing_value_zv != NULL) {
ZVAL_COPY(OBJ_PROP_NUM(zobj, 1), backing_value_zv);
}
return zobj;
}
static void zend_verify_enum_properties(zend_class_entry *ce)
{
zend_property_info *property_info;
ZEND_HASH_MAP_FOREACH_PTR(&ce->properties_info, property_info) {
if (zend_string_equals_literal(property_info->name, "name")) {
continue;
}
if (
ce->enum_backing_type != IS_UNDEF
&& zend_string_equals_literal(property_info->name, "value")
) {
continue;
}
// FIXME: File/line number for traits?
zend_error_noreturn(E_COMPILE_ERROR, "Enum %s cannot include properties",
ZSTR_VAL(ce->name));
} ZEND_HASH_FOREACH_END();
}
static void zend_verify_enum_magic_methods(zend_class_entry *ce)
{
// Only __get, __call and __invoke are allowed
ZEND_ENUM_DISALLOW_MAGIC_METHOD(constructor, "__construct");
ZEND_ENUM_DISALLOW_MAGIC_METHOD(destructor, "__destruct");
ZEND_ENUM_DISALLOW_MAGIC_METHOD(clone, "__clone");
ZEND_ENUM_DISALLOW_MAGIC_METHOD(__get, "__get");
ZEND_ENUM_DISALLOW_MAGIC_METHOD(__set, "__set");
ZEND_ENUM_DISALLOW_MAGIC_METHOD(__unset, "__unset");
ZEND_ENUM_DISALLOW_MAGIC_METHOD(__isset, "__isset");
ZEND_ENUM_DISALLOW_MAGIC_METHOD(__tostring, "__toString");
ZEND_ENUM_DISALLOW_MAGIC_METHOD(__debugInfo, "__debugInfo");
ZEND_ENUM_DISALLOW_MAGIC_METHOD(__serialize, "__serialize");
ZEND_ENUM_DISALLOW_MAGIC_METHOD(__unserialize, "__unserialize");
const char *forbidden_methods[] = {
"__sleep",
"__wakeup",
"__set_state",
};
uint32_t forbidden_methods_length = sizeof(forbidden_methods) / sizeof(forbidden_methods[0]);
for (uint32_t i = 0; i < forbidden_methods_length; ++i) {
const char *forbidden_method = forbidden_methods[i];
if (zend_hash_str_exists(&ce->function_table, forbidden_method, strlen(forbidden_method))) {
zend_error_noreturn(E_COMPILE_ERROR, "Enum %s cannot include magic method %s", ZSTR_VAL(ce->name), forbidden_method);
}
}
}
static void zend_verify_enum_interfaces(zend_class_entry *ce)
{
if (zend_class_implements_interface(ce, zend_ce_serializable)) {
zend_error_noreturn(E_COMPILE_ERROR,
"Enum %s cannot implement the Serializable interface", ZSTR_VAL(ce->name));
}
}
void zend_verify_enum(zend_class_entry *ce)
{
zend_verify_enum_properties(ce);
zend_verify_enum_magic_methods(ce);
zend_verify_enum_interfaces(ce);
}
static int zend_implement_unit_enum(zend_class_entry *interface, zend_class_entry *class_type)
{
if (class_type->ce_flags & ZEND_ACC_ENUM) {
return SUCCESS;
}
zend_error_noreturn(E_ERROR, "Non-enum class %s cannot implement interface %s",
ZSTR_VAL(class_type->name),
ZSTR_VAL(interface->name));
return FAILURE;
}
static int zend_implement_backed_enum(zend_class_entry *interface, zend_class_entry *class_type)
{
if (!(class_type->ce_flags & ZEND_ACC_ENUM)) {
zend_error_noreturn(E_ERROR, "Non-enum class %s cannot implement interface %s",
ZSTR_VAL(class_type->name),
ZSTR_VAL(interface->name));
return FAILURE;
}
if (class_type->enum_backing_type == IS_UNDEF) {
zend_error_noreturn(E_ERROR, "Non-backed enum %s cannot implement interface %s",
ZSTR_VAL(class_type->name),
ZSTR_VAL(interface->name));
return FAILURE;
}
return SUCCESS;
}
void zend_register_enum_ce(void)
{
zend_ce_unit_enum = register_class_UnitEnum();
zend_ce_unit_enum->interface_gets_implemented = zend_implement_unit_enum;
zend_ce_backed_enum = register_class_BackedEnum(zend_ce_unit_enum);
zend_ce_backed_enum->interface_gets_implemented = zend_implement_backed_enum;
memcpy(&zend_enum_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
zend_enum_object_handlers.clone_obj = NULL;
zend_enum_object_handlers.compare = zend_objects_not_comparable;
}
void zend_enum_add_interfaces(zend_class_entry *ce)
{
uint32_t num_interfaces_before = ce->num_interfaces;
ce->num_interfaces++;
if (ce->enum_backing_type != IS_UNDEF) {
ce->num_interfaces++;
}
ZEND_ASSERT(!(ce->ce_flags & ZEND_ACC_RESOLVED_INTERFACES));
ce->interface_names = erealloc(ce->interface_names, sizeof(zend_class_name) * ce->num_interfaces);
ce->interface_names[num_interfaces_before].name = zend_string_copy(zend_ce_unit_enum->name);
ce->interface_names[num_interfaces_before].lc_name = zend_string_init("unitenum", sizeof("unitenum") - 1, 0);
if (ce->enum_backing_type != IS_UNDEF) {
ce->interface_names[num_interfaces_before + 1].name = zend_string_copy(zend_ce_backed_enum->name);
ce->interface_names[num_interfaces_before + 1].lc_name = zend_string_init("backedenum", sizeof("backedenum") - 1, 0);
}
ce->default_object_handlers = &zend_enum_object_handlers;
}
zend_result zend_enum_build_backed_enum_table(zend_class_entry *ce)
{
ZEND_ASSERT(ce->ce_flags & ZEND_ACC_ENUM);
ZEND_ASSERT(ce->type == ZEND_USER_CLASS);
uint32_t backing_type = ce->enum_backing_type;
ZEND_ASSERT(backing_type != IS_UNDEF);
HashTable *backed_enum_table = emalloc(sizeof(HashTable));
zend_hash_init(backed_enum_table, 0, NULL, ZVAL_PTR_DTOR, 0);
zend_class_set_backed_enum_table(ce, backed_enum_table);
zend_string *enum_class_name = ce->name;
zend_string *name;
zval *val;
ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(CE_CONSTANTS_TABLE(ce), name, val) {
zend_class_constant *c = Z_PTR_P(val);
if ((ZEND_CLASS_CONST_FLAGS(c) & ZEND_CLASS_CONST_IS_CASE) == 0) {
continue;
}
zval *c_value = &c->value;
zval *case_name = zend_enum_fetch_case_name(Z_OBJ_P(c_value));
zval *case_value = zend_enum_fetch_case_value(Z_OBJ_P(c_value));
if (ce->enum_backing_type != Z_TYPE_P(case_value)) {
zend_type_error("Enum case type %s does not match enum backing type %s",
zend_get_type_by_const(Z_TYPE_P(case_value)),
zend_get_type_by_const(ce->enum_backing_type));
goto failure;
}
if (ce->enum_backing_type == IS_LONG) {
zend_long long_key = Z_LVAL_P(case_value);
zval *existing_case_name = zend_hash_index_find(backed_enum_table, long_key);
if (existing_case_name) {
zend_throw_error(NULL, "Duplicate value in enum %s for cases %s and %s",
ZSTR_VAL(enum_class_name),
Z_STRVAL_P(existing_case_name),
ZSTR_VAL(name));
goto failure;
}
zend_hash_index_add_new(backed_enum_table, long_key, case_name);
} else {
ZEND_ASSERT(ce->enum_backing_type == IS_STRING);
zend_string *string_key = Z_STR_P(case_value);
zval *existing_case_name = zend_hash_find(backed_enum_table, string_key);
if (existing_case_name != NULL) {
zend_throw_error(NULL, "Duplicate value in enum %s for cases %s and %s",
ZSTR_VAL(enum_class_name),
Z_STRVAL_P(existing_case_name),
ZSTR_VAL(name));
goto failure;
}
zend_hash_add_new(backed_enum_table, string_key, case_name);
}
} ZEND_HASH_FOREACH_END();
return SUCCESS;
failure:
zend_hash_release(backed_enum_table);
zend_class_set_backed_enum_table(ce, NULL);
return FAILURE;
}
static ZEND_NAMED_FUNCTION(zend_enum_cases_func)
{
zend_class_entry *ce = execute_data->func->common.scope;
zend_class_constant *c;
ZEND_PARSE_PARAMETERS_NONE();
array_init(return_value);
ZEND_HASH_MAP_FOREACH_PTR(CE_CONSTANTS_TABLE(ce), c) {
if (!(ZEND_CLASS_CONST_FLAGS(c) & ZEND_CLASS_CONST_IS_CASE)) {
continue;
}
zval *zv = &c->value;
if (Z_TYPE_P(zv) == IS_CONSTANT_AST) {
if (zval_update_constant_ex(zv, c->ce) == FAILURE) {
RETURN_THROWS();
}
}
Z_ADDREF_P(zv);
zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), zv);
} ZEND_HASH_FOREACH_END();
}
ZEND_API zend_result zend_enum_get_case_by_value(zend_object **result, zend_class_entry *ce, zend_long long_key, zend_string *string_key, bool try)
{
if (ce->type == ZEND_USER_CLASS && !(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) {
if (zend_update_class_constants(ce) == FAILURE) {
return FAILURE;
}
}
HashTable *backed_enum_table = CE_BACKED_ENUM_TABLE(ce);
if (!backed_enum_table) {
goto not_found;
}
zval *case_name_zv;
if (ce->enum_backing_type == IS_LONG) {
case_name_zv = zend_hash_index_find(backed_enum_table, long_key);
} else {
ZEND_ASSERT(ce->enum_backing_type == IS_STRING);
ZEND_ASSERT(string_key != NULL);
case_name_zv = zend_hash_find(backed_enum_table, string_key);
}
if (case_name_zv == NULL) {
not_found:
if (try) {
*result = NULL;
return SUCCESS;
}
if (ce->enum_backing_type == IS_LONG) {
zend_value_error(ZEND_LONG_FMT " is not a valid backing value for enum %s", long_key, ZSTR_VAL(ce->name));
} else {
ZEND_ASSERT(ce->enum_backing_type == IS_STRING);
zend_value_error("\"%s\" is not a valid backing value for enum %s", ZSTR_VAL(string_key), ZSTR_VAL(ce->name));
}
return FAILURE;
}
// TODO: We might want to store pointers to constants in backed_enum_table instead of names,
// to make this lookup more efficient.
ZEND_ASSERT(Z_TYPE_P(case_name_zv) == IS_STRING);
zend_class_constant *c = zend_hash_find_ptr(CE_CONSTANTS_TABLE(ce), Z_STR_P(case_name_zv));
ZEND_ASSERT(c != NULL);
zval *case_zv = &c->value;
if (Z_TYPE_P(case_zv) == IS_CONSTANT_AST) {
if (zval_update_constant_ex(case_zv, c->ce) == FAILURE) {
return FAILURE;
}
}
*result = Z_OBJ_P(case_zv);
return SUCCESS;
}
static void zend_enum_from_base(INTERNAL_FUNCTION_PARAMETERS, bool try)
{
zend_class_entry *ce = execute_data->func->common.scope;
bool release_string = false;
zend_string *string_key = NULL;
zend_long long_key = 0;
if (ce->enum_backing_type == IS_LONG) {
ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_LONG(long_key)
ZEND_PARSE_PARAMETERS_END();
} else {
ZEND_ASSERT(ce->enum_backing_type == IS_STRING);
if (ZEND_ARG_USES_STRICT_TYPES()) {
ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_STR(string_key)
ZEND_PARSE_PARAMETERS_END();
} else {
// We allow long keys so that coercion to string doesn't happen implicitly. The JIT
// skips deallocation of params that don't require it. In the case of from/tryFrom
// passing int to from(int|string) looks like no coercion will happen, so the JIT
// won't emit a dtor call. Thus we allocate/free the string manually.
ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_STR_OR_LONG(string_key, long_key)
ZEND_PARSE_PARAMETERS_END();
if (string_key == NULL) {
release_string = true;
string_key = zend_long_to_str(long_key);
}
}
}
zend_object *case_obj;
if (zend_enum_get_case_by_value(&case_obj, ce, long_key, string_key, try) == FAILURE) {
goto throw;
}
if (case_obj == NULL) {
ZEND_ASSERT(try);
goto return_null;
}
if (release_string) {
zend_string_release(string_key);
}
RETURN_OBJ_COPY(case_obj);
throw:
if (release_string) {
zend_string_release(string_key);
}
RETURN_THROWS();
return_null:
if (release_string) {
zend_string_release(string_key);
}
RETURN_NULL();
}
static ZEND_NAMED_FUNCTION(zend_enum_from_func)
{
zend_enum_from_base(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
}
static ZEND_NAMED_FUNCTION(zend_enum_try_from_func)
{
zend_enum_from_base(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
}
static void zend_enum_register_func(zend_class_entry *ce, zend_known_string_id name_id, zend_internal_function *zif) {
zend_string *name = ZSTR_KNOWN(name_id);
zif->type = ZEND_INTERNAL_FUNCTION;
zif->module = EG(current_module);
zif->scope = ce;
zif->T = ZEND_OBSERVER_ENABLED;
ZEND_MAP_PTR_NEW(zif->run_time_cache);
ZEND_MAP_PTR_SET(zif->run_time_cache, zend_arena_alloc(&CG(arena), zend_internal_run_time_cache_reserved_size()));
if (!zend_hash_add_ptr(&ce->function_table, name, zif)) {
zend_error_noreturn(E_COMPILE_ERROR, "Cannot redeclare %s::%s()", ZSTR_VAL(ce->name), ZSTR_VAL(name));
}
}
void zend_enum_register_funcs(zend_class_entry *ce)
{
const uint32_t fn_flags =
ZEND_ACC_PUBLIC|ZEND_ACC_STATIC|ZEND_ACC_HAS_RETURN_TYPE|ZEND_ACC_ARENA_ALLOCATED;
zend_internal_function *cases_function = zend_arena_calloc(&CG(arena), sizeof(zend_internal_function), 1);
cases_function->handler = zend_enum_cases_func;
cases_function->function_name = ZSTR_KNOWN(ZEND_STR_CASES);
cases_function->fn_flags = fn_flags;
cases_function->arg_info = (zend_internal_arg_info *) (arginfo_class_UnitEnum_cases + 1);
zend_enum_register_func(ce, ZEND_STR_CASES, cases_function);
if (ce->enum_backing_type != IS_UNDEF) {
zend_internal_function *from_function = zend_arena_calloc(&CG(arena), sizeof(zend_internal_function), 1);
from_function->handler = zend_enum_from_func;
from_function->function_name = ZSTR_KNOWN(ZEND_STR_FROM);
from_function->fn_flags = fn_flags;
from_function->num_args = 1;
from_function->required_num_args = 1;
from_function->arg_info = (zend_internal_arg_info *) (arginfo_class_BackedEnum_from + 1);
zend_enum_register_func(ce, ZEND_STR_FROM, from_function);
zend_internal_function *try_from_function = zend_arena_calloc(&CG(arena), sizeof(zend_internal_function), 1);
try_from_function->handler = zend_enum_try_from_func;
try_from_function->function_name = ZSTR_KNOWN(ZEND_STR_TRYFROM);
try_from_function->fn_flags = fn_flags;
try_from_function->num_args = 1;
try_from_function->required_num_args = 1;
try_from_function->arg_info = (zend_internal_arg_info *) (arginfo_class_BackedEnum_tryFrom + 1);
zend_enum_register_func(ce, ZEND_STR_TRYFROM_LOWERCASE, try_from_function);
}
}
void zend_enum_register_props(zend_class_entry *ce)
{
ce->ce_flags |= ZEND_ACC_NO_DYNAMIC_PROPERTIES;
zval name_default_value;
ZVAL_UNDEF(&name_default_value);
zend_type name_type = ZEND_TYPE_INIT_CODE(IS_STRING, 0, 0);
zend_declare_typed_property(ce, ZSTR_KNOWN(ZEND_STR_NAME), &name_default_value, ZEND_ACC_PUBLIC | ZEND_ACC_READONLY, NULL, name_type);
if (ce->enum_backing_type != IS_UNDEF) {
zval value_default_value;
ZVAL_UNDEF(&value_default_value);
zend_type value_type = ZEND_TYPE_INIT_CODE(ce->enum_backing_type, 0, 0);
zend_declare_typed_property(ce, ZSTR_KNOWN(ZEND_STR_VALUE), &value_default_value, ZEND_ACC_PUBLIC | ZEND_ACC_READONLY, NULL, value_type);
}
}
static const zend_function_entry unit_enum_methods[] = {
ZEND_NAMED_ME(cases, zend_enum_cases_func, arginfo_class_UnitEnum_cases, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
ZEND_FE_END
};
static const zend_function_entry backed_enum_methods[] = {
ZEND_NAMED_ME(cases, zend_enum_cases_func, arginfo_class_UnitEnum_cases, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
ZEND_NAMED_ME(from, zend_enum_from_func, arginfo_class_BackedEnum_from, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
ZEND_NAMED_ME(tryFrom, zend_enum_try_from_func, arginfo_class_BackedEnum_tryFrom, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
ZEND_FE_END
};
ZEND_API zend_class_entry *zend_register_internal_enum(
const char *name, zend_uchar type, const zend_function_entry *functions)
{
ZEND_ASSERT(type == IS_UNDEF || type == IS_LONG || type == IS_STRING);
zend_class_entry tmp_ce;
INIT_CLASS_ENTRY_EX(tmp_ce, name, strlen(name), functions);
zend_class_entry *ce = zend_register_internal_class(&tmp_ce);
ce->ce_flags |= ZEND_ACC_ENUM;
ce->enum_backing_type = type;
if (type != IS_UNDEF) {
HashTable *backed_enum_table = pemalloc(sizeof(HashTable), 1);
zend_hash_init(backed_enum_table, 0, NULL, ZVAL_PTR_DTOR, 1);
zend_class_set_backed_enum_table(ce, backed_enum_table);
}
zend_enum_register_props(ce);
if (type == IS_UNDEF) {
zend_register_functions(
ce, unit_enum_methods, &ce->function_table, EG(current_module)->type);
zend_class_implements(ce, 1, zend_ce_unit_enum);
} else {
zend_register_functions(
ce, backed_enum_methods, &ce->function_table, EG(current_module)->type);
zend_class_implements(ce, 1, zend_ce_backed_enum);
}
return ce;
}
static zend_ast_ref *create_enum_case_ast(
zend_string *class_name, zend_string *case_name, zval *value) {
// TODO: Use custom node type for enum cases?
size_t size = sizeof(zend_ast_ref) + zend_ast_size(3)
+ (value ? 3 : 2) * sizeof(zend_ast_zval);
char *p = pemalloc(size, 1);
zend_ast_ref *ref = (zend_ast_ref *) p; p += sizeof(zend_ast_ref);
GC_SET_REFCOUNT(ref, 1);
GC_TYPE_INFO(ref) = GC_CONSTANT_AST | GC_PERSISTENT | GC_IMMUTABLE;
zend_ast *ast = (zend_ast *) p; p += zend_ast_size(3);
ast->kind = ZEND_AST_CONST_ENUM_INIT;
ast->attr = 0;
ast->lineno = 0;
ast->child[0] = (zend_ast *) p; p += sizeof(zend_ast_zval);
ast->child[0]->kind = ZEND_AST_ZVAL;
ast->child[0]->attr = 0;
ZEND_ASSERT(ZSTR_IS_INTERNED(class_name));
ZVAL_STR(zend_ast_get_zval(ast->child[0]), class_name);
ast->child[1] = (zend_ast *) p; p += sizeof(zend_ast_zval);
ast->child[1]->kind = ZEND_AST_ZVAL;
ast->child[1]->attr = 0;
ZEND_ASSERT(ZSTR_IS_INTERNED(case_name));
ZVAL_STR(zend_ast_get_zval(ast->child[1]), case_name);
if (value) {
ast->child[2] = (zend_ast *) p; p += sizeof(zend_ast_zval);
ast->child[2]->kind = ZEND_AST_ZVAL;
ast->child[2]->attr = 0;
ZEND_ASSERT(!Z_REFCOUNTED_P(value));
ZVAL_COPY_VALUE(zend_ast_get_zval(ast->child[2]), value);
} else {
ast->child[2] = NULL;
}
return ref;
}
ZEND_API void zend_enum_add_case(zend_class_entry *ce, zend_string *case_name, zval *value)
{
if (value) {
ZEND_ASSERT(ce->enum_backing_type == Z_TYPE_P(value));
if (Z_TYPE_P(value) == IS_STRING && !ZSTR_IS_INTERNED(Z_STR_P(value))) {
zval_make_interned_string(value);
}
HashTable *backed_enum_table = CE_BACKED_ENUM_TABLE(ce);
zval case_name_zv;
ZVAL_STR(&case_name_zv, case_name);
if (Z_TYPE_P(value) == IS_LONG) {
zend_hash_index_add_new(backed_enum_table, Z_LVAL_P(value), &case_name_zv);
} else {
zend_hash_add_new(backed_enum_table, Z_STR_P(value), &case_name_zv);
}
} else {
ZEND_ASSERT(ce->enum_backing_type == IS_UNDEF);
}
zval ast_zv;
Z_TYPE_INFO(ast_zv) = IS_CONSTANT_AST;
Z_AST(ast_zv) = create_enum_case_ast(ce->name, case_name, value);
zend_class_constant *c = zend_declare_class_constant_ex(
ce, case_name, &ast_zv, ZEND_ACC_PUBLIC, NULL);
ZEND_CLASS_CONST_FLAGS(c) |= ZEND_CLASS_CONST_IS_CASE;
}
ZEND_API void zend_enum_add_case_cstr(zend_class_entry *ce, const char *name, zval *value)
{
zend_string *name_str = zend_string_init_interned(name, strlen(name), 1);
zend_enum_add_case(ce, name_str, value);
zend_string_release(name_str);
}
ZEND_API zend_object *zend_enum_get_case(zend_class_entry *ce, zend_string *name) {
zend_class_constant *c = zend_hash_find_ptr(CE_CONSTANTS_TABLE(ce), name);
ZEND_ASSERT(ZEND_CLASS_CONST_FLAGS(c) & ZEND_CLASS_CONST_IS_CASE);
if (Z_TYPE(c->value) == IS_CONSTANT_AST) {
if (zval_update_constant_ex(&c->value, c->ce) == FAILURE) {
ZEND_UNREACHABLE();
}
}
ZEND_ASSERT(Z_TYPE(c->value) == IS_OBJECT);
return Z_OBJ(c->value);
}
ZEND_API zend_object *zend_enum_get_case_cstr(zend_class_entry *ce, const char *name) {
zend_string *name_str = zend_string_init(name, strlen(name), 0);
zend_object *result = zend_enum_get_case(ce, name_str);
zend_string_release(name_str);
return result;
}