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

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

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

Замечание:

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

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

Версия Описание
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
add a note

User Contributed Notes 5 notes

up
19
toinenkayt (ta at ta) [iwonderr] gmail d
2 years 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.
up
11
anisgazig at example dot com
2 years 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
7
crash
1 year ago
The documentation lacks the information, that it's possible to change the return type of a method defined in an interface when the interface's methods return type is defined as `mixed`.

From the RFC:

"The mixed return type could be narrowed in a subclass as this is covariant and is allowed in LSP." (https://wiki.php.net/rfc/mixed_type_v2)

This means the following code is valid in PHP 8.0:

<?php

interface ITest
{
public function
apfel(): mixed; // valid as of 8.0
}

class
Test implements ITest
{
public function
apfel(): array // more explicit
{
return [];
}
}

var_dump((new Test())->apfel());
?>

You can see the result here: https://3v4l.org/PXDB6
up
-1
Hayley Watson
1 year ago
Not explicit is that function parameters can also have union type declarations; they're not just for property and return value declarations.

<?php

declare(strict_types = 1);

function
foo(int|string $arg)
{
if(
is_string($arg))
{
echo
"It's a string!\n";
}
elseif(
is_int($arg))
{
echo
"It's an integer!\n";
}
else
{
echo
"It shouldn't be here!\n";
}
}

foo(42);
foo('bar');
foo([]);
up
-12
manoj904378 at gmail dot com
1 year ago
PHP Version 7.4
---
true=1;
false=2;

<?php
function a(bool $a,bool $b){
return
$a+$b;
}

var_dump(a(false,3.9));
?>
Output
------
int(1)
To Top