型宣言

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

注意:

親クラスのメソッドをオーバーライドする場合、 子クラスのメソッドは、 親クラスで宣言された戻り値の型のいずれかに一致させなければなりません。 親クラスが戻り値の型を宣言していない場合、 子クラスのメソッドもそうしなければなりません。

基本的な型

説明 バージョン
クラス/インターフェイス の名前 値が、指定したクラスやインターフェイスのインスタンスでなければいけません。  
self 値が、型宣言が行われているクラスと同じクラスのインスタンスでなければいけません。 クラスの内部でのみ使えます。  
parent 値が、型宣言が行われたクラスの、親クラスのインスタンスでなければいけません。 クラスの内部でのみ使えます。  
array 値が、配列でなければいけません。  
callable 値が、callable でなければいけません。 クラスのプロパティの型では宣言できません。  
bool 値が、bool 値でなければいけません。  
float 値が、float でなければいけません。  
int 値が、int でなければいけません。  
string 値が、文字列でなければいけません。  
iterable 値が、配列か、Traversable のインスタンスでなければなりません。 PHP 7.1.0 以降
object 値が、object でなければなりません。 PHP 7.2.0 以降
mixed 値は、あらゆる値であることができます。 PHP 8.0.0 以降
警告

上記のスカラー型のエイリアスはサポートされていません。 つまり、これらはクラスやインターフェイスの名前として扱われているということです。 たとえば、型の宣言に 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

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

// float が返される点に注意
var_dump(sum(12));
?>

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

float(3)

例4 オブジェクトを返す

<?php
class {}

function 
getC(): {
    return new 
C;
}

var_dump(getC());
?>

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

object(C)#1 (0) {
}

Null を許容する型宣言

PHP 7.1.0 以降では、型の名前の前にクエスチョンマーク (?) を付けることで、nullable であるという印を付けられるようになりました。 こうすることで、その値の型が指定されたものか、null であることを指定できます。

例5 Null を許容する型宣言

<?php
class {}

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

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

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

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

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

<?php
function get_item(): ?string {
    if (isset(
$_GET['item'])) {
        return 
$_GET['item'];
    } else {
        return 
null;
    }
}
?>

注意:

デフォルト値に null を指定することで null を許容する引数を指定することができます。 これは、継承関係を破壊するので推奨できません。

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

<?php
class {}

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

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

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

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

複合型

単一の型を組み合わせて、複合型にすることができます。 PHP では、以下のやり方で型を組み合わせることができます:

  • 単一の型の集合を表す union型。 PHP 8.0.0 以降で利用可能です。
  • クラス/インターフェイス の 交差型。 PHP 8.1.0 以降で利用可能です。
警告

交差型 を union型 と組み合わせて使うことはできません。

union 型

型を union として宣言すると、ひとつではなく、 複数の異なる型を値として受け入れることができます。 union 型は、T1|T2|... という文法を使って指定します。 union 型は、PHP 8.0.0 以降で利用可能です。

Nullを受け入れる union 型

null 型も union 型の一部としてサポートされています。 null を受け入れる union を作るために、 T1|T2|null のような指定をすることができます。 既にある ?T 記法は、 よくある T|null の短縮記法と考えられます。

警告

null は、単独の、独立した型として使うことは出来ません。

false 疑似型

false リテラルの型が、union の一部としてサポートされています。 歴史的な理由で、多くの内部関数が失敗時に null ではなく false を返しているためです。 strpos() 関数は、このような関数の典型例です。

警告

false 疑似型は、単独の、独立した型 (単独な null を許容する型としても) として使うことは出来ません。 したがって、false, false|null, ?false はいずれも許されません。

警告

true リテラルの型は存在 しません

交差型

型を 交差型 として宣言すると、 クラス/インターフェイス として宣言された型を (単一ではなく)複数すべて満たす値を受け入れることができます。 交差型は、T1&T2&... という文法を使って指定します。 交差型は、PHP 8.1.0 以降で利用可能です。

重複した冗長な型

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

  • 名前が解決された型は、一度しか現れることができません。 int|string|INTCountable&Traversable&COUNTABLE のような型はエラーになります。
  • mixed 型を複合型で使うとエラーになります。
  • union 型に適用される制限:
    • bool 型が使われている場合、false 疑似型は追加で使えません。
    • object 型が使われている場合、クラスの型は追加で使えません。
    • iterable 型が使われている場合、 arrayTraversable は追加で使えません。
  • 交差型に適用される制限:
    • クラス/インターフェイス として宣言されていない型を、 交差型で使うとエラーになります。
    • self, parent, static のいずれかを、交差型で使うとエラーになります。

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

たとえば、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 as B;
function 
foo(): A|{} // 許されません ("use" は名前解決の一部です)
function foo(): A&{} // 許されません ("use" は名前解決の一部です)

class_alias('X''Y');
function 
foo(): X|{} // 問題ありません (冗長かどうかは、実行時にだけわかります)
function foo(): X&{} // 問題ありません (冗長かどうかは、実行時にだけわかります)
?>

戻り値でのみ有効な型

void

void は、関数が値を返さないことを示す戻り値の型です。 よって、この型は union 型の一部として指定することが出来ません。 PHP 7.1.0 以降で利用できます。

注意:

void を返す関数からリファレンスを返すことは、PHP 8.1.0 以降は推奨されなくなりました。 なぜなら、関数の定義そのものが矛盾しているからです。 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

値が、メソッドが呼び出されているクラスと同じインスタンスでなければなりません。 PHP 8.0.0 以降で利用できます。

厳密な型付け

デフォルトでは、PHP は誤った型の値を 可能であれば期待されたスカラー型の宣言に従うよう自動的に変換します(coercive モード)。 たとえば、関数に int が与えられたが、 引数に文字列が期待されていた場合、 文字列型の値を取得します。

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

警告

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

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

注意:

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

注意:

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

例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

union 型と型の自動変換

strict_types が有効になっていない場合、 スカラー型の宣言は、限られた暗黙の型変換が行われます。 値の正確な型が union の一部に指定されていない場合、 次の順に対象となる型が選択されます:

  1. int
  2. float
  3. string
  4. bool
正確な型が union の中に存在するか、 値が PHP の既存の型チェックのセマンティクスによって型変換できる場合、 その型が選択されます。 そうでない場合、次の型を試そうとします。

警告

例外として、値が文字列で、int と float が union に含まれていた場合、 型は既存の "数値形式の文字列" を解釈するセマンティクスによって決まります。 たとえば、"42" の場合、 int が選ばれますし、 "42.0" の場合、float が選ばれます。

注意:

上のリストに入っていない型については、暗黙の型変換が行われる対象ではありません。 特に、暗黙のうちに nullfalse に変換されることはありません。

例11 union の型のひとつに変換される例

<?php
// int|string
42    --> 42          // 正確に型が一致
"42"  --> "42"        // 正確に型が一致
new ObjectWithToString --> "Result of __toString()"
                      
// オブジェクトは int と互換性がないので、文字列にフォールバック
42.0  --> 42          // float は int と互換性がある
42.1  --> 42          // float は int と互換性がある
1e100 --> "1.0E+100"  // int には大きすぎる float なので、文字列にフォールバック
INF   --> "INF"       // int には大きすぎる float なので、文字列にフォールバック
true  --> 1           // bool は int と互換性がある
[]    --> TypeError   // 配列はint, string と互換性はない。

// int|float|bool
"45"    --> 45        // int の数値形式の文字列
"45.0"  --> 45.0      // float の文字列

"45X"   --> true      // 数値形式の文字列ではない。boolにフォールバック
""      --> false     // 数値形式の文字列ではない。boolにフォールバック
"X"     --> true      // 数値形式の文字列ではない。boolにフォールバック
[]      --> TypeError // 配列は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 
'Error: '$e->getMessage();
}
?>

上の例の PHP 8 での出力は、このようになります。:

int(3)
Error: sum(): Argument #1 ($a) must be of type int, float given, called in - on line 10
関連キーワード:  宣言, クラス, union, int, 関数, 出力, float, TypeError, 交差, 利用