類型聲明

類型聲明可以用于函數(shù)的參數(shù)、返回值,PHP 7.4.0 起還可以用于類的屬性,來顯性的指定需要的類型,如果預期類型在調(diào)用時不匹配,則會拋出一個 TypeError 異常。

注意:

當子類覆蓋父方法時,子類的方法必須匹配父類的類型聲明。如果父類沒有定義返回類型,那么子方法可以指定自己的返回類型。

單一類型

類型 說明 版本
類/接口 名稱 值必須為指定類和接口的實例化對象 instanceof  
self 值必須是用于類型聲明相同類的 instanceof 。 僅可在類中使用。  
parent 值必須是用于類型聲明父級類的 instanceof , 僅可在類中使用。  
array 值必須為 array。  
callable 值必定是一個有效的 callable。 不能用于類屬性的類型聲明。  
bool 值必須為一個布爾值。  
float 值必須為一個浮點數(shù)字。  
int 值必須為一個整型數(shù)字。  
string 值必須為一個 string  
iterable 值必須為 arrayinstanceof 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 等同于 聯(lián)合類型 object|resource|array|string|int|float|bool|null。PHP 8.0.0 起可用。

范例

示例 #1 在類中使用類型聲明

<?php
class {}
class 
extends {}

// 它沒有 extend 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() {} }

// 它沒有 implement 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) {
}

允許為空的(Nullable)類型

自 PHP 7.1.0 起,類型聲明允許前置一個問號 (?) 用來聲明這個值允許為指定類型,或者為 null。

示例 #5 定義可空(Nullable)的參數(shù)類型

<?php
class {}

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ù)的默認值為 null 來實現(xiàn)允許為空的參數(shù)。不建議這樣做,因為影響到類的繼承調(diào)用。

示例 #7 舊版本中實現(xiàn)允許為空參數(shù)的示例

<?php
class {}

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

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

以上例程會輸出:

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

復合類型

還可以組合簡單類型為復合類型。 PHP 支持以下方式復合類型:

  • 簡單類型聯(lián)合。PHP 8.0.0 起可用。
  • class-type(接口、類名)類型交集。PHP 8.1.0 起可用。
警告

無法復合交集類型與聯(lián)合類型。

聯(lián)合類型

聯(lián)合類型接受多個不同的簡單類型做為參數(shù)。聲明聯(lián)合類型的語法為 T1|T2|...。聯(lián)合類型自 PHP 8.0.0 起可用。

允許為空的聯(lián)合類型

null 類型允許在聯(lián)合類型中使用,例如 T1|T2|null 代表接受一個空值為參數(shù)。已經(jīng)存在的 ?T 語法可以視為以下聯(lián)合類型的一個簡寫 T|null

警告

null 不能作為一個獨立的類型使用。

false 偽類型

通過聯(lián)合類型支持字面類型(Literal Type)false, 出于歷史原因,很多內(nèi)部函數(shù)在失敗時返回了 false 而不是 null。 這類函數(shù)的典型例子是 strpos()。

警告

false 不能單獨作為類型使用(包括可空 nullable 類型)。 因此,不可以用 falsefalse|null、 ?false

警告

true 字面類型不存在。

交集類型

只要能滿足 class-type 的值,都可以在交集類型聲明中使用,并且可使用多個值。 交集類型用 T1&T2&... 這樣的語法指定。 交集類型從 PHP 8.1.0 可以使用。

重復冗余的類型

為了能在復合類型聲明中暴露簡單的 bug,不需要加載 class 就可以在編譯時讓重復冗余的類型產(chǎn)生錯誤。 包含:

  • 解析出來的類型只能出現(xiàn)一次。例如這樣的類型 int|string|INT、 Countable&Traversable&COUNTABLE 會導致錯誤。
  • 使用 mixed 會導致錯誤。
  • 對于聯(lián)合類型:
    • 使用了 bool 時就不能再附帶使用 false
    • 使用了 object 時就不能再附帶使用 class 類型。
    • 使用了 iterable 時,array、 Traversable 都不能再附帶使用。
  • 對于交集類型:
    • 使用 class-type 以外的類型會導致錯誤。
    • 使用 self、parent、 static 都會導致錯誤。

注意: 不過它不能確保類型最小化,因為要達到這樣的效果,還要加載使用類型的 class。

例如,假設 AB 都是一個類的別名, 而 A|B 仍然是有效的,哪怕它可以被簡化為 AB。 同樣的,如果 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 as B;
function 
foo(): A|{} // 不允許 ("use" 是名稱解析的一部分)
function foo(): A&{} // 不允許 ("use" 是名稱解析的一部分)

class_alias('X''Y');
function 
foo(): X|{} // 允許 (運行時才能知道重復性)
function foo(): X&{} // 允許 (運行時才能知道重復性)
?>

僅僅返回類型

void

void 是一個返回類型,用于標識函數(shù)沒有返回值。 它不能是聯(lián)合類型的一部分。 PHP 7.1.0 起可用。

注意:

從 PHP 8.1.0 起棄用引用返回 void 的函數(shù), 因為這樣的函數(shù)是矛盾的。 之前,它在調(diào)用時已經(jīng)發(fā)出如下 E_NOTICEOnly variable references should be returned by reference。

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

never

never 是一種表示沒有返回的返回類型。這意味著它可能是調(diào)用 exit(), 拋出異常或者是一個無限循環(huán)。所以,它不能作為聯(lián)合類型的一部分。PHP 8.1.0 起可用。

在類型理論用于中,never 是底層類型。 意味著它是其它所有類型的子類型,并且可以在繼承期間替換任何其他返回類型。

static

它的值必須是一個 class 的 instanceof,該 class 是調(diào)用方法所在的同一個類。 PHP 8.0.0 起有效。

嚴格類型

默認如果可能,PHP 會強制轉(zhuǎn)化不合適的類型為想要的標量類型。 比如,參數(shù)想要 string,傳入的是 int, 則會獲取 string 類型的變量。

可以按文件開啟嚴格模式。 在嚴格模式下,只能接受完全匹配的類型,否則會拋出 TypeError。 唯一的例外是 int 值也可以傳入聲明為 float 的類型。

警告

通過內(nèi)部函數(shù)調(diào)用函數(shù)時,不會受 strict_types 聲明影響。

要開啟嚴格模式,使用 declare 開啟 strict_types

注意:

文件開啟嚴格類型后的內(nèi)部調(diào)用函數(shù)將應用嚴格類型, 而不是在聲明函數(shù)的文件內(nèi)開啟。 如果文件沒有聲明開啟嚴格類型,而被調(diào)用的函數(shù)所在文件有嚴格類型聲明, 那將遵循調(diào)用者的設置(開啟類型強制轉(zhuǎn)化), 值也會強制轉(zhuǎn)化。

注意:

只有為標量類型的聲明開啟嚴格類型。

示例 #8 參數(shù)值的嚴格類型

<?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 參數(shù)值的類型強制轉(zhuǎn)化

<?php
function sum(int $aint $b) {
    return 
$a $b;
}

var_dump(sum(12));

// 以下會強制轉(zhuǎn)化為整型,注意以下內(nèi)容輸出!
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

聯(lián)合類型的內(nèi)部隱式強制轉(zhuǎn)化

沒有開啟 strict_types 時,標量類型可能會限制內(nèi)部隱式類型轉(zhuǎn)化。 如果值的類型不是聯(lián)合類型中的一部分,則目標類型會按以下順序:

  1. int
  2. float
  3. string
  4. bool
如果類型出現(xiàn)在組合中,值可以按 PHP 現(xiàn)有的類型語義檢測進行內(nèi)部隱式強制轉(zhuǎn)化,則會選擇該類型。 否則會嘗試下一個類型。

警告

有一個例外:當值是字符串,而 int 與 float 同時在組合中,將按現(xiàn)有的“數(shù)字字符串”檢測語義,識別首選的類型。 例如,"42" 會選擇 int 類型, 而 "42.0" 會選擇 float 類型。

注意:

沒有出現(xiàn)在上面列表中的類型則不是有效的內(nèi)部隱式轉(zhuǎn)化目標。 尤其是不會出現(xiàn)內(nèi)部隱式轉(zhuǎn)化 nullfalse 類型。

示例 #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 $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