PHPCon Poland 2024

Почему перечисления не расширяемы

Для методов классов объявлены контракты:

<?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 выбросит исключение.

Поэтому перечисления окончательны и их не разрешено расширять.

add a note

User Contributed Notes

There are no user contributed notes for this page.
To Top