PHP 8.0.0 已經正式發佈了,這個對於 PHPer 無疑是一個使人振奮的消息。它包含了不少新功能與優化項, 包括命名參數、聯合類型、註解、構造器屬性提高、match 表達式、nullsafe 運算符、JIT,並改進了類型系統、錯誤處理、語法一致性。
最人性化的特性:命名參數、聯合類型、mixed 類型
這幾個新特性讓 PHP 在強類型方面進一步完善,並且對 PHPDoc 的註釋依賴愈來愈弱,代碼即文檔的好處是開發者最頭疼的事情終於有辦法能夠偷懶了。
命名參數
命名參數可讓函數或者方法的調用更加清晰直觀,對於以下的函數定義
function foo(string $a, string $b, ?string $c = null, ?string $d = null)
{ /* … */ }
你能夠經過下面的方式傳入參數進行調用
foo(
b: 'value b',
a: 'value a',
d: 'value d',
);
最大的好處是傳入參數的順序是和定義無關的,並且還能夠混合傳參(但不建議)。
聯合類型
相對於之前的 PHPDoc 聲明類型的組合,如今能夠用原生支持的聯合類型聲明取而代之,可在實際運行中驗證。
PHP7
class Number {
/** @var int|float */
private $number;
/**
* @param float|int $number
*/public function __construct($number) {
$this->number = $number;
}
}new Number('NaN'); // Ok
PHP8
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 也能夠用做參數或屬性類型,而不只僅是返回類型。
另外因爲 mixed 已經包含 null,所以不容許將其設置爲 nullable。如下內容將觸發錯誤:
// Fatal error: Mixed types cannot be nullable, null is already part of the
mixed type. function bar(): ?mixed {}
最具貢獻的特性:JIT
JIT 做爲 PHP 底層編譯引擎,對於 PHP8 的性能貢獻是很是之大,不過對於常規WEB 應用來講,優點不明顯,但仍然是很是的高大上特性,是 PHP8 的扛鼎之做。
PHP 8 引入了兩個即時編譯引擎。 Tracing JIT 在兩個中更有潛力,它在綜合基準測試中顯示了三倍的性能, 並在某些長時間運行的程序中顯示了 1.5-2 倍的性能改進。 典型的應用性能則和 PHP 7.4 不相上下。
關於 JIT 對 PHP 8 性能的貢獻最實用的特性:構造器屬性提高、Nullsafe 運算符、str_contains()、str_starts_with()、str_ends_with()
構造器屬性提高
這個新的語法糖來用來建立值對象或數據傳輸對象。不用爲類屬性和構造函數指定它們,PHP 如今能夠將它們合併爲一個。
代替以下代碼:
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 運算符
如今能夠用新的 nullsafe 運算符鏈式調用,而不須要條件檢查 null。 若是鏈條中的一個元素失敗了,整個鏈條會停止並認定爲 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;
確實是有點酷
str_contains()、str_starts_with()和 str_ends_with()
函數
有些人可能會說它早就該有了,但咱們終於沒必要再依賴 strpos() 來知道字符串是否包含另外一個字符串了。
代替以下:
if (strpos('string with lots of words', 'words') !== false) { /* … */ }
你能夠這樣作if (str_contains('string with lots of words', 'words')) { /* … */ }
感受大多數場景應該是不須要使用 strpos 了吧,外兩個早就應該有了,
str_starts_with()和 str_ends_with()這兩個函數如今能省事很多。
str_starts_with('haystack', 'hay'); // true
str_ends_with('haystack', 'stack'); // true
最具潛力的特性:註解、Match 表達式、WeakMap
註解
如今能夠用原生的 PHP 語法來使用結構化的元數據,而不須要再依賴 PHPDoc解析,性能也隨之提高。以前定義註解路由可能須要使用:
class PostsController{
/**
* @Route("/api/posts/{id}", methods={"GET"})
*/
public function get($id) { /* ... */ }
}
如今你能夠直接用 PHP 的註解語法來定義,並經過反射直接獲取
class PostsController{
#[Route("/api/posts/{id}", methods: ["GET"])]
public function get($id) { /* ... */ }
}
Match 表達式
你能夠稱它爲 switch 表達式的大哥:match 能夠返回值,不須要 break 語句,能夠組合條件,使用嚴格的類型比較,而且不執行任何類型的強制。
以下所示:
$result = match($input) {
0 => "hello",
'1', '2', '3' => "world",
};WeakMap
WeakMap 保留對對象的引用,這些引用不會阻止這些對象被垃圾回收。
以 ORM 爲例,它們一般實現緩存,這些緩存保存對實體類的引用,以提升實體之間的關係性能。這些實體對象不能被垃圾回收,只要此緩存具備對它們的引用,即便緩存是惟一引用它們的對象。
若是此緩存層使用弱引用和映射代替,PHP 將垃圾收集這些對象當再沒有別的引用他們了。特別是在 ORM 的狀況下,它能夠管理請求中的數百個,若是不是數千個實體;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
如今終於看起來更比較符合邏輯了
0 == 'foobar' // 返回 false
能夠在對象上使用::class一個小而有用的新特性:如今能夠對對象使用::class,它的工做方式與 get_class() 相同。
$foo = new Foo();
var_dump($foo::class);
traits 中的抽象方法改進
Traits 能夠指定抽象方法,這些方法必須由使用它們的類實現。在 PHP8,必須保持一致的方法定義,包括參數類型和返回類型。
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 { }
}