From 0e2733b84197819a536c8f7cdc5c8b03c3f24db0 Mon Sep 17 00:00:00 2001 From: Tyson Andre Date: Mon, 17 May 2021 09:08:05 -0400 Subject: [PATCH] Proposal: Allow static properties in enums These were forbiddden in the initial RFC, which was already very large/complex. Others had suggested allowing static properties, e.g. https://externals.io/message/112626#113037 Continue to forbid instance properties. There are various use cases where static properties would be useful: 1. Using traits that declare static properties without getting a fatal error. 2. Static methods related to an enum that that act on immutable enum instances (e.g. `EnvironmentEnum::current()` could return `::PROD`, `::STAGE`, `::DEV`) The alternatives to static properties would have drawbacks, such as shared state being more visible than intended (not `private`), or not being able to use typed properties. --- Zend/tests/enum/no-inherited-properties.phpt | 16 ++++++++++ Zend/tests/enum/no-name-property.phpt | 2 +- Zend/tests/enum/no-properties.phpt | 4 +-- Zend/tests/enum/no-static-properties.phpt | 12 -------- Zend/tests/enum/no-value-property.phpt | 2 +- Zend/tests/enum/static-properties.phpt | 32 ++++++++++++++++++++ Zend/tests/enum/traits-no-properties.phpt | 2 +- Zend/zend_compile.c | 4 +-- Zend/zend_enum.c | 7 +++-- 9 files changed, 60 insertions(+), 21 deletions(-) create mode 100644 Zend/tests/enum/no-inherited-properties.phpt delete mode 100644 Zend/tests/enum/no-static-properties.phpt create mode 100644 Zend/tests/enum/static-properties.phpt diff --git a/Zend/tests/enum/no-inherited-properties.phpt b/Zend/tests/enum/no-inherited-properties.phpt new file mode 100644 index 0000000000000..b8ac65704f154 --- /dev/null +++ b/Zend/tests/enum/no-inherited-properties.phpt @@ -0,0 +1,16 @@ +--TEST-- +Enum disallows inherited instance properties +--FILE-- + +--EXPECTF-- +Fatal error: Enum "Foo" may not include instance properties in %s on line 7 diff --git a/Zend/tests/enum/no-name-property.phpt b/Zend/tests/enum/no-name-property.phpt index a565c08e83005..3e994aadfeabf 100644 --- a/Zend/tests/enum/no-name-property.phpt +++ b/Zend/tests/enum/no-name-property.phpt @@ -9,4 +9,4 @@ enum Foo { ?> --EXPECTF-- -Fatal error: Enums may not include properties in %s on line %d +Fatal error: Enums may not include instance properties in %s on line %d diff --git a/Zend/tests/enum/no-properties.phpt b/Zend/tests/enum/no-properties.phpt index e846845d22270..42f5f5dfe80f3 100644 --- a/Zend/tests/enum/no-properties.phpt +++ b/Zend/tests/enum/no-properties.phpt @@ -1,5 +1,5 @@ --TEST-- -Enum disallows properties +Enum disallows instance properties --FILE-- --EXPECTF-- -Fatal error: Enums may not include properties in %s on line %d +Fatal error: Enums may not include instance properties in %s on line %d diff --git a/Zend/tests/enum/no-static-properties.phpt b/Zend/tests/enum/no-static-properties.phpt deleted file mode 100644 index 4b823e98e9ffd..0000000000000 --- a/Zend/tests/enum/no-static-properties.phpt +++ /dev/null @@ -1,12 +0,0 @@ ---TEST-- -Enum disallows static properties ---FILE-- - ---EXPECTF-- -Fatal error: Enums may not include properties in %s on line %d diff --git a/Zend/tests/enum/no-value-property.phpt b/Zend/tests/enum/no-value-property.phpt index d8b12f2ca8a23..eb85ae624cb8c 100644 --- a/Zend/tests/enum/no-value-property.phpt +++ b/Zend/tests/enum/no-value-property.phpt @@ -9,4 +9,4 @@ enum Foo: int { ?> --EXPECTF-- -Fatal error: Enums may not include properties in %s on line %d +Fatal error: Enums may not include instance properties in %s on line %d diff --git a/Zend/tests/enum/static-properties.phpt b/Zend/tests/enum/static-properties.phpt new file mode 100644 index 0000000000000..e092af78e11dd --- /dev/null +++ b/Zend/tests/enum/static-properties.phpt @@ -0,0 +1,32 @@ +--TEST-- +Enum allows static properties +--FILE-- +name, "\n"; + self::$loggingEnabled = self::$current !== self::PROD; + var_dump(self::$loggingEnabled); + } +} + +Environment::set(Environment::DEV); + +?> +--EXPECT-- +DEV +bool(true) diff --git a/Zend/tests/enum/traits-no-properties.phpt b/Zend/tests/enum/traits-no-properties.phpt index 5a6faf9deac4e..8338f75a92d42 100644 --- a/Zend/tests/enum/traits-no-properties.phpt +++ b/Zend/tests/enum/traits-no-properties.phpt @@ -22,4 +22,4 @@ enum Suit { ?> --EXPECTF-- -Fatal error: Enum "Suit" may not include properties in %s on line %d +Fatal error: Enum "Suit" may not include instance properties in %s on line %d diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index fbb680be2f716..1219176a437f2 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -7159,8 +7159,8 @@ void zend_compile_prop_decl(zend_ast *ast, zend_ast *type_ast, uint32_t flags, z zend_error_noreturn(E_COMPILE_ERROR, "Interfaces may not include properties"); } - if (ce->ce_flags & ZEND_ACC_ENUM) { - zend_error_noreturn(E_COMPILE_ERROR, "Enums may not include properties"); + if ((ce->ce_flags & ZEND_ACC_ENUM) && !(flags & ZEND_ACC_STATIC)) { + zend_error_noreturn(E_COMPILE_ERROR, "Enums may not include instance properties"); } if (flags & ZEND_ACC_ABSTRACT) { diff --git a/Zend/zend_enum.c b/Zend/zend_enum.c index 437fa2f815071..f86ee80741588 100644 --- a/Zend/zend_enum.c +++ b/Zend/zend_enum.c @@ -66,8 +66,11 @@ static void zend_verify_enum_properties(zend_class_entry *ce) ) { continue; } + if (property_info->flags & ZEND_ACC_STATIC) { + continue; + } // FIXME: File/line number for traits? - zend_error_noreturn(E_COMPILE_ERROR, "Enum \"%s\" may not include properties", + zend_error_noreturn(E_COMPILE_ERROR, "Enum \"%s\" may not include instance properties", ZSTR_VAL(ce->name)); } ZEND_HASH_FOREACH_END(); } @@ -212,7 +215,7 @@ void zend_enum_add_interfaces(zend_class_entry *ce) 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->interface_names[num_interfaces_before + 1].lc_name = zend_string_init("backedenum", sizeof("backedenum") - 1, 0); } }