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

В классах обязательства по контрактам берут на себя методы:

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

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

`↑` and `↓` to navigate • `Enter` to select • `Esc` to close • `/` to open