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

Объявления типов могут использоваться для аргументов функций, возвращаемых значений и, начиная с 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 {}
class 
extends {}

// Не является наследником C.
class {}

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 { public function f(); }
class 
implements { public function f() {} }

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

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(12));
?>

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

float(3)

Пример #4 Возвращение объекта

<?php
class {}

function 
getC(): {
    return new 
C;
}

var_dump(getC());
?>

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

object(C)#1 (0) {
}

Обнуляемые типы

Начиная с PHP 7.1.0, объявления типов могут быть помечены как обнуляемые, путём добавления префикса в виде знака вопроса(?). Это означает, что значение может быть как объявленного типа, так и быть равным null.

Пример #5 Объявление обнуляемых типов

<?php
class {}

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 {}

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 as B;
function 
foo(): A|{} // Запрещено ("use" является частью разрешения имён)

class_alias('X''Y');
function 
foo(): X|{} // Допустимо (повторение будет определено только во время выполнения)
?>

Типы, подходящие только для возвращаемого значения

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 $aint $b) {
    return 
$a $b;
}

var_dump(sum(12));
var_dump(sum(1.52.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 $aint $b) {
    return 
$a $b;
}

var_dump(sum(12));

// Переданные значения будут приведены к целым числам: обратите внимание на вывод ниже!
var_dump(sum(1.52.5));
?>

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

int(3)
int(3)

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

<?php
declare(strict_types=1);

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

var_dump(sum(12));
var_dump(sum(12.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 не разрешено, то объявления скалярных типов подлежат ограниченному неявному приведению. Если фактический тип не является частью объединения, то используется следующий порядок приведения типов:

  1. int
  2. float
  3. string
  4. bool
Если тип присутствует в объединении и значение может быть приведено к нему основываясь на существующей семантике приведения типов PHP, то поизойдёт приведение к нему. Если нет, то проверится следующий тип в списке.

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

Единственным исключением является приведение строки в случае, если в объединении одновременно присутствуют и 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 $aint $b) {
    return 
$a $b;
}

try {
    
var_dump(sum(12));
    
var_dump(sum(1.52.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