Для методов классов объявлены контракты:
<?php
class A {}
class B extends A {}
function foo(A $a) {}
function bar(B $b) {
foo($b);
}
?>
Этот код безопасен для типов, поскольку класс B выполняет контракт класса A, и, за счёт магии ковариантности и контравариантности, ожидания в отношении методов будут оправданы, за исключением исключений.
У перечислений есть контракты на их варианты, а не методы:
<?php
enum ErrorCode
{
case SOMETHING_BROKE;
}
function quux(ErrorCode $errorCode)
{
// Кажется, что этот код охватывает все варианты
match ($errorCode) {
ErrorCode::SOMETHING_BROKE => true,
}
}
?>
Проведя статический анализ выражения match в функции quux
,
легко понять, что охвачен каждый вариант перечисления ErrorCode.
Но представьте, что было бы разрешено расширять перечисления:
<?php
// Экспериментальный код, в котором перечисления не конечно.
// Обратите внимание, что это не будет работать в PHP.
enum MoreErrorCode extends ErrorCode
{
case PEBKAC;
}
function fot(MoreErrorCode $errorCode) {
quux($errorCode);
}
fot(MoreErrorCode::PEBKAC);
?>
Следуя стандартным правилам наследования, класс, расширяющий другой класс, пройдёт проверку типа.
Проблема состоит в том, что выражение match в функции quux()
уже не покрывает все случаи.
Поскольку оно не знает о варианте MoreErrorCode::PEBKAC
, выражение match выбросит исключение.
Поэтому перечисления окончательны и их не разрешено расширять.