PHP 8.2.0 Beta 3 available for testing

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

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

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

Объявления типов могут быть помечены как обнуляемые, путём добавления префикса в виде знака вопроса(?). Это означает, что значение может быть как объявленного типа, так и быть равным 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

Составные типы

Можно комбинировать простые типы в составные типы. PHP позволяет комбинировать типы следующими способами:

  • Объединение простых типов. Начиная с PHP 8.0.0.
  • Пересечение типов классов (интерфейсы и имена классов). Начиная с PHP 8.1.0.
Предостережение

Невозможно комбинировать пересечения типов с объединением типов.

Объединение типов

Объединение типов позволяет использовать несколько типов, а не исключительно один. Для их объявления используется следующий синтаксис: T1|T2|.... Объединение типов доступно, начиная с PHP 8.0.0.

Объединение типов, допускающее значение null

Тип null можно использовать как часть объединения следующим образом: T1|T2|null. Существующая нотация ?T рассматривается как сокращение для T|null.

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

null не может использоваться как отдельный тип.

Псевдотип false

Псевдотип false поддерживается как часть объединения типов. Он добавлен по историческим причинам, так как многие встроенные функции возвращают false вместо null в случае возникновения ошибки. Классический пример - функция strpos().

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

false нельзя использовать как самостоятельный тип (включая обнуляемый вариант). Таким образом, все объявления такого типа недопустимы: false, false|null и ?false.

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

Обратите внимание, что псевдотип true не существует.

Пересечения типов

Объявление пересечения типов принимает значения, которые удовлетворяют нескольким объявлениям типа класса, а не одному. Пересечения типов указываются с использованием синтаксиса T1&T2&.... Пересечения типов доступны, начиная с PHP 8.1.0.

Дублирующиеся и повторяющиеся типы

Для отлова простых ошибок в составных объявлениях, повторяющиеся типы, которые можно отследить без загрузки класса, приведут к ошибке компиляции. В том числе:

  • Каждый тип, распознаваемый по имени, должен встречаться только один раз. Типы вида int|string|INT или Countable&Traversable&COUNTABLE приведут к ошибке.
  • Использование типа mixed приводит к ошибке.
  • Для объединения типов:
    • Если используется bool, то использовать дополнительно false нельзя.
    • Если используется тип object, то дополнительное использование типов классов недопустимо.
    • Если используется iterable, то к нему нельзя добавить array или Traversable.
  • Для пересечения типов:
    • Использование типа, который не является типом класса, приводит к ошибке.
    • Использование self, parent или static приводит к ошибке.

Замечание: Это не гарантирует, что все объединённые типы объявлены корректно, поскольку такая проверка потребует загрузки всех используемых классов.

К примеру, если 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 as B;
function 
foo(): A|{} // Запрещено ("use" является частью разрешения имён)
function foo(): A&{} // Запрещено ("use" является частью разрешения имён)
class_alias('X''Y');
function 
foo(): X|{} // Допустимо (повторение будет определено только во время выполнения)
function foo(): X&{} // Допустимо (повторение будет определено только во время выполнения)
?>

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

void

Тип void означает, что функция ничего не возвращает. Соответственно, он не может быть частью объединения. Доступно с PHP 7.1.0.

Замечание:

Возврат по ссылке из функции с возвращаемым типом void устарел с PHP 8.1.0, потому что такая функция противоречива. Ранее при вызове выдавалась следующая ошибка уровня E_NOTICE: Only variable references should be returned by reference.

<?php
function &test(): void {}
?>

never

never - тип возвращаемого значения, указывающий, что функция ничего не возвращает. Это означает, что она либо вызывает exit(), либо выбрасывает исключение, либо представляет собой бесконечный цикл. Следовательно, она не может быть частью объявления union-типа. Доступно с PHP 8.1.0.

never, говоря языком теории типов, является низшим типом. Это означает, что это подтип любого другого типа и может заменить любой другой тип возвращаемого значения при наследовании.

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

User Contributed Notes 5 notes

up
8
toinenkayt (ta at ta) [iwonderr] gmail d
1 year 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
5
anisgazig at example dot com
1 year 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
-1
crash
10 months 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
-5
Hayley Watson
10 months 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
-8
manoj904378 at gmail dot com
9 months 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