PHP 8 有哪些值得期待的新特性

NiZerin發表於2020-04-07

新的 PHP 主要版本 PHP8 預計將於 2020 年底釋出。
它現在正處於非常活躍的開發中,所以在接下來的幾個月裡,開發速度和開發程式可能會有很大的變化。

在這篇文章中,我會羅列出 PHP8 中會發生的一些改變:新功能、效能改進和突破性變化。
因為 PHP8 是一個新的主要版本,所以程式碼及語法向下相容性會更低。
如果您一直保持與最新版本保持同步,那麼升級應該不會太難,因為大多數突破性的更改在 7.* 版本中都已棄用。

除了突破性的變化,PHP8 還帶來了一些不錯的新特性,比如 JIT 編譯器和 union types,當然還有其它更多的特性。

新特性

從新特性開始說起,但是 PHP8 仍在積極開發中,因此這個清單將隨著時間的推移而增長。

聯合型別(Union types) RFC

考慮到 PHP 的動態型別特性,聯合型別在很多情況下都很有用。
聯合型別是兩個或多個型別的集合,這些型別指示可以使用這兩個型別中的任何一個。

public function foo(Foo|Bar $input): int|float;

我怎麼感覺這個和C語言裡的聯合體有點相似。

請注意,void 永遠不能是聯合型別的一部分,因為它表示“根本沒有返回值”。
此外,可以使用|NULL或使用現有的

public function foo(Foo|null $foo): void;

public function bar(?Bar $bar): void;

JIT RFC

JIT-Just-In-Time 編譯器承諾顯著提高效能,儘管在 Web 應用可能沒有較大的好處。
在這一點上還沒有任何準確的基準,但它們肯定會出現的。

靜態返回型別(Static return type) RFC

雖然已經可以返回 self ,但在 PHP8 之前,靜態不是有效的返回型別。考慮到 PHP 的動態型別特性,它對許多開發人員都很有用。

class Foo
{
    public function test(): static
    {
        return new static();
    }
}

弱對映(Weak maps) RFC

基於在 PHP 7.4 中新增的 WeakRefs RFC 的基礎上,在 PHP 8中 新增了 WeakMap 實現。WeakMap 包含對物件的引用,這不會阻止這些物件被垃圾回收。

以 ORM 為例,它們經常實現包含對實體類的引用的快取,以提高實體之間關係的效能。
這些實體物件不能被垃圾回收,只要該快取有對它們的引用,即使快取是唯一引用它們的東西。

如果該快取層改為使用弱引用和對映,則 PHP 將在其他物件不再引用這些物件時對它們進行垃圾回收。
特別是在 ORM 的情況下,它可以在一個請求中管理數百個(如果不是數千個)實體;弱對映可以提供一種更好、更資源友好的方式來處理這些物件。

以下是 Weak maps 的用法,RFC 中的一個示例:

class Foo 
{
    private WeakMap $cache;

    public function getSomethingWithCaching(object $obj): object
    {
        return $this->cache[$obj]
           ??= $this->computeSomethingExpensive($obj);
    }
}

可以在物件上使用::class RFC

一個小而有用的新特性:現在可以對物件使用::class,而不必對它們使用get_class()
它的工作方式與get_class()相同。

$foo = new Foo();

var_dump($foo::class);

建立 DateTime 物件的介面

您已經可以使用DateTime::createFromImmutable($immutableDateTime),從DateTimeImmutable物件建立DateTime物件,但是反過來很棘手。
通過新增DateTime::createFromInterface()DatetimeImmutable::createFromInterface(),現在有了一種將DateTimeDateTimeImmutable物件相互轉換的通用方法。

DateTime::createFromInterface(DateTimeInterface $other);

DateTimeImmutable::createFromInterface(DateTimeInterface $other);

新的 Stringable 介面 RFC

Stringable介面可用於鍵入提示任何字串或實現__toString()
此外,每當類實現__toString()時,它都會自動在幕後實現介面,不需要手動實現它。

class Foo
{
    public function __toString(): string
    {
        return 'foo';
    }
}

function bar(Stringable $stringable) { /* … */ }

bar(new Foo());
bar('abc');

新的 str_contains() 函式 RFC

有些人可能會說這是早就應該實現的功能,但是我們最終不必再依賴 strpos() 來知道一個字串是否包含另一個字串。

以前:

if (strpos('string with lots of words', 'words') !== false) { /* … */ }

現在:

if (str_contains('string with lots of words', 'words')) { /* … */ }

新的 fdiv() 函式 PR

新的 fdiv() 函式的作用類似於 fmod() 和 intdiv() 函式,它們允許被 0 整除。
您將得到 INF、-INF 或 NaN ,而不是錯誤,具體取決於大小寫。

新的 get_debug_type() 函式 RFC

get_debug_type() 返回一個變數的型別。
聽起來像是 gettype() 可以實現的功能。
get_debug_type() 為陣列、字串、匿名類和物件返回更有用的輸出。

例如,在類 \foo\Bar 上呼叫 gettype() 將返回 Object。
使用 get_debug_type() 將返回類名。

可以在 RFC 中找到 get_debug_type() 和 gettype() 之間差異的完整列表。

改進 traits 裡的抽象方法 RFC

traits 可以指定必須由使用它們的類實現的抽象方法。
但是有一個警告:在 PHP8 之前,這些方法實現的簽名沒有經過驗證。
在以下程式碼中有效:

trait Test {
    abstract public function test(int $input): int;
}

class UsesTrait
{
    use Test;

    public function test($input)
    {
        return $input;
    }
}

在使用 traits 並實現其抽象方法時,PHP8 將執行正確的方法簽名驗證。
這意味著您需要改寫以下內容:

class UsesTrait
{
    use Test;

    public function test(int $input): int
    {
        return $input;
    }
}

token_get_all() 的物件介面 RFC

函式的作用是:返回值的是一個陣列。
此 RFC 使用 PhpToken::getall() 方法新增一個 PhpToken 類。
此實現使用物件,而不是普通值。
它消耗更少的記憶體,更容易閱讀。

變數語法調整 RFC

來自 RFC:“統一變數語法RFC解決了PHP變數語法中的一些不一致問題”,這個 RFC 打算解決少數被忽略的情況。

內部函式的型別批註

很多人都參與到為所有內部函式新增適當型別註釋的工作中。
這是一個長期存在的問題,通過在以前版本中對 PHP 所做的所有更改,最終可以解決這個問題。
這意味著內部函式和方法在反射中將具有完整的型別資訊。

統一錯誤型別 RFC

PHP 中的使用者定義函式已經丟擲 TypeErrors,但是內部函式沒有丟擲 TypeErrors,而是發出警告並返回 NULL。
從 PHP8 開始,內部函式的行為已經保持一致。

重新分類 zend engine 報錯 RFC

許多以前只觸發警告或通知的錯誤已轉換為適當的錯誤。
以下警告已更改。

  • 未定義變數:錯誤異常而不是通知。
  • 未定義的陣列索引:警告而不是通知。
  • 被零除:DivisionByZeroError 異常而不是警告。
  • 嘗試遞增/遞減非物件的屬性‘%s’:錯誤異常而不是警告。
  • 試圖修改非物件的屬性‘%s’:錯誤異常而不是警告。
  • 嘗試分配非物件的屬性‘%s’:錯誤異常而不是警告。
  • 從空值建立預設物件:錯誤異常而不是警告。
  • 正在嘗試獲取非物件的屬性‘%s’:警告而不是通知。
  • 未定義屬性:%s::$%s:警告而不是通知。
  • 無法將元素新增到陣列,因為下一個元素已被佔用:錯誤異常而不是警告。
  • 無法取消設定非陣列變數中的偏移量:錯誤異常而不是警告。
  • 不能將標量值用作陣列:錯誤異常而不是警告。
  • 只能解包陣列和遍歷:TypeError 異常而不是警告。
  • 為 foreach() 提供的引數無效:TypeError 異常而不是警告。
  • 偏移型別非法:TypeError 異常而不是警告。
  • isset 中的偏移型別非法或為空:TypeError 異常而不是警告。
  • 未設定中的偏移型別非法:TypeError 異常而不是警告。
  • 陣列到字串的轉換:警告而不是通知。
  • 資源 ID#%d 用作偏移量,轉換為整數(%d):警告而不是通知。
  • 發生字串偏移量轉換:警告而不是通知。
  • 未初始化的字串偏移量:%d:警告而不是通知。
  • 無法將空字串分配給字串偏移量:錯誤異常而不是警告

預設錯誤報告級別

現在是 E_ALL,而不是除 E_NOTICE 和 E_DEVERATED 之外的所有內容。
這意味著可能會彈出許多以前被悄悄忽略的錯誤,儘管在 PHP8 之前可能已經存在。

@運算子不再忽略致命錯誤

此更改可能會揭示在 PHP8 之前隱藏的錯誤。請確保在生產伺服器上設定 display_errors=off !

串聯優先順序 RFC

雖然在 PHP7.4 中已不推薦使用,但此更改現在生效。
如果你這樣寫的話:

echo "sum: " . $a + $b;

PHP 以前會這樣解釋它:

echo ("sum: " . $a) + $b;

PHP 8 將會這樣解釋它:

echo "sum: " . ($a + $b);

反射方法簽名更改

反射類的三個方法簽名已更改:

ReflectionClass::newInstance($args);
ReflectionFunction::invoke($args);
ReflectionMethod::invoke($object, $args);

現已成為:

ReflectionClass::newInstance(...$args);
ReflectionFunction::invoke(...$args);
ReflectionMethod::invoke($object, ...$args);

升級指南指定,如果您擴充套件了這些類,並且仍然希望同時支援 PHP 7和 PHP 8,則允許以下簽名:

ReflectionClass::newInstance($arg = null, ...$args);
ReflectionFunction::invoke($arg = null, ...$args);
ReflectionMethod::invoke($object, $arg = null, ...$args);

其他

在PHP7.*開發過程中,被用的 RFC,現在已在PHP8中完成。

本作品採用《CC 協議》,轉載必須註明作者和本文連結

By: Laravel-China 寧澤林
MyBlog: nizer.in

相關文章