## Объявления типов

Объявления типов добавляют параметрам функций, значениям возврата, начиная с PHP 7.4.0 свойствам класса и начиная с PHP 8.3.0 константам класса. Объявления типов гарантируют, что при вызове значение принадлежит заданному типу, иначе PHP выбросит ошибку TypeError.

Каждый тип, который поддерживает PHP, за исключением ресурсов (resource), разрешается указывать в пользовательском объявлении типа. Страница содержит журнал изменений доступности отдельных типов и документацию, которая описывает правила объявления типов.

> Замечание:

> Класс, который реализует метод интерфейса или переопределяет метод родительского класса, подчиняется правилам совместимости. Метод совместим, если следует правилам вариантности.

### Список изменений

Версия | Описание
--- | ---
8.3.0 | Добавили поддержку типизации констант классов, интерфейсов, трейтов и перечислений.
8.2.0 | Добавили поддержку DNF-типов.
8.2.0 | Добавили поддержку самостоятельного типа true.
8.2.0 | Типы null и false разрешили указывать отдельно.
8.1.0 | Добавили поддержку пересечений типов.
8.1.0 | Возврат по ссылке из функции с типом значения возврата void устарел.
8.1.0 | Для значения возврата добавили поддержку типа never.
8.0.0 | Для значения возврата добавили поддержку типа mixed.
8.0.0 | Для значения возврата добавили поддержку типа static.
8.0.0 | Добавили поддержку объединения типов.
7.4.0 | Добавили поддержку типизации свойств классов.
7.2.0 | Для значения возврата добавили поддержку типа object.
7.1.0 | Для значения возврата добавили поддержку типа iterable.
7.1.0 | Для значения возврата добавили поддержку типа void.
7.1.0 | Для значения возврата добавили поддержку обнуляемого типа.

### Атомарные типы

Атомарные типы ведут себя прямолинейно, но с рядом оговорок, которые описывает этот раздел.

#### Скалярные типы

Внимание

Псевдонимы имён для скалярных типов, к которым относятся типы bool, int, float и string, не поддерживаются. Вместо назначения псевдонимов названия классов и интерфейсов трактуются как типы. Например, буквальное название `boolean` в объявлении типа потребует, чтобы значение выполняло условие `instanceof` в отношении класса или интерфейса `boolean`, а не принадлежало типу bool:

    <?php

    function test(boolean $param) {}
    test(true);

    ?>

Результат выполнения приведённого примера в PHP 8:

    Warning: "boolean" will be interpreted as a class name. Did you mean "bool"? Write "\boolean" to suppress this warning in /in/9YrUX on line 2

    Fatal error: Uncaught TypeError: test(): Argument #1 ($param) must be of type boolean, bool given, called in - on line 3 and defined in -:2
    Stack trace:
    #0 -(3): test(true)
    #1 {main}
      thrown in - on line 2

#### Тип void

> Замечание:

> Возврат по ссылке из void-функции устарел начиная с PHP 8.1.0, поскольку такая функция противоречива. Раньше при вызове такой функции уже выдавалась ошибка уровня `E_NOTICE`: Только ссылки на переменные должны возвращаться по ссылке.

>     <?php

>     function &test(): void {}

>     ?>

#### Тип Callable

Свойствам класса нельзя объявлять тип callable.

> Замечание: Тип callable нельзя указывать как название функции.

#### Объявление типов параметрам, которые принимают ссылку

Параметр, который принимает ссылку, проверяет тип переменной только при входе в функцию, в начале вызова, но не при возврате из функции, поэтому в функции возможно изменение типа ссылки на переменную.

Пример #1 Типизированный параметр, который принимает ссылку

    <?php

    function array_baz(array &$param)
    {
        $param = 1;
    }

    $var = [];
    array_baz($var);
    var_dump($var);
    array_baz($var);

    ?>

Вывод приведённого примера будет похож на:

    int(1)

    Fatal error: Uncaught TypeError: array_baz(): Argument #1 ($param) must be of type array, int given, called in - on line 9 and defined in -:2
    Stack trace:
    #0 -(9): array_baz(1)
    #1 {main}
      thrown in - on line 2

### Примечание по составным типам

На объявления составных типов распространяется ряд ограничений; компилятор выполняет проверку избыточности типов, чтобы предотвратить простые ошибки.

Предостережение

Комбинирование пересечений и объединений типов оставалось невозможным до PHP 8.2.0 и введения DNF-типов.

#### Объединение типов

Внимание

Невозможно определить в объединённом типе два одноэлементных типа false и true. Вместо этого указывают тип bool.

Предостережение

До PHP 8.2.0 запрещали определять типы false и null отдельно, поэтому объединение типов, которое состояло только из этих типов, оставалось недопустимым. К таким объединениям относятся типы: false, `false|null` и `?false`.

##### Синтаксический сахар обнуляемого типа

Объявление отдельного базового типа помечают как nullable путём добавления к типу префикса в виде вопросительного знака (`?`). Поэтому типы `?T` и `T|null` идентичны.

> Замечание: Этот синтаксис поддерживается с PHP 7.1.0 и предшествует поддержке объединения типов.

> Замечание:

> Ещё один способ объявить nullable-параметр — указать значением по умолчанию значение `null`. Объявлять тип таким способом не рекомендуют, поскольку изменение значения по умолчанию в дочернем классе нарушит совместимость типов, и к объявлению типа потребуется добавить тип null. Это поведение также устарело начиная с PHP 8.4.

> Пример #2 Старый способ указать nullable-параметр

>     <?php

>     class C {}

>     function f(C $c = null)
>     {
>         var_dump($c);
>     }

>     f(new C());
>     f(null);

>     ?>

> Результат выполнения приведённого примера:

>     object(C)#1 (0) {
>     }
>     NULL

#### Повторные и избыточные типы

Правила объявления составных типов предусматривают ряд ограничений. При компиляции кода с избыточными типами, которые возможно обнаружить без загрузки классов, возникнет ошибка. Примеры ошибок в объявлениях составных типов включают:

- Повторение типа в объявлениях наподобие `int|string|INT` или `Countable&Traversable&COUNTABLE` вызовет ошибку, поскольку после разрешения названия типа внутренними средствами языка тип встретится больше одного раза.
- Объявление типа mixed или never с другими типами вызовет ошибку.
- Ошибки объединения типов:
  - Избыточное объявление типа false или true вместе с типом bool.
  - Избыточное объявление типа класса вместе с типом object.
  - Избыточное объявление типа array или Traversable вместе с типом iterable.
- Ошибки пересечения типов:
  - Объявление типа, который не относится к классовому типу.
  - Объявление типа self, parent или static.
- Ошибки объявления DNF-типов:
  - Избыточное объявление более строго типа с более общим типом.
  - Объявление дубликатов в пересечении типов.

> Замечание: Эти проверки не гарантируют, что тип «минимален» и не содержит повторений или избыточных типов, поскольку для этого потребовалось бы загрузить все указанные типы классов.

Например, если типы `A` и `B` — псевдонимы классов, то составной тип `A|B` остаётся корректным объединением типов, даже если тип получится свести либо к `A`, либо к `B`. Аналогично, если класс `B extends A {}`, то составной тип `A|B` также относится к корректному объединению типов, даже если тип получится свести только к типу `A`.

    <?php

    function foo(): int|INT {} // Запрещено
    function foo(): bool|false {} // Запрещено
    function foo(): int&Traversable {} // Запрещено
    function foo(): self&Traversable {} // Запрещено

    use A as B;
    function foo(): A|B {} // Запрещено (оператор "use" только создаёт псевдоним через механизм разрешения имён, а не отдельный тип)
    function foo(): A&B {} // Запрещено (оператор "use" только создаёт псевдоним через механизм разрешения имён, а не отдельный тип)

    class_alias('X', 'Y');
    function foo(): X|Y {} // Разрешено (избыточность обнаружится только при выполнении кода)
    function foo(): X&Y {} // Разрешено (избыточность обнаружится только при выполнении кода)

    ?>

### Примеры

Пример #3 Пример объявления типа через название класса

    <?php

    class C {}
    class D extends C {}

    // Класс не наследует класс C
    class E {}

    function f(C $c)
    {
        echo get_class($c)."\n";
    }

    f(new C);
    f(new D);
    f(new E);

    ?>

Результат выполнения приведённого примера в PHP 8:

    C
    D

    Fatal error: Uncaught TypeError: f(): Argument #1 ($c) must be of type C, E given, called in /in/gLonb on line 14 and defined in /in/gLonb:8
    Stack trace:
    #0 -(14): f(Object(E))
    #1 {main}
      thrown in - on line 8

Пример #4 Пример объявления типа через название интерфейса

    <?php

    interface I
    {
        public function f();
    }

    class C implements I
    {
        public function f() {}
    }

    // Класс не реализует интерфейс I
    class E {}

    function f(I $i)
    {
        echo get_class($i) . "\n";
    }

    f(new C);
    f(new E);

    ?>

Результат выполнения приведённого примера в PHP 8:

    C

    Fatal error: Uncaught TypeError: f(): Argument #1 ($i) must be of type I, E given, called in - on line 13 and defined in -:8
    Stack trace:
    #0 -(13): f(Object(E))
    #1 {main}
      thrown in - on line 8

Пример #5 Пример объявления типа значения возврата

    <?php

    function sum($a, $b): float
    {
        return $a + $b;
    }

    // Обратите внимание, что функция вернёт значение с типом float
    var_dump(sum(1, 2));

    ?>

Результат выполнения приведённого примера:

    float(3)

Пример #6 Возврат объекта

    <?php

    class C {}

    function getC(): C
    {
        return new C();
    }

    var_dump(getC());

    ?>

Результат выполнения приведённого примера:

    object(C)#1 (0) {
    }

Пример #7 Объявление параметра с обнуляемым типом

    <?php

    class C {}

    function f(?C $c)
    {
        var_dump($c);
    }

    f(new C());
    f(null);

    ?>

Результат выполнения приведённого примера:

    object(C)#1 (0) {
    }
    NULL

Пример #8 Объявление типа значения возврата как обнуляемого

    <?php

    function get_item(): ?string
    {
        if (isset($_GET['item'])) {
            return $_GET['item'];
        } else {
            return null;
        }
    }

    ?>

Пример #9 Объявление типа свойства класса

    <?php

    class User
    {
        public static string $foo = 'foo';

        public int $id;
        public string $username;

        public function __construct(int $id, string $username)
        {
            $this->id = $id;
            $this->username = $username;
        }
    }

    ?>

### Строгая типизация

В нестрогом режиме, в котором проверка типов работает по умолчанию, PHP преобразовывает значения неправильного типа в скалярный тип, который указали в объявлении, поэтому, например, при передаче в параметр с типом string аргумента с типом int функция получит значение с типом string.

Строгий режим включается отдельно для каждого файла. В строгом режиме механизм проверки типов признаёт только значения, которые соответствуют типу объявления, иначе выбрасывается ошибка TypeError. Единственное исключение из правила — значения с типом int, которые передают в объявления типа float.

Внимание

Объявление директивы `strict_types` не влияет на вызовы функций внутри встроенных функций.

Строгий режим включают инструкцией `declare` с объявлением директивы `strict_types`:

> Замечание:

> На строгость проверки типов при вызове функций влияет режим, который объявили внутри файла вызова, а не режим, который установили в файле объявления функции. Механизм проверки типов применит режим, который установили на стороне вызова, и автоматически приведёт типы значений, если файл без строгой типизации вызывает функцию, которую объявили в файле со строгой типизацией.

> Замечание:

> Строгая типизация определяется только для объявлений скалярных типов.

Пример #10 Строгая типизация для значений аргументов

    <?php

    declare(strict_types=1);

    function sum(int $a, int $b)
    {
        return $a + $b;
    }

    var_dump(sum(1, 2));
    var_dump(sum(1.5, 2.5));

    ?>

Результат выполнения приведённого примера в PHP 8:

    int(3)

    Fatal error: Uncaught TypeError: sum(): Argument #1 ($a) must be of type int, float given, called in - on line 9 and defined in -:4
    Stack trace:
    #0 -(9): sum(1.5, 2.5)
    #1 {main}
      thrown in - on line 4

Пример #11 Приведение типов для значений аргументов

    <?php

    function sum(int $a, int $b)
    {
        return $a + $b;
    }

    var_dump(sum(1, 2));

    // Механизм проверки типов приведёт значения аргументов к целым числам: обратите внимание на вывод!
    var_dump(sum(1.5, 2.5));

    ?>

Результат выполнения приведённого примера:

    int(3)
    int(3)

Пример #12 Строгая типизация для значений возврата

    <?php

    declare(strict_types=1);

    function sum($a, $b): int
    {
        return $a + $b;
    }

    var_dump(sum(1, 2));
    var_dump(sum(1, 2.5));

    ?>

Результат выполнения приведённого примера:

    int(3)

    Fatal error: Uncaught TypeError: sum(): Return value must be of type int, float returned in -:5
    Stack trace:
    #0 -(9): sum(1, 2.5)
    #1 {main}
      thrown in - on line 5

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