PHP RFC: Enumerated Types
- Version: 2.1 (The old version by pierrick and melon can be found here: https://wiki.php.net/rfc/enum?rev=1365505707)
- Date: 2015-04-09
- Authors: Bob Weinand bwoebi@php.net, Levi Morrison levim@php.net
- Status: Obsolete
- First Published at: http://wiki.php.net/rfc/enum
Introduction
Often programmers encounter data that is naturally represented as small finite set of exclusive values. Currently in PHP there is no convenient type-safe way to represent these data sets. This RFC proposes to add syntax and semantics to facilitate representing this kind of data.
Proposal
Enums are given a name and a finite comma-separated list of unique, case-sensitive values. A trailing comma is permitted. Enum types share the same symbol table as classes, interfaces and traits. Names that are invalid for classes are also invalid for enum types. Enum types are implicitly final; they cannot be extended. Enums are not editable after definition in any way. Using an enum type that isn't loaded will trigger the autoloader just like a unloaded class. Enum values can be serialized.
Syntax
Here is a definition of an enum type RenewalAction
with two values Deny
and Approve
:
enum RenewalAction { Deny, Approve }
To access an enum value use the enum type name followed by two colons and then the name of the value (e.g. RenewalAction::Deny
). This is the same syntax for accessing class constants.
Use in Type Declarations
Enums are strongly typed and can be used as parameter or return types. They can also be used in case statements.
function other(RenewalAction $action): RenewalAction { switch ($action) { case RenewalAction::Approve: return RenewalAction::Deny; case RenewalAction::Deny: return RenewalAction::Approve; } } other(RenewalAction::Approve);
Equality and Comparisons
TODO: talk about being equal only to itself and bool.
Reflection
TODO: Figure out appropriate reflection operations. Maybe:
- Add
isEnum(): bool
toReflectionClass
? Or add a separateReflectionEnum
? - Ensure
getConstant()
andgetConstants()
work correctly.
Backwards Compatibility
This RFC adds a new token T_ENUM
. This means any code using enum
as the name for a function, method, class, interface, trait or constant will now emit a parsing error.
There are no other known backwards compatibility breaks.
Voting
The vote will be a simple “yes” or “no” and requires 2/3 of the votes to be “yes” to pass. This RFC targets PHP version 7.2.
Patches and Tests
A proof of concept implementation can be found on this branch: https://github.com/morrisonlevi/php-src/tree/enum. A proper implementation has been provided by Bob Weinand: https://github.com/php/php-src/pull/1698.
Explanation of Implementation
The implementation by Bob adds a new primitive type for enums that fits in a zval. This allows enums to be very light-weight. Equality comparisons for enums are expected to be their most common operation and the zval contains all the information needed to do the equality comparison.
The w1
and w2
entries on the zval are used as handles into arrays or hashtables. They should not be used directly nor should the macros ZEND_ENUM_CLASS
, ZEND_ENUM_HANDLE
and the _P
variants of them be used. The functions zend_enum_ce
, zend_enum_name
and zend_enum_equals
should be used instead.
Future Scope
A few ideas for things that could potentially happen:
- Algebraic data types and pattern matching:
enum Maybe { None, Some($t) } match ($maybe) { case Maybe::None { echo "None"; } case Maybe::Some($t) { echo "Some($t)"; } }
- User defined methods:
enum Direction { North { function opposite(): Direction { return Direction::South; } }, East { function opposite(): Direction { return Direction::West; } }, South { function opposite(): Direction { return Direction::North; } }, West { function opposite(): Direction { return Direction::East; } } }
- Box primitive types, such as what Hack does:
enum Flags : int { a = 1 << 0, b = 1 << 1, c = 1 << 2, d = 1 << 3 }
These ideas are not necessarily fully compatible, so we should choose carefully on further expansions to enums.