The same match on an enum, in four places, each returning a different aspect of the same thing.
interface HasLabel { public function label(): string; }
enum Status: string implements HasLabel
{
case Pending = 'pending';
case Paid = 'paid';
public function label(): string
{
return match ($this) {
self::Pending => 'Awaiting payment',
self::Paid => 'Paid',
};
}
public function isTerminal(): bool { /* ... */ }
}
Moving the match onto the enum means adding a case produces a compile-time incomplete-match error in one place rather than a silent fallthrough in four. The counter-argument is real: presentation strings on a domain enum is a layering violation, and the honest answer is that isTerminal() belongs there and label() probably belongs in a presenter. We kept both and wrote down why, which is the part that matters.