Объявление типов
Объявления типов могут использоваться для аргументов функций, возвращаемых значений и, начиная с PHP 7.4.0, для свойств класса. Они используются во время исполнения для проверки, что значение имеет точно тот тип, который для них указан. В противном случае будет выброшено исключение TypeError.
Замечание:
При переопределении родительского метода, тип возвращаемого значения дочернего метода должен соответствовать любому объявлению возвращаемого типа родительского. Если в родительском методе тип возвращаемого значения не объявлен, то это можно сделать в дочернем.
Одиночные типы
Тип | Описание | Версия |
---|---|---|
Имя класса/интерфейса |
Значение должно представлять собой instanceof заданного класса или интерфейса.
|
|
self |
Значение должно представлять собой instanceof того же класса, в котором определён метод.
Может использоваться только в классах.
|
|
array | Значение должно быть типа array. | |
callable | Значение должно быть корректным callable. Нельзя использовать в качестве объявления для свойств класса. | |
bool | Значение должно быть логического типа. | |
float | Значение должно быть числом с плавающей запятой. | |
int | Значение должно быть целым числом. | |
string | Значение должно быть строкой (тип string). | |
iterable |
Значение может быть либо массивом (тип array) или представлять собой
instanceof Traversable.
|
PHP 7.1.0 |
object | Значение должно быть объектом (тип object). | PHP 7.2.0 |
mixed | Значение может иметь любой тип. | PHP 8.0.0 |
Псевдонимы для указанных выше скалярных типов не поддерживаются.
В случае использования они будут считаться за имя класса или интерфейса.
К примеру, при использовании в качестве типа 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
mixed
mixed эквивалентен типу union array|bool|callable|int|float |object|resource|string|null . Доступно с PHP 8.0.0.
Примеры
Пример #1 Объявление типа для класса
<?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
Пример #2 Объявление типа для интерфейса
<?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
Пример #3 Объявление типа возвращаемого значения
<?php
function sum($a, $b): float {
return $a + $b;
}
// Обратите внимание, что будет возвращено число с плавающей запятой.
var_dump(sum(1, 2));
?>
Результат выполнения данного примера:
float(3)
Пример #4 Возвращение объекта
<?php
class C {}
function getC(): C {
return new C;
}
var_dump(getC());
?>
Результат выполнения данного примера:
object(C)#1 (0) { }
Обнуляемые типы
Начиная с PHP 7.1.0, объявления типов могут быть помечены как обнуляемые, путём
добавления префикса в виде знака вопроса(?
).
Это означает, что значение может быть как объявленного типа, так и быть равным null
.
Пример #5 Объявление обнуляемых типов
<?php
class C {}
function f(?C $c) {
var_dump($c);
}
f(new C);
f(null);
?>
Результат выполнения данного примера:
object(C)#1 (0) { } NULL
Пример #6 Обнуляемые типы для возвращаемого значения
<?php
function get_item(): ?string {
if (isset($_GET['item'])) {
return $_GET['item'];
} else {
return null;
}
}
?>
Замечание:
До PHP 7.1.0, было возможно задавать обнуляемые типы аргументов функций путём задания значения по умолчанию равного
null
. Так делать не рекомендуется, поскольку это может поломать наследование.Пример #7 Старый способ задавать обнуляемые типы для аргументов
<?php
class C {}
function f(C $c = null) {
var_dump($c);
}
f(new C);
f(null);
?>Результат выполнения данного примера:
object(C)#1 (0) { } NULL
Объединённые типы
Объединённые типы позволяют использовать несколько типов, а не исключительно один.
Для их объявления используется следующий синтаксис T1|T2|...
.
Объединённые типы доступны начиная с PHP 8.0.0.
Обнуляемые объединённые типы
Тип null
можно использовать как часть объединений следующим образов:
T1|T2|null
.
Существующая нотация ?T
рассматривается как сокращение для T|null
.
null
не может использоваться как отдельный тип.
Псевдо-тип false
Псевдо-тип false
поддерживается как часть объединённых типов.
Он добавлен по историческим причинам, так как многие встроеные функции возвращают
false
вместо null
в случае ошибок.
Классический пример - функция strpos().
false
нельзя использовать как самостоятельный тип (включая
обнуляемый вариант).
Таким образом, все объявления такого типа недопустимы: false
, false|null
и ?false
.
Обратите внимание, что псевдо-тип true
не существует.
Дублирующиеся и повторяющиеся типы
Для отлова простых ошибок в объединённых объявлениях, повторяющиеся типы, которые можно отследить без загрузки класса, приведут к ошибке компиляции. В том числе:
-
Каждый тип распознаваемый по имени должен встречаться только один раз.
Типы вида
int|string|INT
приведут к ошибке. - Если используется bool, то использовать дополнительно false нельзя.
- Если используется тип object, то дополнительное использование имён классов недопустимо.
- Если используется iterable, то к нему нельзя добавить array или Traversable.
Замечание: Это не гарантирует, что все объединённые типы объявлены корректно, поскольку такая проверка потребует загрузки всех используемых классов.
К примеру, если A
и B
являются псевдонимами одного и того же класса,
то A|B
выглядит как корректный объединённый тип, даже если фактически объявление
может быть сокращено до A
или B
.
Аналогично, если B extends A {}
, то A|B
тоже
выглядит корректным типом, не смотря на то, что он может быть сокращён до
A
.
<?php
function foo(): int|INT {} // Запрещено
function foo(): bool|false {} // Запрещено
use A as B;
function foo(): A|B {} // Запрещено ("use" является частью разрешения имён)
class_alias('X', 'Y');
function foo(): X|Y {} // Допустимо (повторение будет определено только во время выполнения)
?>
Типы, подходящие только для возвращаемого значения
void
Тип void
означает, что функция ничего не возвращает.
Соответственно он не может быть частью объединения.
Доступно с PHP 7.1.0.
static
Значение должно представлять собой instanceof
того же класса, в котором был вызван метод.
Доступно с PHP 8.0.0.
Строгое типизирование
По умолчанию, PHP будет преобразовывать значения неправильного типа в ожидаемые. К примеру, если в функцию передать параметр типа int в аргумент, объявленный как string, то он преобразуется в string.
Можно включить режим строгого типизирования на уровне файла. В этом режиме, тип значения должен строго соответствовать объявленому, иначе будет выброшено исключение TypeError. Единственным исключением из этого правила является передача значения типа int туда, где ожидается float.
На вызовы из внутренних функций, действие strict_types
не распространяется.
Для включения строгой типизации используется оператор declare
с объявлением
strict_types
:
Замечание:
Строгая типизация применяется к вызовам функций, сделанным изнутри файла с включенной строгой типизацией, а не к функциям, объявленным в этом файле. Если из файла без включенной строгой типизации вызывается функция, которая была определена в файле со строгой типизацией, то будет использованы его предпочтения по типизации - т.е. правила строгой типизации будут проигнорированы и для значений будет применяться приведение типов.
Замечание:
Строгая типизация определяется только для объявлений скалярных типов.
Пример #8 Строгая типизация для значений аргументов
<?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
Пример #9 Приведение типов для значений аргументов
<?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)
Пример #10 Строгая типизация для возвращаемых значений
<?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
Приведение для объеденённых типов
Если strict_types
не разрешено, то объявления скалярных типов
подлежат ограниченному неявному приведению. Если фактический тип не является частью объединения,
то используется следующий порядок приведения типов:
- int
- float
- string
- bool
Единственным исключением является приведение строки в случае, если в объединении
одновременно присутствуют и int и float. В таком случае будет
выбрано наиболее подходящий тип по правилу приведения "числовых строк".
К примеру, для "42"
бдет выбран тип int,
а для "42.0"
тип float.
Замечание:
Типы, не входящие в данный список не подходят для целей неявного приведения. targets for implicit coercion. В часности, для
null
иfalse
неявного приведения не случится.
Пример #11 Пример приведения для объединённых типов
<?php
// int|string
42 --> 42 // явный тип
"42" --> "42" // явный тип
new ObjectWithToString --> строка с результатом выполнения __toString()
// Объекты никогда не будут приведены к целому числу, даже если вернут "числовую строку"
42.0 --> 42 // float совместим с int
42.1 --> 42 // float совместим с int
1e100 --> "1.0E+100" // float слишком большой для типа int, преобразуется в строку
INF --> "INF" // float слишком большой для типа int, преобразуется в строку
true --> 1 // bool совместим с int
[] --> TypeError // array не совместим ни с int ни со string
// int|float|bool
"45" --> 45 // целочисленная "чистовая строка"
"45.0" --> 45.0 // "чистовая строка" с плавающей запятой
"45X" --> true // не "чистовая строка", приведётся к bool
"" --> false // не "чистовая строка", приведётся к bool
"X" --> true // не "чистовая строка", приведётся к bool
[] --> TypeError // array не совместим ни с int ни с float ни с bool
?>
Дополнительно
Пример #12 Типизированые параметры передаваемые по ссылке
Объявление типов для параметров передаваемых по ссылке проверяется только на этапе вызова функции. Не никакой гарантии, что после выхода из функции тип переменной останется неизменным.
<?php
function array_baz(array &$param)
{
$param = 1;
}
$var = [];
array_baz($var);
var_dump($var);
array_baz($var);
?>
Результат выполнения данного примера в PHP 8:
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
Пример #13 Обработка TypeError
<?php
declare(strict_types=1);
function sum(int $a, int $b) {
return $a + $b;
}
try {
var_dump(sum(1, 2));
var_dump(sum(1.5, 2.5));
} catch (TypeError $e) {
echo 'Ошибка: ', $e->getMessage();
}
?>
Результат выполнения данного примера в PHP 8:
int(3) Ошибка: sum(): Argument #1 ($a) must be of type int, float given, called in - on line 10