Skip to content

Commit d186354

Browse files
committed
Make backed enum errors non-fatal
1 parent 5e9aca2 commit d186354

File tree

8 files changed

+136
-46
lines changed

8 files changed

+136
-46
lines changed

Zend/tests/enum/backed-duplicate-int.phpt

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,33 @@ enum Foo: int {
88
case Baz = 0;
99
}
1010

11-
var_dump(Foo::Bar);
11+
try {
12+
var_dump(Foo::Bar);
13+
} catch (Error $e) {
14+
echo $e->getMessage(), "\n";
15+
}
16+
17+
try {
18+
var_dump(Foo::Bar);
19+
} catch (Error $e) {
20+
echo $e->getMessage(), "\n";
21+
}
22+
23+
try {
24+
var_dump(Foo::from(42));
25+
} catch (Error $e) {
26+
echo $e->getMessage(), "\n";
27+
}
28+
29+
try {
30+
var_dump(Foo::tryFrom('bar'));
31+
} catch (Error $e) {
32+
echo $e->getMessage(), "\n";
33+
}
1234

1335
?>
14-
--EXPECTF--
15-
Fatal error: Duplicate value in enum Foo for cases Bar and Baz in %s on line %s
36+
--EXPECT--
37+
Duplicate value in enum Foo for cases Bar and Baz
38+
Duplicate value in enum Foo for cases Bar and Baz
39+
Duplicate value in enum Foo for cases Bar and Baz
40+
Foo::tryFrom(): Argument #1 ($value) must be of type int, string given

Zend/tests/enum/backed-duplicate-string.phpt

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,33 @@ enum Suit: string {
1010
case Spades = 'H';
1111
}
1212

13-
var_dump(Suit::Hearts);
13+
try {
14+
var_dump(Suit::Hearts);
15+
} catch (Error $e) {
16+
echo $e->getMessage(), "\n";
17+
}
18+
19+
try {
20+
var_dump(Suit::Hearts);
21+
} catch (Error $e) {
22+
echo $e->getMessage(), "\n";
23+
}
24+
25+
try {
26+
var_dump(Suit::from(42));
27+
} catch (Error $e) {
28+
echo $e->getMessage(), "\n";
29+
}
30+
31+
try {
32+
var_dump(Suit::tryFrom('bar'));
33+
} catch (Error $e) {
34+
echo $e->getMessage(), "\n";
35+
}
1436

1537
?>
16-
--EXPECTF--
17-
Fatal error: Duplicate value in enum Suit for cases Hearts and Spades in %s on line %s
38+
--EXPECT--
39+
Duplicate value in enum Suit for cases Hearts and Spades
40+
Duplicate value in enum Suit for cases Hearts and Spades
41+
Duplicate value in enum Suit for cases Hearts and Spades
42+
Duplicate value in enum Suit for cases Hearts and Spades

Zend/tests/enum/backed-mismatch.phpt

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,33 @@ enum Foo: int {
77
case Bar = 'bar';
88
}
99

10-
var_dump(Foo::Bar);
10+
try {
11+
var_dump(Foo::Bar);
12+
} catch (Error $e) {
13+
echo $e->getMessage(), "\n";
14+
}
15+
16+
try {
17+
var_dump(Foo::Bar);
18+
} catch (Error $e) {
19+
echo $e->getMessage(), "\n";
20+
}
21+
22+
try {
23+
var_dump(Foo::from(42));
24+
} catch (Error $e) {
25+
echo $e->getMessage(), "\n";
26+
}
27+
28+
try {
29+
var_dump(Foo::from('bar'));
30+
} catch (Error $e) {
31+
echo $e->getMessage(), "\n";
32+
}
1133

1234
?>
13-
--EXPECTF--
14-
Fatal error: Enum case type string does not match enum backing type int in %s on line %d
35+
--EXPECT--
36+
Enum case type string does not match enum backing type int
37+
Enum case type string does not match enum backing type int
38+
Enum case type string does not match enum backing type int
39+
Foo::from(): Argument #1 ($value) must be of type int, string given

Zend/zend_API.c

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1494,6 +1494,12 @@ ZEND_API zend_result zend_update_class_constants(zend_class_entry *class_type) /
14941494
}
14951495
}
14961496

1497+
if (class_type->type == ZEND_USER_CLASS && class_type->ce_flags & ZEND_ACC_ENUM && class_type->enum_backing_type != IS_UNDEF) {
1498+
if (zend_enum_build_backed_enum_table(class_type) == FAILURE) {
1499+
return FAILURE;
1500+
}
1501+
}
1502+
14971503
ce_flags |= ZEND_ACC_CONSTANTS_UPDATED;
14981504
ce_flags &= ~ZEND_ACC_HAS_AST_CONSTANTS;
14991505
ce_flags &= ~ZEND_ACC_HAS_AST_PROPERTIES;
@@ -1504,10 +1510,6 @@ ZEND_API zend_result zend_update_class_constants(zend_class_entry *class_type) /
15041510
class_type->ce_flags = ce_flags;
15051511
}
15061512

1507-
if (class_type->type == ZEND_USER_CLASS && class_type->ce_flags & ZEND_ACC_ENUM && class_type->enum_backing_type != IS_UNDEF) {
1508-
zend_enum_build_backed_enum_table(class_type);
1509-
}
1510-
15111513
return SUCCESS;
15121514
}
15131515
/* }}} */

Zend/zend_enum.c

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
#include "zend_enum_arginfo.h"
2323
#include "zend_interfaces.h"
2424
#include "zend_enum.h"
25-
#include "zend_exceptions.h"
2625

2726
#define ZEND_ENUM_DISALLOW_MAGIC_METHOD(propertyName, methodName) \
2827
do { \
@@ -185,16 +184,14 @@ void zend_enum_add_interfaces(zend_class_entry *ce)
185184
}
186185
}
187186

188-
void zend_enum_build_backed_enum_table(zend_class_entry *ce)
187+
zend_result zend_enum_build_backed_enum_table(zend_class_entry *ce)
189188
{
190189
ZEND_ASSERT(ce->ce_flags & ZEND_ACC_ENUM);
191190
ZEND_ASSERT(ce->type == ZEND_USER_CLASS);
192191

193192
uint32_t backing_type = ce->enum_backing_type;
194193
ZEND_ASSERT(backing_type != IS_UNDEF);
195194

196-
ZEND_ASSERT(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED);
197-
198195
ce->backed_enum_table = emalloc(sizeof(HashTable));
199196
zend_hash_init(ce->backed_enum_table, 0, NULL, ZVAL_PTR_DTOR, 0);
200197

@@ -213,34 +210,44 @@ void zend_enum_build_backed_enum_table(zend_class_entry *ce)
213210
zval *case_value = zend_enum_fetch_case_value(Z_OBJ_P(c_value));
214211

215212
if (ce->enum_backing_type != Z_TYPE_P(case_value)) {
216-
zend_error_noreturn(E_COMPILE_ERROR, "Enum case type %s does not match enum backing type %s",
213+
zend_throw_error(NULL, "Enum case type %s does not match enum backing type %s",
217214
zend_get_type_by_const(Z_TYPE_P(case_value)),
218215
zend_get_type_by_const(ce->enum_backing_type));
216+
goto failure;
219217
}
220218

221219
if (ce->enum_backing_type == IS_LONG) {
222220
zend_long long_key = Z_LVAL_P(case_value);
223221
zval *existing_case_name = zend_hash_index_find(ce->backed_enum_table, long_key);
224222
if (existing_case_name) {
225-
zend_error_noreturn(E_COMPILE_ERROR, "Duplicate value in enum %s for cases %s and %s",
223+
zend_throw_error(NULL, "Duplicate value in enum %s for cases %s and %s",
226224
ZSTR_VAL(enum_class_name),
227225
Z_STRVAL_P(existing_case_name),
228226
ZSTR_VAL(name));
227+
goto failure;
229228
}
230229
zend_hash_index_add_new(ce->backed_enum_table, long_key, case_name);
231230
} else {
232231
ZEND_ASSERT(ce->enum_backing_type == IS_STRING);
233232
zend_string *string_key = Z_STR_P(case_value);
234233
zval *existing_case_name = zend_hash_find(ce->backed_enum_table, string_key);
235234
if (existing_case_name != NULL) {
236-
zend_error_noreturn(E_COMPILE_ERROR, "Duplicate value in enum %s for cases %s and %s",
235+
zend_throw_error(NULL, "Duplicate value in enum %s for cases %s and %s",
237236
ZSTR_VAL(enum_class_name),
238237
Z_STRVAL_P(existing_case_name),
239238
ZSTR_VAL(name));
239+
goto failure;
240240
}
241241
zend_hash_add_new(ce->backed_enum_table, string_key, case_name);
242242
}
243243
} ZEND_HASH_FOREACH_END();
244+
245+
return SUCCESS;
246+
247+
failure:
248+
zend_hash_release(ce->backed_enum_table);
249+
ce->backed_enum_table = NULL;
250+
return FAILURE;
244251
}
245252

246253
static ZEND_NAMED_FUNCTION(zend_enum_cases_func)
@@ -270,7 +277,9 @@ static ZEND_NAMED_FUNCTION(zend_enum_cases_func)
270277
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)
271278
{
272279
if (ce->type == ZEND_USER_CLASS && !(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) {
273-
zend_update_class_constants(ce);
280+
if (zend_update_class_constants(ce) == FAILURE) {
281+
return FAILURE;
282+
}
274283
}
275284

276285
zval *case_name_zv;

Zend/zend_enum.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ extern ZEND_API zend_class_entry *zend_ce_backed_enum;
2929

3030
void zend_register_enum_ce(void);
3131
void zend_enum_add_interfaces(zend_class_entry *ce);
32-
void zend_enum_build_backed_enum_table(zend_class_entry *ce);
32+
zend_result zend_enum_build_backed_enum_table(zend_class_entry *ce);
3333
zend_object *zend_enum_new(zval *result, zend_class_entry *ce, zend_string *case_name, zval *backing_value_zv);
3434
void zend_verify_enum(zend_class_entry *ce);
3535
void zend_enum_register_funcs(zend_class_entry *ce);

Zend/zend_vm_def.h

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5896,14 +5896,15 @@ ZEND_VM_HANDLER(181, ZEND_FETCH_CLASS_CONSTANT, VAR|CONST|UNUSED|CLASS_FETCH, CO
58965896
HANDLE_EXCEPTION();
58975897
}
58985898
value = &c->value;
5899+
// Enums require loading of all class constants to build the backed enum table
5900+
if (ce->ce_flags & ZEND_ACC_ENUM && ce->enum_backing_type != IS_UNDEF && ce->type == ZEND_USER_CLASS && !(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) {
5901+
if (UNEXPECTED(zend_update_class_constants(ce) == FAILURE)) {
5902+
HANDLE_EXCEPTION();
5903+
}
5904+
}
58995905
if (Z_TYPE_P(value) == IS_CONSTANT_AST) {
59005906
ce = c->ce;
5901-
if (ce->ce_flags & ZEND_ACC_ENUM && ce->type == ZEND_USER_CLASS && ce->enum_backing_type != IS_UNDEF) {
5902-
// Enums require loading of all class constants to build the backed enum table
5903-
zend_update_class_constants(ce);
5904-
} else {
5905-
zval_update_constant_ex(value, ce);
5906-
}
5907+
zval_update_constant_ex(value, ce);
59075908
if (UNEXPECTED(EG(exception) != NULL)) {
59085909
ZVAL_UNDEF(EX_VAR(opline->result.var));
59095910
HANDLE_EXCEPTION();

Zend/zend_vm_execute.h

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7065,14 +7065,15 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_CONS
70657065
HANDLE_EXCEPTION();
70667066
}
70677067
value = &c->value;
7068+
// Enums require loading of all class constants to build the backed enum table
7069+
if (ce->ce_flags & ZEND_ACC_ENUM && ce->enum_backing_type != IS_UNDEF && ce->type == ZEND_USER_CLASS && !(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) {
7070+
if (UNEXPECTED(zend_update_class_constants(ce) == FAILURE)) {
7071+
HANDLE_EXCEPTION();
7072+
}
7073+
}
70687074
if (Z_TYPE_P(value) == IS_CONSTANT_AST) {
70697075
ce = c->ce;
7070-
if (ce->ce_flags & ZEND_ACC_ENUM && ce->type == ZEND_USER_CLASS && ce->enum_backing_type != IS_UNDEF) {
7071-
// Enums require loading of all class constants to build the backed enum table
7072-
zend_update_class_constants(ce);
7073-
} else {
7074-
zval_update_constant_ex(value, ce);
7075-
}
7076+
zval_update_constant_ex(value, ce);
70767077
if (UNEXPECTED(EG(exception) != NULL)) {
70777078
ZVAL_UNDEF(EX_VAR(opline->result.var));
70787079
HANDLE_EXCEPTION();
@@ -24616,14 +24617,15 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_VAR_
2461624617
HANDLE_EXCEPTION();
2461724618
}
2461824619
value = &c->value;
24620+
// Enums require loading of all class constants to build the backed enum table
24621+
if (ce->ce_flags & ZEND_ACC_ENUM && ce->enum_backing_type != IS_UNDEF && ce->type == ZEND_USER_CLASS && !(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) {
24622+
if (UNEXPECTED(zend_update_class_constants(ce) == FAILURE)) {
24623+
HANDLE_EXCEPTION();
24624+
}
24625+
}
2461924626
if (Z_TYPE_P(value) == IS_CONSTANT_AST) {
2462024627
ce = c->ce;
24621-
if (ce->ce_flags & ZEND_ACC_ENUM && ce->type == ZEND_USER_CLASS && ce->enum_backing_type != IS_UNDEF) {
24622-
// Enums require loading of all class constants to build the backed enum table
24623-
zend_update_class_constants(ce);
24624-
} else {
24625-
zval_update_constant_ex(value, ce);
24626-
}
24628+
zval_update_constant_ex(value, ce);
2462724629
if (UNEXPECTED(EG(exception) != NULL)) {
2462824630
ZVAL_UNDEF(EX_VAR(opline->result.var));
2462924631
HANDLE_EXCEPTION();
@@ -33460,14 +33462,15 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_UNUS
3346033462
HANDLE_EXCEPTION();
3346133463
}
3346233464
value = &c->value;
33465+
// Enums require loading of all class constants to build the backed enum table
33466+
if (ce->ce_flags & ZEND_ACC_ENUM && ce->enum_backing_type != IS_UNDEF && ce->type == ZEND_USER_CLASS && !(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) {
33467+
if (UNEXPECTED(zend_update_class_constants(ce) == FAILURE)) {
33468+
HANDLE_EXCEPTION();
33469+
}
33470+
}
3346333471
if (Z_TYPE_P(value) == IS_CONSTANT_AST) {
3346433472
ce = c->ce;
33465-
if (ce->ce_flags & ZEND_ACC_ENUM && ce->type == ZEND_USER_CLASS && ce->enum_backing_type != IS_UNDEF) {
33466-
// Enums require loading of all class constants to build the backed enum table
33467-
zend_update_class_constants(ce);
33468-
} else {
33469-
zval_update_constant_ex(value, ce);
33470-
}
33473+
zval_update_constant_ex(value, ce);
3347133474
if (UNEXPECTED(EG(exception) != NULL)) {
3347233475
ZVAL_UNDEF(EX_VAR(opline->result.var));
3347333476
HANDLE_EXCEPTION();

0 commit comments

Comments
 (0)