By default, Enumerated Cases have no scalar equivalent. They are simply singleton objects. However, there are ample cases where an Enumerated Case needs to be able to round-trip to a database or similar datastore, so having a built-in scalar (and thus trivially serializable) equivalent defined intrinsically is useful.
To define a scalar equivalent for an Enumeration, the syntax is as follows:
<?php
enum Suit: string
{
case Hearts = 'H';
case Diamonds = 'D';
case Clubs = 'C';
case Spades = 'S';
}
?>
A case that has a scalar equivalent is called a Backed Case, as it is "Backed" by a simpler value. An Enum that contains all Backed Cases is called a "Backed Enum." A Backed Enum may contain only Backed Cases. A Pure Enum may contain only Pure Cases.
A Backed Enum may be backed by types of int
or string
,
and a given enumeration supports only a single type at a time (that is, no union of int|string
).
If an enumeration is marked as having a scalar equivalent, then all cases must have a unique
scalar equivalent defined explicitly. There are no auto-generated scalar equivalents
(e.g., sequential integers). Backed cases must be unique; two backed enum cases may
not have the same scalar equivalent. However, a constant may refer to a case, effectively
creating an alias. See Enumeration constants.
Equivalent values must be literals or literal expressions. Constants and constant expressions
are not supported. That is, 1 + 1
is allowed, but 1 + SOME_CONST
is not.
Backed Cases have an additional read-only property, value
, which is the value
specified in the definition.
<?php
print Suit::Clubs->value;
// Prints "C"
?>
In order to enforce the value
property as read-only, a variable cannot
be assigned as a reference to it. That is, the following throws an error:
<?php
$suit = Suit::Clubs;
$ref = &$suit->value;
// Error: Cannot acquire reference to property Suit::$value
?>
Backed enums implement an internal BackedEnum interface, which exposes two additional methods:
from(int|string): self
will take a scalar and return the corresponding
Enum Case. If one is not found, it will throw a ValueError. This is mainly
useful in cases where the input scalar is trusted and a missing enum value should be
considered an application-stopping error.
tryFrom(int|string): ?self
will take a scalar and return the
corresponding Enum Case. If one is not found, it will return null
.
This is mainly useful in cases where the input scalar is untrusted and the caller wants
to implement their own error handling or default-value logic.
The from()
and tryFrom()
methods follow standard
weak/strong typing rules. In weak typing mode, passing an integer or string is acceptable
and the system will coerce the value accordingly. Passing a float will also work and be
coerced. In strict typing mode, passing an integer to from()
on a
string-backed enum (or vice versa) will result in a TypeError,
as will a float in all circumstances. All other parameter types will throw a TypeError
in both modes.
<?php
$record = get_stuff_from_database($id);
print $record['suit'];
$suit = Suit::from($record['suit']);
// Invalid data throws a ValueError: "X" is not a valid scalar value for enum "Suit"
print $suit->value;
$suit = Suit::tryFrom('A') ?? Suit::Spades;
// Invalid data returns null, so Suit::Spades is used instead.
print $suit->value;
?>
Manually defining a from()
or tryFrom()
method on a Backed Enum will result in a fatal error.