xref: /PHP-8.1/Zend/zend_enum.c (revision 93fc88e8)
1 /*
2    +----------------------------------------------------------------------+
3    | Zend Engine                                                          |
4    +----------------------------------------------------------------------+
5    | Copyright (c) Zend Technologies Ltd. (http://www.zend.com)           |
6    +----------------------------------------------------------------------+
7    | This source file is subject to version 2.00 of the Zend license,     |
8    | that is bundled with this package in the file LICENSE, and is        |
9    | available through the world-wide-web at the following url:           |
10    | http://www.zend.com/license/2_00.txt.                                |
11    | If you did not receive a copy of the Zend license and are unable to  |
12    | obtain it through the world-wide-web, please send a note to          |
13    | license@zend.com so we can mail you a copy immediately.              |
14    +----------------------------------------------------------------------+
15    | Authors: Ilija Tovilo <ilutov@php.net>                               |
16    +----------------------------------------------------------------------+
17 */
18 
19 #include "zend.h"
20 #include "zend_API.h"
21 #include "zend_compile.h"
22 #include "zend_enum_arginfo.h"
23 #include "zend_interfaces.h"
24 
25 #define ZEND_ENUM_DISALLOW_MAGIC_METHOD(propertyName, methodName) \
26 	do { \
27 		if (ce->propertyName) { \
28 			zend_error_noreturn(E_COMPILE_ERROR, "Enum may not include %s", methodName); \
29 		} \
30 	} while (0);
31 
32 ZEND_API zend_class_entry *zend_ce_unit_enum;
33 ZEND_API zend_class_entry *zend_ce_backed_enum;
34 
35 static zend_object_handlers enum_handlers;
36 
zend_enum_new(zval * result,zend_class_entry * ce,zend_string * case_name,zval * backing_value_zv)37 zend_object *zend_enum_new(zval *result, zend_class_entry *ce, zend_string *case_name, zval *backing_value_zv)
38 {
39 	zend_object *zobj = zend_objects_new(ce);
40 	ZVAL_OBJ(result, zobj);
41 
42 	ZVAL_STR_COPY(OBJ_PROP_NUM(zobj, 0), case_name);
43 	if (backing_value_zv != NULL) {
44 		ZVAL_COPY(OBJ_PROP_NUM(zobj, 1), backing_value_zv);
45 	}
46 
47 	zobj->handlers = &enum_handlers;
48 
49 	return zobj;
50 }
51 
zend_verify_enum_properties(zend_class_entry * ce)52 static void zend_verify_enum_properties(zend_class_entry *ce)
53 {
54 	zend_property_info *property_info;
55 
56 	ZEND_HASH_FOREACH_PTR(&ce->properties_info, property_info) {
57 		if (zend_string_equals_literal(property_info->name, "name")) {
58 			continue;
59 		}
60 		if (
61 			ce->enum_backing_type != IS_UNDEF
62 			&& zend_string_equals_literal(property_info->name, "value")
63 		) {
64 			continue;
65 		}
66 		// FIXME: File/line number for traits?
67 		zend_error_noreturn(E_COMPILE_ERROR, "Enum \"%s\" may not include properties",
68 			ZSTR_VAL(ce->name));
69 	} ZEND_HASH_FOREACH_END();
70 }
71 
zend_verify_enum_magic_methods(zend_class_entry * ce)72 static void zend_verify_enum_magic_methods(zend_class_entry *ce)
73 {
74 	// Only __get, __call and __invoke are allowed
75 
76 	ZEND_ENUM_DISALLOW_MAGIC_METHOD(constructor, "__construct");
77 	ZEND_ENUM_DISALLOW_MAGIC_METHOD(destructor, "__destruct");
78 	ZEND_ENUM_DISALLOW_MAGIC_METHOD(clone, "__clone");
79 	ZEND_ENUM_DISALLOW_MAGIC_METHOD(__get, "__get");
80 	ZEND_ENUM_DISALLOW_MAGIC_METHOD(__set, "__set");
81 	ZEND_ENUM_DISALLOW_MAGIC_METHOD(__unset, "__unset");
82 	ZEND_ENUM_DISALLOW_MAGIC_METHOD(__isset, "__isset");
83 	ZEND_ENUM_DISALLOW_MAGIC_METHOD(__tostring, "__toString");
84 	ZEND_ENUM_DISALLOW_MAGIC_METHOD(__debugInfo, "__debugInfo");
85 	ZEND_ENUM_DISALLOW_MAGIC_METHOD(__serialize, "__serialize");
86 	ZEND_ENUM_DISALLOW_MAGIC_METHOD(__unserialize, "__unserialize");
87 
88 	const char *forbidden_methods[] = {
89 		"__sleep",
90 		"__wakeup",
91 		"__set_state",
92 	};
93 
94 	uint32_t forbidden_methods_length = sizeof(forbidden_methods) / sizeof(forbidden_methods[0]);
95 	for (uint32_t i = 0; i < forbidden_methods_length; ++i) {
96 		const char *forbidden_method = forbidden_methods[i];
97 
98 		if (zend_hash_str_exists(&ce->function_table, forbidden_method, strlen(forbidden_method))) {
99 			zend_error_noreturn(E_COMPILE_ERROR, "Enum may not include magic method %s", forbidden_method);
100 		}
101 	}
102 }
103 
zend_verify_enum_interfaces(zend_class_entry * ce)104 static void zend_verify_enum_interfaces(zend_class_entry *ce)
105 {
106 	if (zend_class_implements_interface(ce, zend_ce_serializable)) {
107 		zend_error_noreturn(E_COMPILE_ERROR,
108 			"Enums may not implement the Serializable interface");
109 	}
110 }
111 
zend_verify_enum(zend_class_entry * ce)112 void zend_verify_enum(zend_class_entry *ce)
113 {
114 	zend_verify_enum_properties(ce);
115 	zend_verify_enum_magic_methods(ce);
116 	zend_verify_enum_interfaces(ce);
117 }
118 
zend_implement_unit_enum(zend_class_entry * interface,zend_class_entry * class_type)119 static int zend_implement_unit_enum(zend_class_entry *interface, zend_class_entry *class_type)
120 {
121 	if (class_type->ce_flags & ZEND_ACC_ENUM) {
122 		return SUCCESS;
123 	}
124 
125 	zend_error_noreturn(E_ERROR, "Non-enum class %s cannot implement interface %s",
126 		ZSTR_VAL(class_type->name),
127 		ZSTR_VAL(interface->name));
128 
129 	return FAILURE;
130 }
131 
zend_implement_backed_enum(zend_class_entry * interface,zend_class_entry * class_type)132 static int zend_implement_backed_enum(zend_class_entry *interface, zend_class_entry *class_type)
133 {
134 	if (!(class_type->ce_flags & ZEND_ACC_ENUM)) {
135 		zend_error_noreturn(E_ERROR, "Non-enum class %s cannot implement interface %s",
136 			ZSTR_VAL(class_type->name),
137 			ZSTR_VAL(interface->name));
138 		return FAILURE;
139 	}
140 
141 	if (class_type->enum_backing_type == IS_UNDEF) {
142 		zend_error_noreturn(E_ERROR, "Non-backed enum %s cannot implement interface %s",
143 			ZSTR_VAL(class_type->name),
144 			ZSTR_VAL(interface->name));
145 		return FAILURE;
146 	}
147 
148 	return SUCCESS;
149 }
150 
zend_register_enum_ce(void)151 void zend_register_enum_ce(void)
152 {
153 	zend_ce_unit_enum = register_class_UnitEnum();
154 	zend_ce_unit_enum->interface_gets_implemented = zend_implement_unit_enum;
155 
156 	zend_ce_backed_enum = register_class_BackedEnum(zend_ce_unit_enum);
157 	zend_ce_backed_enum->interface_gets_implemented = zend_implement_backed_enum;
158 
159 	memcpy(&enum_handlers, &std_object_handlers, sizeof(zend_object_handlers));
160 	enum_handlers.clone_obj = NULL;
161 	enum_handlers.compare = zend_objects_not_comparable;
162 }
163 
zend_enum_add_interfaces(zend_class_entry * ce)164 void zend_enum_add_interfaces(zend_class_entry *ce)
165 {
166 	uint32_t num_interfaces_before = ce->num_interfaces;
167 
168 	ce->num_interfaces++;
169 	if (ce->enum_backing_type != IS_UNDEF) {
170 		ce->num_interfaces++;
171 	}
172 
173 	ZEND_ASSERT(!(ce->ce_flags & ZEND_ACC_RESOLVED_INTERFACES));
174 
175 	ce->interface_names = erealloc(ce->interface_names, sizeof(zend_class_name) * ce->num_interfaces);
176 
177 	ce->interface_names[num_interfaces_before].name = zend_string_copy(zend_ce_unit_enum->name);
178 	ce->interface_names[num_interfaces_before].lc_name = zend_string_init("unitenum", sizeof("unitenum") - 1, 0);
179 
180 	if (ce->enum_backing_type != IS_UNDEF) {
181 		ce->interface_names[num_interfaces_before + 1].name = zend_string_copy(zend_ce_backed_enum->name);
182 		ce->interface_names[num_interfaces_before + 1].lc_name = zend_string_init("backedenum", sizeof("backedenum") - 1, 0);
183 	}
184 }
185 
ZEND_NAMED_FUNCTION(zend_enum_cases_func)186 static ZEND_NAMED_FUNCTION(zend_enum_cases_func)
187 {
188 	zend_class_entry *ce = execute_data->func->common.scope;
189 	zend_class_constant *c;
190 
191 	ZEND_PARSE_PARAMETERS_NONE();
192 
193 	array_init(return_value);
194 
195 	ZEND_HASH_FOREACH_PTR(CE_CONSTANTS_TABLE(ce), c) {
196 		if (!(ZEND_CLASS_CONST_FLAGS(c) & ZEND_CLASS_CONST_IS_CASE)) {
197 			continue;
198 		}
199 		zval *zv = &c->value;
200 		if (Z_TYPE_P(zv) == IS_CONSTANT_AST) {
201 			if (zval_update_constant_ex(zv, c->ce) == FAILURE) {
202 				RETURN_THROWS();
203 			}
204 		}
205 		Z_ADDREF_P(zv);
206 		zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), zv);
207 	} ZEND_HASH_FOREACH_END();
208 }
209 
zend_enum_from_base(INTERNAL_FUNCTION_PARAMETERS,bool try)210 static void zend_enum_from_base(INTERNAL_FUNCTION_PARAMETERS, bool try)
211 {
212 	zend_class_entry *ce = execute_data->func->common.scope;
213 	bool release_string = false;
214 	zend_string *string_key;
215 	zend_long long_key;
216 
217 	zval *case_name_zv;
218 	if (ce->enum_backing_type == IS_LONG) {
219 		ZEND_PARSE_PARAMETERS_START(1, 1)
220 			Z_PARAM_LONG(long_key)
221 		ZEND_PARSE_PARAMETERS_END();
222 
223 		case_name_zv = zend_hash_index_find(ce->backed_enum_table, long_key);
224 	} else {
225 		ZEND_ASSERT(ce->enum_backing_type == IS_STRING);
226 
227 		if (ZEND_ARG_USES_STRICT_TYPES()) {
228 			ZEND_PARSE_PARAMETERS_START(1, 1)
229 				Z_PARAM_STR(string_key)
230 			ZEND_PARSE_PARAMETERS_END();
231 		} else {
232 			// We allow long keys so that coercion to string doesn't happen implicitly. The JIT
233 			// skips deallocation of params that don't require it. In the case of from/tryFrom
234 			// passing int to from(int|string) looks like no coercion will happen, so the JIT
235 			// won't emit a dtor call. Thus we allocate/free the string manually.
236 			ZEND_PARSE_PARAMETERS_START(1, 1)
237 				Z_PARAM_STR_OR_LONG(string_key, long_key)
238 			ZEND_PARSE_PARAMETERS_END();
239 
240 			if (string_key == NULL) {
241 				release_string = true;
242 				string_key = zend_long_to_str(long_key);
243 			}
244 		}
245 
246 		case_name_zv = zend_hash_find(ce->backed_enum_table, string_key);
247 	}
248 
249 	if (case_name_zv == NULL) {
250 		if (try) {
251 			goto return_null;
252 		}
253 
254 		if (ce->enum_backing_type == IS_LONG) {
255 			zend_value_error(ZEND_LONG_FMT " is not a valid backing value for enum \"%s\"", long_key, ZSTR_VAL(ce->name));
256 		} else {
257 			ZEND_ASSERT(ce->enum_backing_type == IS_STRING);
258 			zend_value_error("\"%s\" is not a valid backing value for enum \"%s\"", ZSTR_VAL(string_key), ZSTR_VAL(ce->name));
259 		}
260 		goto throw;
261 	}
262 
263 	// TODO: We might want to store pointers to constants in backed_enum_table instead of names,
264 	// to make this lookup more efficient.
265 	ZEND_ASSERT(Z_TYPE_P(case_name_zv) == IS_STRING);
266 	zend_class_constant *c = zend_hash_find_ptr(CE_CONSTANTS_TABLE(ce), Z_STR_P(case_name_zv));
267 	ZEND_ASSERT(c != NULL);
268 	zval *case_zv = &c->value;
269 	if (Z_TYPE_P(case_zv) == IS_CONSTANT_AST) {
270 		if (zval_update_constant_ex(case_zv, c->ce) == FAILURE) {
271 			goto throw;
272 		}
273 	}
274 
275 	if (release_string) {
276 		zend_string_release(string_key);
277 	}
278 	RETURN_COPY(case_zv);
279 
280 throw:
281 	if (release_string) {
282 		zend_string_release(string_key);
283 	}
284 	RETURN_THROWS();
285 
286 return_null:
287 	if (release_string) {
288 		zend_string_release(string_key);
289 	}
290 	RETURN_NULL();
291 }
292 
ZEND_NAMED_FUNCTION(zend_enum_from_func)293 static ZEND_NAMED_FUNCTION(zend_enum_from_func)
294 {
295 	zend_enum_from_base(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
296 }
297 
ZEND_NAMED_FUNCTION(zend_enum_try_from_func)298 static ZEND_NAMED_FUNCTION(zend_enum_try_from_func)
299 {
300 	zend_enum_from_base(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
301 }
302 
zend_enum_register_funcs(zend_class_entry * ce)303 void zend_enum_register_funcs(zend_class_entry *ce)
304 {
305 	const uint32_t fn_flags =
306 		ZEND_ACC_PUBLIC|ZEND_ACC_STATIC|ZEND_ACC_HAS_RETURN_TYPE|ZEND_ACC_ARENA_ALLOCATED;
307 	zend_internal_function *cases_function =
308 		zend_arena_alloc(&CG(arena), sizeof(zend_internal_function));
309 	memset(cases_function, 0, sizeof(zend_internal_function));
310 	cases_function->type = ZEND_INTERNAL_FUNCTION;
311 	cases_function->module = EG(current_module);
312 	cases_function->handler = zend_enum_cases_func;
313 	cases_function->function_name = ZSTR_KNOWN(ZEND_STR_CASES);
314 	cases_function->scope = ce;
315 	cases_function->fn_flags = fn_flags;
316 	cases_function->arg_info = (zend_internal_arg_info *) (arginfo_class_UnitEnum_cases + 1);
317 	if (!zend_hash_add_ptr(&ce->function_table, ZSTR_KNOWN(ZEND_STR_CASES), cases_function)) {
318 		zend_error_noreturn(E_COMPILE_ERROR, "Cannot redeclare %s::cases()", ZSTR_VAL(ce->name));
319 	}
320 
321 	if (ce->enum_backing_type != IS_UNDEF) {
322 		zend_internal_function *from_function =
323 			zend_arena_alloc(&CG(arena), sizeof(zend_internal_function));
324 		memset(from_function, 0, sizeof(zend_internal_function));
325 		from_function->type = ZEND_INTERNAL_FUNCTION;
326 		from_function->module = EG(current_module);
327 		from_function->handler = zend_enum_from_func;
328 		from_function->function_name = ZSTR_KNOWN(ZEND_STR_FROM);
329 		from_function->scope = ce;
330 		from_function->fn_flags = fn_flags;
331 		from_function->num_args = 1;
332 		from_function->required_num_args = 1;
333 		from_function->arg_info = (zend_internal_arg_info *) (arginfo_class_BackedEnum_from + 1);
334 		if (!zend_hash_add_ptr(&ce->function_table, ZSTR_KNOWN(ZEND_STR_FROM), from_function)) {
335 			zend_error_noreturn(E_COMPILE_ERROR,
336 				"Cannot redeclare %s::from()", ZSTR_VAL(ce->name));
337 		}
338 
339 		zend_internal_function *try_from_function =
340 			zend_arena_alloc(&CG(arena), sizeof(zend_internal_function));
341 		memset(try_from_function, 0, sizeof(zend_internal_function));
342 		try_from_function->type = ZEND_INTERNAL_FUNCTION;
343 		try_from_function->module = EG(current_module);
344 		try_from_function->handler = zend_enum_try_from_func;
345 		try_from_function->function_name = ZSTR_KNOWN(ZEND_STR_TRYFROM);
346 		try_from_function->scope = ce;
347 		try_from_function->fn_flags = fn_flags;
348 		try_from_function->num_args = 1;
349 		try_from_function->required_num_args = 1;
350 		try_from_function->arg_info = (zend_internal_arg_info *) (arginfo_class_BackedEnum_tryFrom + 1);
351 		if (!zend_hash_add_ptr(
352 				&ce->function_table, ZSTR_KNOWN(ZEND_STR_TRYFROM_LOWERCASE), try_from_function)) {
353 			zend_error_noreturn(E_COMPILE_ERROR,
354 				"Cannot redeclare %s::tryFrom()", ZSTR_VAL(ce->name));
355 		}
356 	}
357 }
358 
zend_enum_register_props(zend_class_entry * ce)359 void zend_enum_register_props(zend_class_entry *ce)
360 {
361 	ce->ce_flags |= ZEND_ACC_NO_DYNAMIC_PROPERTIES;
362 
363 	zval name_default_value;
364 	ZVAL_UNDEF(&name_default_value);
365 	zend_type name_type = ZEND_TYPE_INIT_CODE(IS_STRING, 0, 0);
366 	zend_declare_typed_property(ce, ZSTR_KNOWN(ZEND_STR_NAME), &name_default_value, ZEND_ACC_PUBLIC | ZEND_ACC_READONLY, NULL, name_type);
367 
368 	if (ce->enum_backing_type != IS_UNDEF) {
369 		zval value_default_value;
370 		ZVAL_UNDEF(&value_default_value);
371 		zend_type value_type = ZEND_TYPE_INIT_CODE(ce->enum_backing_type, 0, 0);
372 		zend_declare_typed_property(ce, ZSTR_KNOWN(ZEND_STR_VALUE), &value_default_value, ZEND_ACC_PUBLIC | ZEND_ACC_READONLY, NULL, value_type);
373 	}
374 }
375 
376 static const zend_function_entry unit_enum_methods[] = {
377 	ZEND_NAMED_ME(cases, zend_enum_cases_func, arginfo_class_UnitEnum_cases, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
378 	ZEND_FE_END
379 };
380 
381 static const zend_function_entry backed_enum_methods[] = {
382 	ZEND_NAMED_ME(cases, zend_enum_cases_func, arginfo_class_UnitEnum_cases, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
383 	ZEND_NAMED_ME(from, zend_enum_from_func, arginfo_class_BackedEnum_from, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
384 	ZEND_NAMED_ME(tryFrom, zend_enum_try_from_func, arginfo_class_BackedEnum_tryFrom, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
385 	ZEND_FE_END
386 };
387 
zend_register_internal_enum(const char * name,zend_uchar type,const zend_function_entry * functions)388 ZEND_API zend_class_entry *zend_register_internal_enum(
389 	const char *name, zend_uchar type, const zend_function_entry *functions)
390 {
391 	ZEND_ASSERT(type == IS_UNDEF || type == IS_LONG || type == IS_STRING);
392 
393 	zend_class_entry tmp_ce;
394 	INIT_CLASS_ENTRY_EX(tmp_ce, name, strlen(name), functions);
395 
396 	zend_class_entry *ce = zend_register_internal_class(&tmp_ce);
397 	ce->ce_flags |= ZEND_ACC_ENUM;
398 	ce->enum_backing_type = type;
399 	if (type != IS_UNDEF) {
400 		ce->backed_enum_table = pemalloc(sizeof(HashTable), 1);
401 		zend_hash_init(ce->backed_enum_table, 0, NULL, ZVAL_PTR_DTOR, 1);
402 	}
403 
404 	zend_enum_register_props(ce);
405 	if (type == IS_UNDEF) {
406 		zend_register_functions(
407 			ce, unit_enum_methods, &ce->function_table, EG(current_module)->type);
408 		zend_class_implements(ce, 1, zend_ce_unit_enum);
409 	} else {
410 		zend_register_functions(
411 			ce, backed_enum_methods, &ce->function_table, EG(current_module)->type);
412 		zend_class_implements(ce, 1, zend_ce_backed_enum);
413 	}
414 
415 	return ce;
416 }
417 
create_enum_case_ast(zend_string * class_name,zend_string * case_name,zval * value)418 static zend_ast_ref *create_enum_case_ast(
419 		zend_string *class_name, zend_string *case_name, zval *value) {
420 	// TODO: Use custom node type for enum cases?
421 	size_t size = sizeof(zend_ast_ref) + zend_ast_size(3)
422 		+ (value ? 3 : 2) * sizeof(zend_ast_zval);
423 	char *p = pemalloc(size, 1);
424 	zend_ast_ref *ref = (zend_ast_ref *) p; p += sizeof(zend_ast_ref);
425 	GC_SET_REFCOUNT(ref, 1);
426 	GC_TYPE_INFO(ref) = GC_CONSTANT_AST | GC_PERSISTENT | GC_IMMUTABLE;
427 
428 	zend_ast *ast = (zend_ast *) p; p += zend_ast_size(3);
429 	ast->kind = ZEND_AST_CONST_ENUM_INIT;
430 	ast->attr = 0;
431 	ast->lineno = 0;
432 
433 	ast->child[0] = (zend_ast *) p; p += sizeof(zend_ast_zval);
434 	ast->child[0]->kind = ZEND_AST_ZVAL;
435 	ast->child[0]->attr = 0;
436 	ZEND_ASSERT(ZSTR_IS_INTERNED(class_name));
437 	ZVAL_STR(zend_ast_get_zval(ast->child[0]), class_name);
438 
439 	ast->child[1] = (zend_ast *) p; p += sizeof(zend_ast_zval);
440 	ast->child[1]->kind = ZEND_AST_ZVAL;
441 	ast->child[1]->attr = 0;
442 	ZEND_ASSERT(ZSTR_IS_INTERNED(case_name));
443 	ZVAL_STR(zend_ast_get_zval(ast->child[1]), case_name);
444 
445 	if (value) {
446 		ast->child[2] = (zend_ast *) p; p += sizeof(zend_ast_zval);
447 		ast->child[2]->kind = ZEND_AST_ZVAL;
448 		ast->child[2]->attr = 0;
449 		ZEND_ASSERT(!Z_REFCOUNTED_P(value));
450 		ZVAL_COPY_VALUE(zend_ast_get_zval(ast->child[2]), value);
451 	} else {
452 		ast->child[2] = NULL;
453 	}
454 
455 	return ref;
456 }
457 
zend_enum_add_case(zend_class_entry * ce,zend_string * case_name,zval * value)458 ZEND_API void zend_enum_add_case(zend_class_entry *ce, zend_string *case_name, zval *value)
459 {
460 	if (value) {
461 		ZEND_ASSERT(ce->enum_backing_type == Z_TYPE_P(value));
462 		if (Z_TYPE_P(value) == IS_STRING && !ZSTR_IS_INTERNED(Z_STR_P(value))) {
463 			zval_make_interned_string(value);
464 		}
465 
466 		zval case_name_zv;
467 		ZVAL_STR(&case_name_zv, case_name);
468 		if (Z_TYPE_P(value) == IS_LONG) {
469 			zend_hash_index_add_new(ce->backed_enum_table, Z_LVAL_P(value), &case_name_zv);
470 		} else {
471 			zend_hash_add_new(ce->backed_enum_table, Z_STR_P(value), &case_name_zv);
472 		}
473 	} else {
474 		ZEND_ASSERT(ce->enum_backing_type == IS_UNDEF);
475 	}
476 
477 	zval ast_zv;
478 	Z_TYPE_INFO(ast_zv) = IS_CONSTANT_AST;
479 	Z_AST(ast_zv) = create_enum_case_ast(ce->name, case_name, value);
480 	zend_class_constant *c = zend_declare_class_constant_ex(
481 		ce, case_name, &ast_zv, ZEND_ACC_PUBLIC, NULL);
482 	ZEND_CLASS_CONST_FLAGS(c) |= ZEND_CLASS_CONST_IS_CASE;
483 }
484 
zend_enum_add_case_cstr(zend_class_entry * ce,const char * name,zval * value)485 ZEND_API void zend_enum_add_case_cstr(zend_class_entry *ce, const char *name, zval *value)
486 {
487 	zend_string *name_str = zend_string_init_interned(name, strlen(name), 1);
488 	zend_enum_add_case(ce, name_str, value);
489 	zend_string_release(name_str);
490 }
491 
zend_enum_get_case(zend_class_entry * ce,zend_string * name)492 ZEND_API zend_object *zend_enum_get_case(zend_class_entry *ce, zend_string *name) {
493 	zend_class_constant *c = zend_hash_find_ptr(CE_CONSTANTS_TABLE(ce), name);
494 	ZEND_ASSERT(ZEND_CLASS_CONST_FLAGS(c) & ZEND_CLASS_CONST_IS_CASE);
495 
496 	if (Z_TYPE(c->value) == IS_CONSTANT_AST) {
497 		if (zval_update_constant_ex(&c->value, c->ce) == FAILURE) {
498 			ZEND_UNREACHABLE();
499 		}
500 	}
501 	ZEND_ASSERT(Z_TYPE(c->value) == IS_OBJECT);
502 	return Z_OBJ(c->value);
503 }
504 
zend_enum_get_case_cstr(zend_class_entry * ce,const char * name)505 ZEND_API zend_object *zend_enum_get_case_cstr(zend_class_entry *ce, const char *name) {
506 	zend_string *name_str = zend_string_init(name, strlen(name), 0);
507 	zend_object *result = zend_enum_get_case(ce, name_str);
508 	zend_string_release(name_str);
509 	return result;
510 }
511