最人性化的特性:命名參數(shù)、聯(lián)合類型、mixed類型
這幾個(gè)新特性讓PHP在強(qiáng)類型方面進(jìn)一步完善,而且對PHPDoc的注釋依賴越來越弱,代碼即文檔的好處是開發(fā)者最頭疼的事情終于有辦法可以偷懶了。
命名參數(shù)
命名參數(shù)可以讓函數(shù)或者方法的調(diào)用更加清晰直觀,對于如下的函數(shù)定義
function foo(string $a, string $b, ?string $c = null, ?string $d = null)
{ /* … */ }你可以通過下面的方式傳入?yún)?shù)進(jìn)行調(diào)用
foo( b: 'value b', a: 'value a', d: 'value d', );
最大的好處是傳入?yún)?shù)的順序是和定義無關(guān)的,而且還可以混合傳參(但不建議)。
聯(lián)合類型
相對于以前的 PHPDoc 聲明類型的組合, 現(xiàn)在可以用原生支持的聯(lián)合類型聲明取而代之,可在實(shí)際運(yùn)行中驗(yàn)證。
PHP7
class Number {
/** @var int|float */
private $number;
/**
* @param float|int $number
*/
public function __construct($number) {
$this->number = $number;
}
}
new Number('NaN'); // OkPHP8
class Number {
public function __construct(
private int|float $number
) {}
}
new Number('NaN'); // TypeError新的 mixed類型
mixed本身是以下類型之一:
array
bool
callable
int
float
null
object
resource
string
注意,mixed也可以用作參數(shù)或?qū)傩灶愋停粌H僅是返回類型。
另外由于mixed已經(jīng)包含null,因此不允許將其設(shè)置為nullable。以下內(nèi)容將觸發(fā)錯(cuò)誤:
// Fatal error: Mixed types cannot be nullable, null is already part of the mixed type.
function bar(): ?mixed {}
最具貢獻(xiàn)的特性:JIT
JIT作為PHP底層編譯引擎,對于PHP8的性能貢獻(xiàn)是非常之大,不過對于常規(guī)WEB應(yīng)用來說,優(yōu)勢不明顯,但仍然是非常的高大上特性,是PHP8的扛鼎之作。
PHP 8 引入了兩個(gè)即時(shí)編譯引擎。 Tracing JIT 在兩個(gè)中更有潛力,它在綜合基準(zhǔn)測試中顯示了三倍的性能, 并在某些長時(shí)間運(yùn)行的程序中顯示了 1.5-2 倍的性能改進(jìn)。 典型的應(yīng)用性能則和 PHP 7.4 不相上下。
關(guān)于 JIT 對 PHP 8 性能的貢獻(xiàn)
Just-In-Time compilation
最實(shí)用的特性:構(gòu)造器屬性提升、Nullsafe運(yùn)算符、str_contains()、 str_starts_with()、 str_ends_with()
構(gòu)造器屬性提升
這個(gè)新的語法糖來用來創(chuàng)建值對象或數(shù)據(jù)傳輸對象。不用為類屬性和構(gòu)造函數(shù)指定它們,PHP 現(xiàn)在可以將它們合并為一個(gè)。
代替如下代碼:
class Money
{
public Currency $currency;
public int $amount;
public function __construct(
Currency $currency,
int $amount,
) {
$this->currency = $currency;
$this->amount = $amount;
}
}你可以這樣做:
class Money
{
public function __construct(
public Currency $currency,
public int $amount,
) {}
}nullsafe運(yùn)算符
現(xiàn)在可以用新的 nullsafe 運(yùn)算符鏈?zhǔn)秸{(diào)用,而不需要條件檢查 null。 如果鏈條中的一個(gè)元素失敗了,整個(gè)鏈條會(huì)中止并認(rèn)定為 Null。
$country = null;
if ($session !== null) {
$user = $session->user;
if ($user !== null) {
$address = $user->getAddress();
if ($address !== null) {
$country = $address->country;
}
}
}簡化為一行代碼
$country = $session?->user?->getAddress()?->country;
確實(shí)是有點(diǎn)酷
str_contains()、str_starts_with()和str_ends_with()函數(shù)
有些人可能會(huì)說它早就該有了,但我們終于不必再依賴strpos() 來知道字符串是否包含另一個(gè)字符串了。
代替如下:
if (strpos('string with lots of words', 'words') !== false) { /* … */ }你可以這樣做
if (str_contains('string with lots of words', 'words')) { /* … */ }感覺大多數(shù)場景應(yīng)該是不需要使用strpos了吧,外兩個(gè)早就應(yīng)該有了,str_starts_with()和str_ends_with()這兩個(gè)函數(shù)現(xiàn)在能省事不少。
str_starts_with('haystack', 'hay'); // true
str_ends_with('haystack', 'stack'); // true最具潛力的特性:注解、Match表達(dá)式、WeakMap
注解
現(xiàn)在可以用原生的PHP語法來使用結(jié)構(gòu)化的元數(shù)據(jù),而不需要再依賴PHPDoc解析,性能也隨之提升。之前定義注解路由可能需要使用:
class PostsController
{
/**
* @Route("/api/posts/{id}", methods={"GET"})
*/
public function get($id) { /* ... */ }
}現(xiàn)在你可以直接用PHP的注解語法來定義,并通過反射直接獲取
class PostsController
{
#[Route("/api/posts/{id}", methods: ["GET"])]
public function get($id) { /* ... */ }
}Match表達(dá)式
你可以稱它為switch表達(dá)式的大哥:match可以返回值,不需要break語句,可以組合條件,使用嚴(yán)格的類型比較,并且不執(zhí)行任何類型的強(qiáng)制。
如下所示:
$result = match($input) {
0 => "hello",
'1', '2', '3' => "world",
};WeakMap
WeakMap保留對對象的引用,這些引用不會(huì)阻止這些對象被垃圾回收。
以 ORM 為例,它們通常實(shí)現(xiàn)緩存,這些緩存保存對實(shí)體類的引用,以提高實(shí)體之間的關(guān)系性能。這些實(shí)體對象不能被垃圾回收,只要此緩存具有對它們的引用,即使緩存是唯一引用它們的對象。
如果此緩存層使用弱引用和映射代替,PHP 將垃圾收集這些對象當(dāng)再?zèng)]有別的引用他們了。特別是在 ORM 的情況下,它可以管理請求中的數(shù)百個(gè),如果不是數(shù)千個(gè)實(shí)體;weak maps可以提供更好、更資源友好的處理這些對象的方法。
下面是weak maps的示例:
class Foo
{
private WeakMap $cache;
public function getSomethingWithCaching(object $obj): object
{
return $this->cache[$obj]
??= $this->computeSomethingExpensive($obj);
}
}其它特性
0 == 'foobar' 終于返回了false
我們知道在PHP7里面
0 == 'foobar' // 返回true
現(xiàn)在終于看起來更比較符合邏輯了
0 == 'foobar' // 返回false
可以在對象上使用::class
一個(gè)小而有用的新特性:現(xiàn)在可以對對象使用::class,它的工作方式與 get_class() 相同。
$foo = new Foo(); var_dump($foo::class);
traits 中的抽象方法改進(jìn)
Traits 可以指定抽象方法,這些方法必須由使用它們的類實(shí)現(xiàn)。在PHP8,必須保持一致的方法定義,包括參數(shù)類型和返回類型。
trait MyTrait {
abstract private function neededByTheTrait(): string;
public function doSomething() {
return strlen($this->neededByTheTrait());
}
}
class TraitUser {
use MyTrait;
// This is allowed:
private function neededByTheTrait(): string { }
// This is forbidden (incorrect return type)
private function neededByTheTrait(): stdClass { }
// This is forbidden (non-static changed to static)
private static function neededByTheTrait(): string { }
}