PHP 8.3.4 Released!

型宣言

関数のパラメータや戻り値、 クラスのプロパティ (PHP 7.4.0 以降) に対して型を宣言することができます。 これによって、その値が特定の型であることを保証できます。 その型でない場合は、TypeError がスローされます。

PHP がサポートしている単一の型それぞれを、 ユーザーが行う型宣言の中で使うことができます。 但し、resource 型を除きます。 このページでは、それぞれの型がいつ利用可能になったかの変更履歴や、 型宣言におけるそれらの使い方について記しています。

注意:

クラスがインターフェイスのメソッドを実装したり、 親クラスで既に定義されているメソッドを再実装する場合、 そのメソッドは、既に存在するメソッドと互換性がなければなりません。 共変性と反変性 のルールに従っている場合、メソッドには互換性があります。

変更履歴

バージョン 説明
8.3.0 クラス、インターフェイス、トレイト、そして列挙型の定数は、新たに型宣言をサポートするようになりました。
8.2.0 DNF 型のサポートが追加されました。
8.2.0 リテラル型 true のサポートが追加されました。
8.2.0 nullfalse 型が、独立した型として使えるようになりました。
8.1.0 交差型のサポートが追加されました。
8.1.0 戻り値を void とした関数からリファレンスを返すことは、 推奨されなくなりました。
8.1.0 戻り値にのみ指定できる型として、 never 型のサポートが追加されました。
8.0.0 mixed 型のサポートが追加されました。
8.0.0 戻り値にのみ指定できる型として、 static 型のサポートが追加されました。
8.0.0 union 型のサポートが追加されました。
7.4.0 クラスのプロパティに、型宣言のサポートが追加されました。
7.2.0 object 型のサポートが追加されました。
7.1.0 iterable 型のサポートが追加されました。
7.1.0 void 型のサポートが追加されました。
7.1.0 nullable な型のサポートが追加されました。

基本型を使うときの注意

基本型は、ここで説明する小さな注意事項はいくつかあるものの、 わかりやすい振る舞いをします。

スカラー型

警告

スカラー型(bool, int, float, string) のエイリアスはサポートされていません。 つまり、これらはクラスやインターフェイスの名前として扱われているということです。 たとえば、型の宣言に boolean を使った場合、 値が 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 以降は推奨されなくなりました。 なぜなら、関数の定義そのものが矛盾しているからです。 PHP 8.1.0 より前のバージョンでは、 関数をコールした際に次のような E_NOTICE が発生していました: Only variable references should be returned by reference

<?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 型がサポートされる前は、 交差型とunion型を組み合わせることはできませんでした。

union 型

警告

union 型の中で、Value型 truefalse を同時に組み合わせて使うことはできません。 bool 型を使ってください。

警告

PHP 8.2.0 より前のバージョンでは、 falsenull 型は 独立した型として使えず、 union 型でそれらだけを指定することも許されませんでした。 つまり、false, false|null, ?false のような型はいずれも許されませんでした。

nullable な型とシンタックスシュガー

単一の基本型を宣言した場合、 型の名前の前にクエスチョンマーク (?) を付けることで、nullable であるという印を付けることができます。 よって、?TT|null は同じ意味です。

注意: この文法は、PHP 7.1.0 以降でサポートされており、 一般化された union 型がサポートされる前から存在します。

注意:

デフォルト値に null を指定することで、 null を許容するパラメータを指定することができます。 これは、子クラスでデフォルト値が変更された場合にクラスの互換性が壊れ、 型宣言で null 型を追加しなければならなくなるため、おすすめできません。

例2 引数にnullを許容する古いやり方

<?php
class C {}

function
f(C $c = null) {
var_dump($c);
}

f(new C);
f(null);
?>

上の例の出力は以下となります。

object(C)#1 (0) {
}
NULL

重複した冗長な型

複合型の宣言に関する単純なバグを見つけるため、 クラスの読み込みを行わずに検出できる冗長な型はコンパイル時にエラーになります。 たとえば、以下のような場合です:

  • 名前が解決された型は、一度しか現れることができません。 int|string|INTCountable&Traversable&COUNTABLE のような型はエラーになります。
  • mixed 型を複合型で使うとエラーになります。
  • union 型に適用される制限:
    • bool 型が使われている場合、falsetrue 型は追加で使えません。
    • object 型が使われている場合、クラス型は追加で使えません。
    • iterable 型が使われている場合、 arrayTraversable は追加で使えません。
  • 交差型に適用される制限:
    • クラス型でない型を、交差型で使うとエラーになります。
    • self, parent, static のいずれかを、交差型で使うとエラーになります。
  • DNF 型に適用される制限:
    • より広い型をひとつ使った場合、 それより狭い型は冗長とみなされます。
    • ふたつの等しい交差型を使った場合、冗長とみなされます。

注意: これによって、型が "最低限" であることは保証しません。 なぜなら、最低限であることを保証するためには、 使われている全てのクラスの型を読み込まなければならないからです。

たとえば、AB がクラスのエイリアスだったとします。 この場合、A|BA または B のみに縮めることができますが、 正しい union 型です。 同様に、B extends A {} というクラスがあった場合、 A|BA のみに縮めることができますが、 正しい union 型です。

<?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 Null を許容する型宣言

<?php
class C {}

function
f(?C $c) {
var_dump($c);
}

f(new C);
f(null);
?>

上の例の出力は以下となります。

object(C)#1 (0) {
}
NULL

例8 Null を許容する戻り値の型宣言

<?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 は誤った型の値を 可能であれば期待されたスカラー型の宣言に従うよう自動的に変換します(coercive モード)。 たとえば、関数に int が与えられたが、 パラメータで文字列が期待されていた場合、文字列型の値を取得します。

ファイルごとに strict モードを有効にすることができます。 strict モードでは、型宣言に正確に対応する値のみを受け入れ、 それ以外の値の場合、TypeError がスローされます。 このルールに関する唯一の例外は、int の値が float 型の宣言に渡せることだけです。

警告

内部関数の中からの関数呼び出しは、 strict_types 宣言の影響を受けません。

strict モードを有効にするには、declare 文を strict_types 宣言と一緒に使います。

注意:

厳密な型付けは、strict モードが有効になったファイルの 内部 から行われる関数呼び出しに適用されます。 そのファイルで宣言された関数への呼び出しに対して適用されるわけではありません。 厳密な型付けが有効になっていないファイルから、 厳密な型付けが有効になっているファイルで定義された関数を呼び出した場合は、 呼び出し側の好み(型の自動変換)が尊重され、値は型変換されます。

注意:

厳密な型付けは、スカラー型の宣言に対してのみ定義されます。

例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 2 notes

up
18
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
12
crash
2 years 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
To Top