PHP 7.4.24 Released!

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

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

Замечание:

При переопределении родительского метода, тип возвращаемого значения дочернего метода должен соответствовать любому объявлению возвращаемого типа родительского. Если в родительском методе тип возвращаемого значения не объявлен, то это можно сделать в дочернем.

Одиночные типы

Тип Описание Версия
Имя класса/интерфейса Значение должно представлять собой instanceof заданного класса или интерфейса.  
self Значение должно представлять собой instanceof того же класса, в котором используется объявление типа. Может использоваться только в классах.  
parent Значение должно представлять собой 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 object|resource|array|string|int|float|bool|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.

Замечание:

Типы, не входящие в данный список, не подходят для целей неявного приведения. В частности, для 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
add a note add a note

User Contributed Notes 2 notes

up
7
anisgazig at example dot com
4 months ago
same data type and same value but first function declare as a argument type declaration and return int(7)
and second fucntion declare as a return type declaration but return int(8).

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

var_dump(argument_type_declaration(3.5,4.7));
//output:int(7)

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

var_dump(return_type_declaration(3.5,4.7));

//output:int(8)
up
4
toinenkayt (ta at ta) [iwonderr] gmail d
2 months ago
While waiting for native support for typed arrays, here are a couple of alternative ways to ensure strong typing of arrays by abusing variadic functions. The performance of these methods is a mystery to the writer and so the responsibility of benchmarking them falls unto the reader.

PHP 5.6 added the splat operator (...) which is used to unpack arrays to be used as function arguments. PHP 7.0 added scalar type hints. Latter versions of PHP have further improved the type system. With these additions and improvements, it is possible to have a decent support for typed arrays.

<?php
declare (strict_types=1);

function
typeArrayNullInt(?int ...$arg): void {
}

function
doSomething(array $ints): void {
    (function (?
int ...$arg) {})(...$ints);
   
// Alternatively,
   
(fn (?int ...$arg) => $arg)(...$ints);
   
// Or to avoid cluttering memory with too many closures
   
typeArrayNullInt(...$ints);

   
/* ... */
}

function
doSomethingElse(?int ...$ints): void {
   
/* ... */
}

$ints = [1,2,3,4,null];
doSomething ($ints);
doSomethingElse (...$ints);
?>

Both methods work with all type declarations. The key idea here is to have the functions throw a runtime error if they encounter a typing violation. The typing method used in doSomethingElse is cleaner of the two but it disallows having any other parameters after the variadic parameter. It also requires the call site to be aware of this typing implementation and unpack the array. The method used in doSomething is messier but it does not require the call site to be aware of the typing method as the unpacking is performed within the function. It is also less ambiguous as the doSomethingElse would also accept n individual parameters where as doSomething only accepts an array. doSomething's method is also easier to strip away if native typed array support is ever added to PHP. Both of these methods only work for input parameters. An array return value type check would need to take place at the call site.

If strict_types is not enabled, it may be desirable to return the coerced scalar values from the type check function (e.g. floats and strings become integers) to ensure proper typing.
To Top