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