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

Объявления типов могут использоваться для аргументов функций, возвращаемых значений и, начиная с PHP 7.4.0, для свойств класса. Они используются во время исполнения для проверки, что значение имеет точно тот тип, который для них указан. В противном случае будет выброшено исключение 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 Добавлена поддержка типа возвращаемого значения nullable.

Примечание по использованию атомарных типов

У атомарных типов прямолинейное поведение с некоторыми незначительными оговорками, которые описаны в этом разделе.

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

Внимание

Псевдонимы имён для скалярных типов (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

Этот тип не может использоваться в качестве объявления типа свойства класса.

Замечание: Невозможно указать сигнатуру функции.

Объявление типов в параметрах передачи по ссылкам

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

Пример #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

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

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

Замечание:

Также можно добиться nullable аргументов, указав null значением по умолчанию. Это не рекомендуется, поскольку если значение по умолчанию будет изменено в дочернем классе, возникнет нарушение совместимости типов, так как в объявление типа нужно будет добавить тип null.

Пример #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 приводит к ошибке.
  • Для объединения типов:
    • Если используется тип bool, то false или true не могут быть использованы дополнительно.
    • Если используется тип object, типы классов не могут быть использованы дополнительно.
    • Если используется тип iterable, то array и Traversable не могут быть использованы дополнительно.
  • Для пересечения типов:
    • Использование типа, который не является типом класса, приводит к ошибке.
    • Использование 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 Объявление аргумента с типом Nullable

<?php
class C {}

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

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

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

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

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

<?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 будет преобразовывать значения неправильного типа в ожидаемые. К примеру, если в функцию передать параметр типа int в аргумент, объявленный как string, то он преобразуется в 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