Skip to content

Commit 3ae7a00

Browse files
committed
Some fixes and cleanup
1 parent 9549133 commit 3ae7a00

File tree

7 files changed

+87
-91
lines changed

7 files changed

+87
-91
lines changed
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
--TEST--
2+
Enum must not implement Serializable indirectly
3+
--FILE--
4+
<?php
5+
6+
interface MySerializable extends Serializable {}
7+
8+
enum Foo implements MySerializable {
9+
case Bar;
10+
11+
public function serialize() {
12+
return serialize('Hello');
13+
}
14+
15+
public function unserialize($data) {
16+
return unserialize($data);
17+
}
18+
}
19+
20+
var_dump(unserialize(serialize(Foo::Bar)));
21+
22+
?>
23+
--EXPECTF--
24+
Fatal error: Enums may not implement the Serializable interface in %s on line %d
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
--TEST--
2+
Non-backed enum errors when case has int expression value
3+
--FILE--
4+
<?php
5+
6+
enum Foo {
7+
case Bar = 1 + 1;
8+
}
9+
10+
?>
11+
--EXPECTF--
12+
Fatal error: Case Bar of non-backed enum Foo must not have a value, try adding ": int" to the enum declaration in %s on line %d

Zend/zend_ast.c

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -773,16 +773,15 @@ ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate(zval *result, zend_ast *ast
773773
zend_string *class_name = zend_ast_get_str(class_name_ast);
774774

775775
zend_ast *case_name_ast = ast->child[1];
776-
zval *case_name_zv = zend_ast_get_zval(case_name_ast);
776+
zend_string *case_name = zend_ast_get_str(case_name_ast);
777777

778778
zend_ast *case_value_ast = ast->child[2];
779779
zval *case_value_zv = case_value_ast != NULL
780780
? zend_ast_get_zval(case_value_ast)
781781
: NULL;
782782

783783
zend_class_entry *ce = zend_lookup_class(class_name);
784-
zend_enum_new(result, ce, Z_STR_P(case_name_zv), case_value_zv);
785-
784+
zend_enum_new(result, ce, case_name, case_value_zv);
786785
break;
787786
}
788787
default:

Zend/zend_compile.c

Lines changed: 18 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,6 @@ ZEND_API zend_executor_globals executor_globals;
8989

9090
static zend_op *zend_emit_op(znode *result, zend_uchar opcode, znode *op1, znode *op2);
9191
static bool zend_try_ct_eval_array(zval *result, zend_ast *ast);
92-
zend_bool zend_is_allowed_in_const_expr(zend_ast_kind kind);
9392

9493
static void init_op(zend_op *op)
9594
{
@@ -4629,7 +4628,7 @@ void zend_compile_static_call(znode *result, zend_ast *ast, uint32_t type) /* {{
46294628
}
46304629
/* }}} */
46314630

4632-
zend_class_entry *zend_compile_class_decl(znode *result, zend_ast *ast, bool toplevel);
4631+
void zend_compile_class_decl(znode *result, zend_ast *ast, bool toplevel);
46334632

46344633
void zend_compile_new(znode *result, zend_ast *ast) /* {{{ */
46354634
{
@@ -7450,13 +7449,7 @@ static void zend_compile_enum_backing_type(zend_class_entry *ce, zend_ast *enum_
74507449
ZEND_ASSERT(ce->ce_flags & ZEND_ACC_ENUM);
74517450
zend_type type = zend_compile_typename(enum_backing_type_ast, 0);
74527451
uint32_t type_mask = ZEND_TYPE_PURE_MASK(type);
7453-
if (
7454-
ZEND_TYPE_HAS_CLASS(type)
7455-
|| (
7456-
type_mask != MAY_BE_LONG
7457-
&& type_mask != MAY_BE_STRING
7458-
)
7459-
) {
7452+
if (ZEND_TYPE_HAS_CLASS(type) || (type_mask != MAY_BE_LONG && type_mask != MAY_BE_STRING)) {
74607453
zend_string *type_string = zend_type_to_string(type);
74617454
zend_error_noreturn(E_COMPILE_ERROR,
74627455
"Enum backing type must be int or string, %s given",
@@ -7474,7 +7467,7 @@ static void zend_compile_enum_backing_type(zend_class_entry *ce, zend_ast *enum_
74747467
zend_hash_init(ce->backed_enum_table, 0, NULL, ZVAL_PTR_DTOR, 0);
74757468
}
74767469

7477-
zend_class_entry *zend_compile_class_decl(znode *result, zend_ast *ast, bool toplevel) /* {{{ */
7470+
void zend_compile_class_decl(znode *result, zend_ast *ast, bool toplevel) /* {{{ */
74787471
{
74797472
zend_ast_decl *decl = (zend_ast_decl *) ast;
74807473
zend_ast *extends_ast = decl->child[0];
@@ -7579,10 +7572,6 @@ zend_class_entry *zend_compile_class_decl(znode *result, zend_ast *ast, bool top
75797572
zend_verify_abstract_class(ce);
75807573
}
75817574

7582-
if (ce->ce_flags & ZEND_ACC_ENUM) {
7583-
zend_verify_enum(ce);
7584-
}
7585-
75867575
CG(active_class_entry) = original_ce;
75877576

75887577
if (toplevel) {
@@ -7605,15 +7594,15 @@ zend_class_entry *zend_compile_class_decl(znode *result, zend_ast *ast, bool top
76057594
if (zend_try_early_bind(ce, parent_ce, lcname, NULL)) {
76067595
CG(zend_lineno) = ast->lineno;
76077596
zend_string_release(lcname);
7608-
return ce;
7597+
return;
76097598
}
76107599
CG(zend_lineno) = ast->lineno;
76117600
}
76127601
} else if (EXPECTED(zend_hash_add_ptr(CG(class_table), lcname, ce) != NULL)) {
76137602
zend_string_release(lcname);
76147603
zend_build_properties_info_table(ce);
76157604
ce->ce_flags |= ZEND_ACC_LINKED;
7616-
return ce;
7605+
return;
76177606
}
76187607
} else if (!extends_ast) {
76197608
/* Link unbound simple class */
@@ -7667,8 +7656,6 @@ zend_class_entry *zend_compile_class_decl(znode *result, zend_ast *ast, bool top
76677656
opline->result.opline_num = -1;
76687657
}
76697658
}
7670-
7671-
return ce;
76727659
}
76737660
/* }}} */
76747661

@@ -7698,9 +7685,15 @@ static void zend_compile_enum_case(zend_ast *ast)
76987685
ZSTR_VAL(enum_class_name));
76997686
}
77007687
if (case_value_ast != NULL) {
7688+
zend_eval_const_expr(&ast->child[1]);
7689+
case_value_ast = ast->child[1];
7690+
if (case_value_ast->kind != ZEND_AST_ZVAL) {
7691+
zend_error_noreturn(E_COMPILE_ERROR, "Enum case value must be constant");
7692+
}
7693+
7694+
zval case_value_zv;
7695+
ZVAL_COPY(&case_value_zv, zend_ast_get_zval(case_value_ast));
77017696
if (enum_class->enum_backing_type == IS_UNDEF) {
7702-
zval case_value_zv;
7703-
ZVAL_COPY(&case_value_zv, zend_ast_get_zval(case_value_ast));
77047697
if (Z_TYPE(case_value_zv) == IS_LONG || Z_TYPE(case_value_zv) == IS_STRING) {
77057698
zend_error_noreturn(E_COMPILE_ERROR, "Case %s of non-backed enum %s must not have a value, try adding \": %s\" to the enum declaration",
77067699
ZSTR_VAL(enum_case_name),
@@ -7713,21 +7706,13 @@ static void zend_compile_enum_case(zend_ast *ast)
77137706
}
77147707
}
77157708

7716-
zend_eval_const_expr(&ast->child[1]);
7717-
case_value_ast = ast->child[1];
7718-
if (case_value_ast->kind != ZEND_AST_ZVAL) {
7719-
zend_error_noreturn(E_COMPILE_ERROR, "Enum case value must be constant");
7720-
}
7721-
7722-
zval case_value_zv;
7723-
ZVAL_COPY(&case_value_zv, zend_ast_get_zval(case_value_ast));
77247709
if (enum_class->enum_backing_type != Z_TYPE(case_value_zv)) {
77257710
zend_error_noreturn(E_COMPILE_ERROR, "Enum case type %s does not match enum backing type %s",
77267711
zend_get_type_by_const(Z_TYPE(case_value_zv)),
77277712
zend_get_type_by_const(enum_class->enum_backing_type));
77287713
}
7729-
case_value_zval_ast = zend_ast_create_zval(&case_value_zv);
77307714

7715+
case_value_zval_ast = zend_ast_create_zval(&case_value_zv);
77317716
Z_TRY_ADDREF(case_name_zval);
77327717
if (enum_class->enum_backing_type == IS_LONG) {
77337718
zend_long long_key = Z_LVAL(case_value_zv);
@@ -7738,18 +7723,18 @@ static void zend_compile_enum_case(zend_ast *ast)
77387723
Z_STRVAL_P(existing_case_name),
77397724
ZSTR_VAL(enum_case_name));
77407725
}
7741-
zend_hash_index_add(enum_class->backed_enum_table, long_key, &case_name_zval);
7726+
zend_hash_index_add_new(enum_class->backed_enum_table, long_key, &case_name_zval);
77427727
} else {
77437728
ZEND_ASSERT(enum_class->enum_backing_type == IS_STRING);
77447729
zend_string *string_key = Z_STR(case_value_zv);
7745-
zval *existing_case_name = zend_hash_find_ex(enum_class->backed_enum_table, string_key, 1);
7730+
zval *existing_case_name = zend_hash_find(enum_class->backed_enum_table, string_key);
77467731
if (existing_case_name != NULL) {
77477732
zend_error_noreturn(E_COMPILE_ERROR, "Duplicate value in enum %s for cases %s and %s",
77487733
ZSTR_VAL(enum_class_name),
77497734
Z_STRVAL_P(existing_case_name),
77507735
ZSTR_VAL(enum_case_name));
77517736
}
7752-
zend_hash_add(enum_class->backed_enum_table, string_key, &case_name_zval);
7737+
zend_hash_add_new(enum_class->backed_enum_table, string_key, &case_name_zval);
77537738
}
77547739
}
77557740

@@ -7759,13 +7744,12 @@ static void zend_compile_enum_case(zend_ast *ast)
77597744
zend_const_expr_to_zval(&value_zv, &const_enum_init_ast);
77607745
zend_class_constant *c = zend_declare_class_constant_ex(enum_class, enum_case_name, &value_zv, ZEND_ACC_PUBLIC, NULL);
77617746
Z_ACCESS_FLAGS(c->value) |= ZEND_CLASS_CONST_IS_CASE;
7747+
zend_ast_destroy(const_enum_init_ast);
77627748

77637749
zend_ast *attr_ast = ast->child[2];
77647750
if (attr_ast) {
77657751
zend_compile_attributes(&c->attributes, attr_ast, 0, ZEND_ATTRIBUTE_TARGET_CLASS_CONST);
77667752
}
7767-
7768-
zend_ast_destroy(const_enum_init_ast);
77697753
}
77707754

77717755
static HashTable *zend_get_import_ht(uint32_t type) /* {{{ */

Zend/zend_enum.c

Lines changed: 30 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "zend_API.h"
2121
#include "zend_compile.h"
2222
#include "zend_enum_arginfo.h"
23+
#include "zend_interfaces.h"
2324

2425
#define ZEND_ENUM_PROPERTY_ERROR() \
2526
zend_throw_error(NULL, "Enum properties are immutable")
@@ -87,7 +88,7 @@ static void zend_verify_enum_magic_methods(zend_class_entry *ce)
8788
ZEND_ENUM_DISALLOW_MAGIC_METHOD(__serialize, "__serialize");
8889
ZEND_ENUM_DISALLOW_MAGIC_METHOD(__unserialize, "__unserialize");
8990

90-
const char* forbidden_methods[] = {
91+
const char *forbidden_methods[] = {
9192
"__sleep",
9293
"__wakeup",
9394
"__set_state",
@@ -97,22 +98,17 @@ static void zend_verify_enum_magic_methods(zend_class_entry *ce)
9798
for (uint32_t i = 0; i < forbidden_methods_length; ++i) {
9899
const char *forbidden_method = forbidden_methods[i];
99100

100-
if (zend_hash_str_find_ptr(&ce->function_table, forbidden_method, strlen(forbidden_method))) {
101+
if (zend_hash_str_exists(&ce->function_table, forbidden_method, strlen(forbidden_method))) {
101102
zend_error_noreturn(E_COMPILE_ERROR, "Enum may not include magic method %s", forbidden_method);
102103
}
103104
}
104105
}
105106

106107
static void zend_verify_enum_interfaces(zend_class_entry *ce)
107108
{
108-
for (uint32_t i = 0; i < ce->num_interfaces; i++) {
109-
zend_string *interface_name = ce->ce_flags & ZEND_ACC_RESOLVED_INTERFACES
110-
? ce->interfaces[i]->name
111-
: ce->interface_names[i].lc_name;
112-
113-
if (zend_string_equals_literal(interface_name, "serializable")) {
114-
zend_error_noreturn(E_COMPILE_ERROR, "Enums may not implement the Serializable interface");
115-
}
109+
if (zend_class_implements_interface(ce, zend_ce_serializable)) {
110+
zend_error_noreturn(E_COMPILE_ERROR,
111+
"Enums may not implement the Serializable interface");
116112
}
117113
}
118114

@@ -125,11 +121,7 @@ void zend_verify_enum(zend_class_entry *ce)
125121

126122
static zval *zend_enum_read_property(zend_object *zobj, zend_string *name, int type, void **cache_slot, zval *rv) /* {{{ */
127123
{
128-
if (
129-
type == BP_VAR_W
130-
|| type == BP_VAR_RW
131-
|| type == BP_VAR_UNSET
132-
) {
124+
if (type == BP_VAR_W || type == BP_VAR_RW || type == BP_VAR_UNSET) {
133125
zend_throw_error(NULL, "Cannot acquire reference to property %s::$%s", ZSTR_VAL(zobj->ce->name), ZSTR_VAL(name));
134126
return &EG(uninitialized_zval);
135127
}
@@ -153,15 +145,6 @@ static zval *zend_enum_get_property_ptr_ptr(zend_object *zobj, zend_string *name
153145
return NULL;
154146
}
155147

156-
static int zend_enum_compare_objects(zval *o1, zval *o2)
157-
{
158-
if (Z_TYPE_P(o1) != IS_OBJECT || Z_TYPE_P(o2) != IS_OBJECT) {
159-
return ZEND_UNCOMPARABLE;
160-
}
161-
162-
return Z_OBJ_P(o1) == Z_OBJ_P(o2) ? 0 : 1;
163-
}
164-
165148
static int zend_implement_unit_enum(zend_class_entry *interface, zend_class_entry *class_type)
166149
{
167150
if (class_type->ce_flags & ZEND_ACC_ENUM) {
@@ -208,7 +191,7 @@ void zend_register_enum_ce(void)
208191
enum_handlers.unset_property = zend_enum_unset_property;
209192
enum_handlers.get_property_ptr_ptr = zend_enum_get_property_ptr_ptr;
210193
enum_handlers.clone_obj = NULL;
211-
enum_handlers.compare = zend_enum_compare_objects;
194+
enum_handlers.compare = zend_objects_not_comparable;
212195
}
213196

214197
void zend_enum_add_interfaces(zend_class_entry *ce)
@@ -220,15 +203,15 @@ void zend_enum_add_interfaces(zend_class_entry *ce)
220203
ce->num_interfaces++;
221204
}
222205

223-
ZEND_ASSERT(!(ce->ce_flags & ZEND_ACC_LINKED));
206+
ZEND_ASSERT(!(ce->ce_flags & ZEND_ACC_RESOLVED_INTERFACES));
224207

225208
ce->interface_names = erealloc(ce->interface_names, sizeof(zend_class_name) * ce->num_interfaces);
226209

227-
ce->interface_names[num_interfaces_before].name = zend_string_init("UnitEnum", sizeof("UnitEnum") - 1, 0);
210+
ce->interface_names[num_interfaces_before].name = zend_string_copy(zend_ce_unit_enum->name);
228211
ce->interface_names[num_interfaces_before].lc_name = zend_string_init("unitenum", sizeof("unitenum") - 1, 0);
229212

230213
if (ce->enum_backing_type != IS_UNDEF) {
231-
ce->interface_names[num_interfaces_before + 1].name = zend_string_init("BackedEnum", sizeof("BackedEnum") - 1, 0);
214+
ce->interface_names[num_interfaces_before + 1].name = zend_string_copy(zend_ce_backed_enum->name);
232215
ce->interface_names[num_interfaces_before + 1].lc_name = zend_string_init("backedenum", sizeof("backedenum") - 1, 0);
233216
}
234217
}
@@ -238,9 +221,7 @@ static ZEND_NAMED_FUNCTION(zend_enum_cases_func)
238221
zend_class_entry *ce = execute_data->func->common.scope;
239222
zend_class_constant *c;
240223

241-
if (zend_parse_parameters_none() == FAILURE) {
242-
RETURN_THROWS();
243-
}
224+
ZEND_PARSE_PARAMETERS_NONE();
244225

245226
array_init(return_value);
246227

@@ -250,9 +231,8 @@ static ZEND_NAMED_FUNCTION(zend_enum_cases_func)
250231
}
251232
zval *zv = &c->value;
252233
if (Z_TYPE_P(zv) == IS_CONSTANT_AST) {
253-
zval_update_constant_ex(zv, c->ce);
254-
if (UNEXPECTED(EG(exception) != NULL)) {
255-
return;
234+
if (zval_update_constant_ex(zv, c->ce) == FAILURE) {
235+
RETURN_THROWS();
256236
}
257237
}
258238
Z_ADDREF_P(zv);
@@ -268,15 +248,15 @@ static void zend_enum_from_base(INTERNAL_FUNCTION_PARAMETERS, bool try)
268248

269249
zval *case_name_zv;
270250
if (ce->enum_backing_type == IS_LONG) {
271-
if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &long_key) == FAILURE) {
272-
RETURN_THROWS();
273-
}
251+
ZEND_PARSE_PARAMETERS_START(1, 1)
252+
Z_PARAM_LONG(long_key)
253+
ZEND_PARSE_PARAMETERS_END();
274254

275255
case_name_zv = zend_hash_index_find(ce->backed_enum_table, long_key);
276256
} else {
277-
if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &string_key) == FAILURE) {
278-
RETURN_THROWS();
279-
}
257+
ZEND_PARSE_PARAMETERS_START(1, 1)
258+
Z_PARAM_STR(string_key)
259+
ZEND_PARSE_PARAMETERS_END();
280260

281261
ZEND_ASSERT(ce->enum_backing_type == IS_STRING);
282262
case_name_zv = zend_hash_find(ce->backed_enum_table, string_key);
@@ -285,24 +265,25 @@ static void zend_enum_from_base(INTERNAL_FUNCTION_PARAMETERS, bool try)
285265
if (case_name_zv == NULL) {
286266
if (try) {
287267
RETURN_NULL();
268+
}
269+
270+
if (ce->enum_backing_type == IS_LONG) {
271+
zend_value_error(ZEND_LONG_FMT " is not a valid backing value for enum \"%s\"", long_key, ZSTR_VAL(ce->name));
288272
} else {
289-
if (ce->enum_backing_type == IS_LONG) {
290-
zend_value_error("%d is not a valid backing value for enum \"%s\"", (int) long_key, ZSTR_VAL(ce->name));
291-
} else {
292-
ZEND_ASSERT(ce->enum_backing_type == IS_STRING);
293-
zend_value_error("\"%s\" is not a valid backing value for enum \"%s\"", ZSTR_VAL(string_key), ZSTR_VAL(ce->name));
294-
}
295-
RETURN_THROWS();
273+
ZEND_ASSERT(ce->enum_backing_type == IS_STRING);
274+
zend_value_error("\"%s\" is not a valid backing value for enum \"%s\"", ZSTR_VAL(string_key), ZSTR_VAL(ce->name));
296275
}
276+
RETURN_THROWS();
297277
}
298278

279+
// TODO: We might want to store pointers to constants in backed_enum_table instead of names,
280+
// to make this lookup more efficient.
299281
ZEND_ASSERT(Z_TYPE_P(case_name_zv) == IS_STRING);
300282
zend_class_constant *c = zend_hash_find_ptr(CE_CONSTANTS_TABLE(ce), Z_STR_P(case_name_zv));
301283
ZEND_ASSERT(c != NULL);
302284
zval *case_zv = &c->value;
303285
if (Z_TYPE_P(case_zv) == IS_CONSTANT_AST) {
304-
zval_update_constant_ex(case_zv, c->ce);
305-
if (UNEXPECTED(EG(exception) != NULL)) {
286+
if (zval_update_constant_ex(case_zv, c->ce) == FAILURE) {
306287
RETURN_THROWS();
307288
}
308289
}

0 commit comments

Comments
 (0)