Enum-like class constants, and the three things they cannot do

Enums are 8.1, so a set of allowed values in 2020 is class constants with a validating constructor — and the gaps are worth naming rather than discovering.

final class OrderStatus
{
    public const PENDING = 'pending';
    public const PAID    = 'paid';

    private const ALL = [self::PENDING, self::PAID];

    private string $value;

    public function __construct(string $value)
    {
        if (! in_array($value, self::ALL, true)) {
            throw new InvalidArgumentException($value);
        }

        $this->value = $value;
    }
}

Three things this cannot do. It cannot be exhaustively checked, so a match over the constants has no compiler telling you when one is missed. It cannot be used as a type hint that guarantees validity — the constructor can be bypassed by anything that unserialises. And two instances of the same value are not identical, so === fails and every comparison needs a method. All three go away in 8.1, which is worth knowing when deciding how much to build now.