PHP8 類型聲明

2023-08-14 17:42 更新

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

PHP 支持各種單一類型,除了 resource 之外,都可以用于用戶級(jí)別類型聲明。這個(gè)頁(yè)面包含了不同類型間的可用性變更日志以及在類型聲明中用法的文檔。

注意:當(dāng)類實(shí)現(xiàn)了接口方法或者重新實(shí)現(xiàn)了父級(jí)類中定義的方法時(shí),必須與上述定義兼容。如果方法遵循方差規(guī)則,則兼容該方法。

更新日志

版本 說(shuō)明
8.2.0 新增對(duì) DNF 類型的支持。
8.2.0 新增對(duì) literal 類型 true 的支持。
8.2.0 現(xiàn)在可以單獨(dú)使用 null 和 false。
8.1.0 新增對(duì)交集類型的支持。
8.1.0 棄用 void 函數(shù)通過(guò)引用返回。
8.1.0 新增對(duì)返回類型 never 的支持。
8.0.0 新增對(duì) mixed 類型的支持。
8.0.0 新增對(duì)返回類型 static 的支持。
8.0.0 新增對(duì)聯(lián)合類型的支持。
7.4.0 新增對(duì)類屬性類型化的支持。
7.2.0 新增對(duì) object 類型的支持。
7.1.0 新增對(duì) iterable 類型的支持。
7.1.0 新增對(duì) void 類型的支持。
7.1.0 新增對(duì)可為 null 類型的支持。

基本類型使用說(shuō)明

Base types have straight forward behaviour with some minor caveats which are described in this section.

標(biāo)量類型

警告

標(biāo)量類型(bool、int、float、string)不支持別名。別名反而會(huì)視為類或接口名。例如,使用 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

void

注意:從 PHP 8.1.0 起棄用 void 函數(shù)通過(guò)引用返回,因?yàn)檫@樣的函數(shù)自相矛盾。在此之前調(diào)用時(shí)總是會(huì)發(fā)出如下 E_NOTICE:Only variable references should be returned by reference。

Callable 類型

此類型不能用于類屬性的類型聲明。

注意: 無(wú)法指定函數(shù)的簽名。

通過(guò)引用傳遞的參數(shù)類型

如果通過(guò)引用傳遞的參數(shù)有類型聲明,則變量的類型僅在調(diào)用函數(shù)時(shí)檢查,返回時(shí)不會(huì)檢查。這意味著函數(shù)可以改變引用變量的類型。

示例 #1 通過(guò)引用傳遞的參數(shù)類型

<?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

復(fù)合類型使用說(shuō)明

復(fù)合類型聲明有幾個(gè)限制,并且在編譯時(shí)執(zhí)行冗余檢查以避免簡(jiǎn)單的錯(cuò)誤。

警告

在 PHP 8.2 之前,也就是沒引入 DNF 之前,交集類型和聯(lián)合類型不能組合使用。

聯(lián)合類型

警告

在一個(gè)聯(lián)合類型中不能同時(shí)用兩個(gè) literal 類型 false 和 true。而是使用 bool 替代。

警告

在 PHP 8.2.0 之前,由于 false 和 null 不能作為獨(dú)立的類型使用,因此不允許僅由這些類型組成聯(lián)合類型。這還包括以下類型:false、false|null, 和 ?false。

可為 null 類型語(yǔ)法糖

單個(gè)基本類型聲明可以通過(guò)在類型前添加問號(hào)(?)來(lái)標(biāo)記可為 null。因此 ?T 和 T|null 是相同的。

注意: 該語(yǔ)法自 PHP 7.1.0 起支持,且早于完整的(generalized)聯(lián)合類型支持。
注意:也可以通過(guò)設(shè)置參數(shù)的參數(shù)的默認(rèn)值為 null 來(lái)實(shí)現(xiàn)允許為 null。但并不建議這么做,因?yàn)槿绻谧宇愔懈牧四J(rèn)值,會(huì)引發(fā)類型兼容沖突,需要將 null 類型添加到類型聲明中。示例 #2 使參數(shù)可以為 null 的舊方法 以上示例會(huì)輸出:object(C)#1 (0) { } NULL

重復(fù)冗余的類型

為了能捕獲復(fù)合類型聲明中的簡(jiǎn)單錯(cuò)誤,不需要類加載檢測(cè)到的冗余類型將導(dǎo)致編譯時(shí)錯(cuò)誤。包含:

  • 解析出來(lái)的類型只能出現(xiàn)一次。例如這樣的類型 int|string|INT 或 Countable&Traversable&COUNTABLE 會(huì)導(dǎo)致錯(cuò)誤。
  • 使用 mixed 會(huì)導(dǎo)致錯(cuò)誤。
  • 對(duì)于聯(lián)合類型:使用了 bool 時(shí)就不能再附帶使用 false 或者 true。使用了 object 時(shí)就不能再附帶使用 class 類型。使用了 iterable 時(shí),不能再附帶使用 array 和 Traversable。
  • 對(duì)于交集類型:使用 class-type 以外的類型會(huì)導(dǎo)致錯(cuò)誤。使用 self、parent 或 static 都會(huì)導(dǎo)致錯(cuò)誤。
  • DNF 類型:If a more generic type is used, the more restrictive one is redundant.使用兩個(gè)相同的交集類型。
注意: 不過(guò)它不能確保類型最小化,因?yàn)橐_(dá)到這樣的效果,還要加載使用類型的 class。

例如,假設(shè) A 和 B 都是一個(gè)類的別名, 而 A|B 仍然是有效的,哪怕它可以被簡(jiǎn)化為 A 或 B。 同樣的,如果 B extends A {},那 A|B 仍然是有效的聯(lián)合類型,盡管它可以被簡(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ù)性)
?>

示例

示例 #3 基礎(chǔ)類類型聲明

<?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 基礎(chǔ)接口類型聲明

<?php
interface I { public function f(); }
class C implements I { public function f() {} }

// 沒有實(shí)現(xiàn)(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

示例 #5 基礎(chǔ)返回類型聲明

<?php
function sum($a, $b): float {
return $a + $b;
}

// Note that a float will be returned.
var_dump(sum(1, 2));
?>

以上示例會(huì)輸出:

float(3)

示例 #6 返回對(duì)象

<?php
class C {}

function getC(): C {
return new C;
}

var_dump(getC());
?>

以上示例會(huì)輸出:

object(C)#1 (0) {
}

示例 #7 可為 null 參數(shù)類型聲明

<?php
class C {}

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

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

以上示例會(huì)輸出:

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

嚴(yán)格類型

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

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

警告

通過(guò)內(nèi)部函數(shù)調(diào)用函數(shù)時(shí),不會(huì)受 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è)置(開啟類型強(qiáng)制轉(zhuǎn)化), 值也會(huì)強(qiáng)制轉(zhuǎn)化。
注意:只有為標(biāo)量類型的聲明開啟嚴(yán)格類型。

示例 #10 參數(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

示例 #11 參數(shù)值的類型強(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)

示例 #12 返回值的嚴(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));
?>

以上示例會(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


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)