類(lèi)型聲明可以用于函數(shù)的參數(shù)、返回值,PHP 7.4.0 起還可以用于類(lèi)的屬性,來(lái)顯性的指定需要的類(lèi)型,如果預(yù)期類(lèi)型在調(diào)用時(shí)不匹配,則會(huì)拋出一個(gè) TypeError 異常。
注意:
當(dāng)子類(lèi)覆蓋父方法時(shí),子類(lèi)的方法必須匹配父類(lèi)的類(lèi)型聲明。如果父類(lèi)沒(méi)有定義返回類(lèi)型,那么子方法可以指定自己的返回類(lèi)型。
類(lèi)型 | 說(shuō)明 | 版本 |
---|---|---|
類(lèi)/接口 名稱 |
值必須為指定類(lèi)和接口的實(shí)例化對(duì)象 instanceof
|
|
self |
值必須是用于類(lèi)型聲明相同類(lèi)的 instanceof 。
僅可在類(lèi)中使用。
|
|
parent |
值必須是用于類(lèi)型聲明父級(jí)類(lèi)的 instanceof ,
僅可在類(lèi)中使用。
|
|
array | 值必須為 array。 | |
callable | 值必定是一個(gè)有效的 callable。 不能用于類(lèi)屬性的類(lèi)型聲明。 | |
bool | 值必須為一個(gè)布爾值。 | |
float | 值必須為一個(gè)浮點(diǎn)數(shù)字。 | |
int | 值必須為一個(gè)整型數(shù)字。 | |
string | 值必須為一個(gè) string。 | |
iterable |
值必須為 array 或 instanceof Traversable。
|
PHP 7.1.0 |
object | 值必須為object。 | PHP 7.2.0 |
mixed | 值可以為任何類(lèi)型。 | PHP 8.0.0 |
不支持上述標(biāo)量類(lèi)型的別名。相反,它們被視為類(lèi)或接口名。例如,使用
boolean
作為類(lèi)型聲明,將要求值是一個(gè) instanceof
類(lèi)或接口
boolean
,而不能是類(lèi)型
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 等同于 聯(lián)合類(lèi)型 object|resource|array|string|int|float|bool|null。PHP 8.0.0 起可用。
示例 #1 在類(lèi)中使用類(lèi)型聲明
<?php
class C {}
class D extends C {}
// 它沒(méi)有 extend 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
示例 #2 在接口中使用類(lèi)型聲明
<?php
interface I { public function f(); }
class C implements I { public function f() {} }
// 它沒(méi)有 implement 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
示例 #3 返回類(lèi)型聲明
<?php
function sum($a, $b): float {
return $a + $b;
}
// 注意必須返回一個(gè) float。
var_dump(sum(1, 2));
?>
以上例程會(huì)輸出:
float(3)
示例 #4 返回一個(gè)對(duì)象
<?php
class C {}
function getC(): C {
return new C;
}
var_dump(getC());
?>
以上例程會(huì)輸出:
object(C)#1 (0) { }
自 PHP 7.1.0 起,類(lèi)型聲明允許前置一個(gè)問(wèn)號(hào) (?
)
用來(lái)聲明這個(gè)值允許為指定類(lèi)型,或者為 null
。
示例 #5 定義可空(Nullable)的參數(shù)類(lèi)型
<?php
class C {}
function f(?C $c) {
var_dump($c);
}
f(new C);
f(null);
?>
以上例程會(huì)輸出:
object(C)#1 (0) { } NULL
示例 #6 定義可空(Nullable)的返回類(lèi)型
<?php
function get_item(): ?string {
if (isset($_GET['item'])) {
return $_GET['item'];
} else {
return null;
}
}
?>
注意:
可以通過(guò)設(shè)置參數(shù)的默認(rèn)值為
null
來(lái)實(shí)現(xiàn)允許為空的參數(shù)。不建議這樣做,因?yàn)橛绊懙筋?lèi)的繼承調(diào)用。示例 #7 舊版本中實(shí)現(xiàn)允許為空參數(shù)的示例
<?php
class C {}
function f(C $c = null) {
var_dump($c);
}
f(new C);
f(null);
?>以上例程會(huì)輸出:
object(C)#1 (0) { } NULL
還可以組合簡(jiǎn)單類(lèi)型為復(fù)合類(lèi)型。 PHP 支持以下方式復(fù)合類(lèi)型:
無(wú)法復(fù)合交集類(lèi)型與聯(lián)合類(lèi)型。
聯(lián)合類(lèi)型接受多個(gè)不同的簡(jiǎn)單類(lèi)型做為參數(shù)。聲明聯(lián)合類(lèi)型的語(yǔ)法為
T1|T2|...
。聯(lián)合類(lèi)型自 PHP 8.0.0 起可用。
null
類(lèi)型允許在聯(lián)合類(lèi)型中使用,例如
T1|T2|null
代表接受一個(gè)空值為參數(shù)。已經(jīng)存在的 ?T
語(yǔ)法可以視為以下聯(lián)合類(lèi)型的一個(gè)簡(jiǎn)寫(xiě) T|null
。
null
不能作為一個(gè)獨(dú)立的類(lèi)型使用。
通過(guò)聯(lián)合類(lèi)型支持字面類(lèi)型(Literal Type)false
,
出于歷史原因,很多內(nèi)部函數(shù)在失敗時(shí)返回了 false
而不是 null
。
這類(lèi)函數(shù)的典型例子是 strpos()。
false
不能單獨(dú)作為類(lèi)型使用(包括可空 nullable 類(lèi)型)。
因此,不可以用 false
、false|null
、
?false
。
true
字面類(lèi)型不存在。
只要能滿足 class-type 的值,都可以在交集類(lèi)型聲明中使用,并且可使用多個(gè)值。
交集類(lèi)型用 T1&T2&...
這樣的語(yǔ)法指定。
交集類(lèi)型從 PHP 8.1.0 可以使用。
為了能在復(fù)合類(lèi)型聲明中暴露簡(jiǎn)單的 bug,不需要加載 class 就可以在編譯時(shí)讓重復(fù)冗余的類(lèi)型產(chǎn)生錯(cuò)誤。 包含:
int|string|INT
、
Countable&Traversable&COUNTABLE
會(huì)導(dǎo)致錯(cuò)誤。
注意: 不過(guò)它不能確保類(lèi)型最小化,因?yàn)橐_(dá)到這樣的效果,還要加載使用類(lèi)型的 class。
例如,假設(shè) A
和 B
都是一個(gè)類(lèi)的別名,
而 A|B
仍然是有效的,哪怕它可以被簡(jiǎn)化為
A
或
B
。
同樣的,如果 B extends A {}
,那 A|B
仍然是有效的聯(lián)合類(lèi)型,盡管它可以被簡(jiǎn)化為
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 {} // 允許 (運(yùn)行時(shí)才能知道重復(fù)性)
function foo(): X&Y {} // 允許 (運(yùn)行時(shí)才能知道重復(fù)性)
?>
void
是一個(gè)返回類(lèi)型,用于標(biāo)識(shí)函數(shù)沒(méi)有返回值。
它不能是聯(lián)合類(lèi)型的一部分。
PHP 7.1.0 起可用。
注意:
從 PHP 8.1.0 起棄用引用返回 void 的函數(shù), 因?yàn)檫@樣的函數(shù)是矛盾的。 之前,它在調(diào)用時(shí)已經(jīng)發(fā)出如下
E_NOTICE
:Only variable references should be returned by reference
。<?php
function &test(): void {}
?>
never
是一種表示沒(méi)有返回的返回類(lèi)型。這意味著它可能是調(diào)用 exit(),
拋出異?;蛘呤且粋€(gè)無(wú)限循環(huán)。所以,它不能作為聯(lián)合類(lèi)型的一部分。PHP 8.1.0 起可用。
在類(lèi)型理論用于中,never 是底層類(lèi)型。 意味著它是其它所有類(lèi)型的子類(lèi)型,并且可以在繼承期間替換任何其他返回類(lèi)型。
它的值必須是一個(gè) class 的 instanceof
,該 class 是調(diào)用方法所在的同一個(gè)類(lèi)。
PHP 8.0.0 起有效。
默認(rèn)如果可能,PHP 會(huì)強(qiáng)制轉(zhuǎn)化不合適的類(lèi)型為想要的標(biāo)量類(lèi)型。 比如,參數(shù)想要 string,傳入的是 int, 則會(huì)獲取 string 類(lèi)型的變量。
可以按文件開(kāi)啟嚴(yán)格模式。 在嚴(yán)格模式下,只能接受完全匹配的類(lèi)型,否則會(huì)拋出 TypeError。 唯一的例外是 int 值也可以傳入聲明為 float 的類(lèi)型。
通過(guò)內(nèi)部函數(shù)調(diào)用函數(shù)時(shí),不會(huì)受 strict_types
聲明影響。
要開(kāi)啟嚴(yán)格模式,使用 declare
開(kāi)啟
strict_types
:
注意:
文件開(kāi)啟嚴(yán)格類(lèi)型后的內(nèi)部調(diào)用函數(shù)將應(yīng)用嚴(yán)格類(lèi)型, 而不是在聲明函數(shù)的文件內(nèi)開(kāi)啟。 如果文件沒(méi)有聲明開(kāi)啟嚴(yán)格類(lèi)型,而被調(diào)用的函數(shù)所在文件有嚴(yán)格類(lèi)型聲明, 那將遵循調(diào)用者的設(shè)置(開(kāi)啟類(lèi)型強(qiáng)制轉(zhuǎn)化), 值也會(huì)強(qiáng)制轉(zhuǎn)化。
注意:
只有為標(biāo)量類(lèi)型的聲明開(kāi)啟嚴(yán)格類(lèi)型。
示例 #8 參數(shù)值的嚴(yán)格類(lèi)型
<?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
示例 #9 參數(shù)值的類(lèi)型強(qiáng)制轉(zhuǎn)化
<?php
function sum(int $a, int $b) {
return $a + $b;
}
var_dump(sum(1, 2));
// 以下會(huì)強(qiáng)制轉(zhuǎn)化為整型,注意以下內(nèi)容輸出!
var_dump(sum(1.5, 2.5));
?>
以上例程會(huì)輸出:
int(3) int(3)
示例 #10 返回值的嚴(yán)格類(lèi)型
<?php
declare(strict_types=1);
function sum($a, $b): int {
return $a + $b;
}
var_dump(sum(1, 2));
var_dump(sum(1, 2.5));
?>
以上例程會(huì)輸出:
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
沒(méi)有開(kāi)啟 strict_types
時(shí),標(biāo)量類(lèi)型可能會(huì)限制內(nèi)部隱式類(lèi)型轉(zhuǎn)化。
如果值的類(lèi)型不是聯(lián)合類(lèi)型中的一部分,則目標(biāo)類(lèi)型會(huì)按以下順序:
有一個(gè)例外:當(dāng)值是字符串,而 int 與 float 同時(shí)在組合中,將按現(xiàn)有的“數(shù)字字符串”檢測(cè)語(yǔ)義,識(shí)別首選的類(lèi)型。
例如,"42"
會(huì)選擇 int 類(lèi)型,
而 "42.0"
會(huì)選擇 float 類(lèi)型。
注意:
沒(méi)有出現(xiàn)在上面列表中的類(lèi)型則不是有效的內(nèi)部隱式轉(zhuǎn)化目標(biāo)。 尤其是不會(huì)出現(xiàn)內(nèi)部隱式轉(zhuǎn)化
null
和false
類(lèi)型。
示例 #11 類(lèi)型強(qiáng)制轉(zhuǎn)換為聯(lián)合類(lèi)型的例子
<?php
// int|string
42 --> 42 // 類(lèi)型完全匹配
"42" --> "42" // 類(lèi)型完全匹配
new ObjectWithToString --> "__toString() 的結(jié)果"
// object 不兼容 int,降級(jí)到 string
42.0 --> 42 // float 與 int 兼容
42.1 --> 42 // float 與 int 兼容
1e100 --> "1.0E+100" // float 比 int 大太多了,降級(jí)到 string
INF --> "INF" // float 比 int 大太多了,降級(jí)到 string
true --> 1 // bool 與 int 兼容
[] --> TypeError // array 不兼容 int 或 string
// int|float|bool
"45" --> 45 // int 的數(shù)字字符串
"45.0" --> 45.0 // float 的數(shù)字字符串
"45X" --> true // 不是數(shù)字字符串,降級(jí)到 bool
"" --> false // 不是數(shù)字字符串,降級(jí)到 bool
"X" --> true // 不是數(shù)字字符串,降級(jí)到 bool
[] --> TypeError // array 不兼容 int、float、bool
?>
示例 #12 傳引用參數(shù)的類(lèi)型
僅僅會(huì)在函數(shù)入口檢查傳引用的參數(shù)類(lèi)型,而不是在函數(shù)返回時(shí)檢查。 所以函數(shù)返回時(shí),參數(shù)類(lèi)型可能會(huì)發(fā)生變化。
<?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 $a, int $b) {
return $a + $b;
}
try {
var_dump(sum(1, 2));
var_dump(sum(1.5, 2.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