PHP 8.1 的新特性:特性、變化、改進等

不久前,PHP 8.0 大張旗鼓地發布了。它帶來了許多新特性、性能增強和變化——其中最令人興奮的是新的 JIT 編譯器。

嘗試免費演示

技術世界總是在向前發展,PHP 也是如此。

PHP 8.1 快要發布了,它包含了幾個令人興奮的功能。它定於今年晚些時候於 2021 年 11 月 25 日發布。

在本文中,我們將詳細介紹 PHP 8.1 中的新增功能。從其新功能和性能改進到重大更改和棄用,我們將深入介紹它們。

穩坐!

PHP 8.1 中的新功能

讓我們首先介紹 PHP 8.1 中的所有新功能。這是一個相當多的清單。

注意:隨著 PHP 8.1 發布日期的臨近,此列表可能會增加或縮小。我們將致力於使其保持最新狀態。

純交集類型

PHP 8.1 添加了對交集類型的支持。它類似於 PHP 8.0 中引入的聯合類型,但它們的預期用途恰恰相反。

為了更好地理解它的用法,讓我們回顧一下 PHP 中類型聲明的工作原理。

本質上,您可以向函數參數、返回值和類屬性添加類型聲明。這種分配稱為類型提示,並確保值在調用時是正確的類型。否則,它會立即拋出一個 TypeError。反過來,這可以幫助您更好地調試代碼。

但是,聲明單一類型有其局限性。聯合類型通過允許您聲明具有多種類型的值來幫助您克服這個問題,並且輸入必須至少滿足聲明的類型之一。

另一方面,RFC 將交集類型描述為:

「交集類型」需要一個值來滿足多個類型約束而不是單個約束。

…純交集類型使用語法 T1&T2&… 指定,可用於當前接受類型的所有位置…

請注意使用 & (AND) 運算符來聲明交集類型。相反,我們使用 | (OR) 運算符來聲明聯合類型。

在交集類型中使用大多數標準類型將導致永遠無法滿足的類型(例如整數和字元串)。因此,交集類型只能包括類類型(即介面和類名)。

以下是如何使用交集類型的示例代碼:

A 類 {
    私有 Traversable&Countable $countableIterator;

    公共函數 setIterator(Traversable&Countable $countableIterator): void {
        $this->countableIterator = $countableIterator;
    }

    public function getIterator(): Traversable&Countable {
        return $this->countableIterator;
    }
}

在上面的代碼中,我們將變數 countableIterator 定義為兩種類型的交集:Traversable 和 Countable。在這種情況下,聲明的兩種類型是介面。

交集類型也符合已用於類型檢查和繼承的標準 PHP 變化規則。但是還有兩個關於交集類型如何與子類型交互的額外規則。您可以在其 RFC 中閱讀有關交集類型差異規則的更多信息。

在某些編程語言中,您可以在同一個聲明中組合聯合類型和交集類型。但是 PHP 8.1 禁止它。因此,它的實現被稱為「純」交集類型。但是,RFC 確實提到它「留作未來範圍」。

枚舉

PHP 8.1 終於添加了對枚舉(也稱為枚舉或枚舉類型)的支持。它們是用戶定義的數據類型,由一組可能的值組成。

編程語言中最常見的枚舉示例是布爾類型,其中 true 和 false 作為兩個可能的值。它是如此普遍,以至於它被融入到許多現代編程語言中。

根據 RFC,PHP 中的枚舉首先將被限制為「單元枚舉」:

此 RFC 的範圍僅限於「單元枚舉」,即枚舉本身就是一個值,而不是簡單的原始常量的花哨語法,並且不包括附加的相關信息。此功能極大地擴展了對數據建模、自定義類型定義和 monad 樣式行為的支持。枚舉啟用了「使無效狀態不可表示」的建模技術,這會導致更健壯的代碼,而無需進行詳盡的測試。

為了達到這個階段,PHP 團隊研究了許多已經支持枚舉的語言。他們的調查發現,您可以將枚舉分為三類:花式常量、花式對象和完整代數數據類型 (ADT)。這是一個有趣的閱讀!

PHP 實現了「Fancy Objects」枚舉,並計劃在未來將其擴展到完整的 ADT。它在概念和語義上都模仿了 Swift、Rust 和 Kotlin 中的枚舉類型,儘管它沒有直接模仿它們中的任何一個。

RFC 使用一副牌中著名的西裝類比來解釋它是如何工作的:

enum Suit {
  case Hearts;
  案例鑽石;
  案例俱樂部;
  案例黑桃;
}

在這裡, Suit 枚舉定義了四個可能的值:Hearts、Diamonds、Clubs 和 Spades。您可以使用以下語法直接訪問這些值:Suit::Hearts、Suit::Diamonds、Suit::Clubs 和 Suit::Spades。

這種用法可能看起來很熟悉,因為枚舉是建立在類和對象之上的。它們的行為相似並且具有幾乎完全相同的要求。枚舉與類、介面和特徵共享相同的命名空間。

上面提到的枚舉稱為純枚舉。

如果您想為任何情況提供標量等效值,您還可以定義支持枚舉。但是,支持的枚舉只能有一種類型,int 或 string(決不能同時具有)。

枚舉套裝:字元串 {
  case Hearts=”H”;
  案例鑽石=「D」;
  案例俱樂部=「C」;
  案例黑桃=「S」;
}

此外,支持枚舉的所有不同情況都必須具有唯一值。你永遠不能混合純枚舉和支持枚舉。

RFC 進一步深入探討了枚舉方法、靜態方法、常量、常量表達式等等。涵蓋所有這些超出了本文的範圍。您可以參考文檔以熟悉它的所有優點。

永不返回類型

PHP 8.1 添加了一個名為 never 的新返回類型提示。在總是拋出或退出的函數中使用非常有用。

根據 RFC,總是退出(顯式或隱式)的 URL 重定向函數是其使用的一個很好的例子:

函數重定向(字元串 $uri):從不{
    header(’位置:’ . $uri);
    出口();
}

function redirectToLoginPage(): never {
    redirect(‘/login’);
}

一個從未聲明的函數應該滿足三個條件:

  • 它不應該顯式定義 return 語句。
  • 它不應該隱式定義 return 語句(例如 if-else 語句)。
  • 它必須以退出語句(顯式或隱式)結束其執行。

上面的 URL 重定向示例顯示了從不返回類型的顯式和隱式用法。

從不返回類型與 void 返回類型有許多相似之處。它們都確保函數或方法不返回值。但是,它的不同之處在於執行更嚴格的規則。例如,聲明為 void 的函數仍然可以在沒有顯式值的情況下返回,但不能對從未聲明的函數執行相同操作。

根據經驗,當您希望 PHP 在函數調用後繼續執行時,請使用 void。當你想要相反的時候,永遠不要。

此外, never 被定義為「底部」類型。因此,任何聲明為 never 的類方法都不能「永遠」將其返回類型更改為其他類型。但是,您可以使用從未聲明的方法擴展聲明為空的方法。

信息

原始 RFC 將 never return 類型列為 noreturn,這是兩個 PHP 靜態分析工具(即 Psalm 和 PHPStan)已經支持的返回類型。由於這是由 Psalm 和 PHPStan 的作者自己提出的,因此他們保留了其術語。然而,由於命名約定,PHP 團隊對 n​​oreturn 與 never 進行了民意調查,從沒有成為永遠的贏家。因此,對於 PHP 8.1+ 版本,始終將 noreturn 替換為 never。

纖維

從歷史上看,PHP 代碼幾乎一直是同步代碼。代碼執行暫停,直到返回結果,即使是 I/O 操作。您可以想像為什麼這個過程可能會使代碼執行速度變慢。

有多種第三方解決方案可以克服這一障礙,允許開發人員非同步編寫 PHP 代碼,尤其是並發 I/O 操作。一些流行的示例包括 amphp、ReactPHP 和 Guzzle。

但是,在 PHP 中沒有處理此類實例的標準方法。此外,在同一個調用堆棧中處理同步和非同步代碼會導致其他問題。

纖程是 PHP 通過虛擬線程(或綠色線程)處理並行性的方式。它試圖通過允許 PHP 函數中斷而不影響整個調用堆棧來消除同步和非同步代碼之間的差異。

以下是 RFC 的承諾:

  • 向 PHP 添加對 Fibers 的支持。
  • 引入一個新的 Fiber 類和對應的反射類 ReflectionFiber。
  • 添加異常類 FiberError 和 FiberExit 來表示錯誤。
  • Fibers 允許現有介面(PSR-7、Doctrine ORM 等)的透明非阻塞 I/O 實現。那是因為佔位符(promise)對象被消除了。相反,函數可以聲明 I/O 結果類型,而不是無法指定解析類型的佔位符對象,因為 PHP 不支持泛型。

您可以使用 Fibers 開發全棧、可中斷的 PHP 函數,然後您可以使用這些函數在 PHP 中實現協作多任務處理。當 Fiber 暫停整個執行堆棧時,您可以放心,因為它不會損害您的其餘代碼。

圖表說明了使用 Fibers 的 PHP 代碼執行流程。

為了說明 Fibers 的用法,它的 RFC 使用了這個簡單的例子:

$fiber = new Fiber(function (): void {
    $value = Fiber::suspend(‘fiber’);
    echo “Value used to resume fiber: “, $value, “n”;
});

$value = $fiber->start();

回聲「來自纖維懸掛的價值:」,$價值,「n」;

$fiber->resume(‘test’);

您在上面的代碼中創建了一個「光纖」,並立即用字元串光纖掛起它。echo 語句用作光纖恢復的視覺提示。

您可以從對 $fiber->start() 的調用中檢索此字元串值。

然後,您使用字元串「test」恢復光纖,該字元串從對 Fiber::suspend() 的調用返回。完整的代碼執行會產生如下輸出:

纖維暫停的
價值:纖維用於恢復纖維的價值:測試

這是工作中 PHP Fibers 的准系統教科書示例。這是執行七個非同步 GET 請求的另一個 Fibers 示例。

儘管如此,大多數 PHP 開發人員永遠不會直接處理 Fibers。RFC 甚至提出了同樣的建議:

光纖是大多數用戶不會直接使用的高級功能。此功能主要針對庫和框架作者,以提供事件循環和非同步編程 API。Fibers 允許在任何時候將非同步代碼執行無縫集成到同步代碼中,而無需修改應用程序調用堆棧或添加樣板代碼。

Fiber API 不應直接在應用程序級代碼中使用。Fibers 提供了一個基本的、低級別的流控制 API 來創建更高級別的抽象,然後在應用程序代碼中使用這些抽象。

考慮到它的性能優勢,您可以期待 PHP 庫和框架能夠利用這一新功能。看看他們如何在他們的生態系統中實施 Fibers 會很有趣。

只讀屬性

PHP 8.1 添加了對只讀屬性的支持。它們只能從它們被聲明的作用域初始化一次。初始化後,您永遠無法修改它們的值。這樣做會引發錯誤異常。

它的 RFC 寫道:

只讀屬性只能初始化一次,並且只能在聲明它的範圍內初始化。對該屬性的任何其他分配或修改都將導致 Error 異常。

這是一個如何使用它的示例:

class Test {
    public readonly string $kinsta;

    public function __construct(string $kinsta) {
        // 合法的初始化。
        $this->kinsta = $kinsta;
    }
}

一旦初始化,就再也回不去了。將此功能融入 PHP 會大大減少通常用於啟用此功能的樣板代碼。

readonly 屬性在類內部和外部都提供了強大的不變性保證。中間運行什麼代碼並不重要。調用只讀屬性將始終返回相同的值。

但是,在特定用例中使用 readonly 屬性可能並不理想。例如,您只能將它們與類型化屬性一起使用,因為沒有類型的聲明是隱式 null 並且不能是只讀的。

此外,設置只讀屬性不會使對象不可變。readonly 屬性將保存相同的對象,但該對象本身可以更改。

此屬性的另一個小問題是您無法克隆它。已經有針對此特定用例的解決方法。如有必要,請查看它。

定義最終類常量

從 PHP 8.0 開始,您可以使用其子類覆蓋類常量。這是由於在 PHP 中實現繼承的方式。

以下是如何覆蓋先前聲明的常量值的示例:

class Moo
{
    public const M = “moo”;
}

class Meow extends Moo
{
    public const M = “meow”;
}  

現在,如果奶牛想要更嚴格地控​​制貓的行為(至少在常量方面),他們可以使用 PHP 8.1 的新 final 修飾符來做到這一點。

一旦你將一個常量聲明為 final,就意味著。

class Moo
{
    final public const M = “moo”;
}

class Meow extends Moo
{
    public const M = “meow”;
}

// 致命錯誤:Meow::M 不能覆蓋最終常量 Moo::M

您可以在最終的類常量 PHP RFC 中閱讀有關它的更多信息。

新的 fsync() 和 fdatasync() 函數

PHP 8.1 添加了兩個名為 fsync() 和 fdatasync() 的新文件系統函數。對於那些習慣於 Linux 同名函數的人來說,它們似乎很熟悉。那是因為它們是相關的,只是為 PHP 實現的。

事實上,這一補充早該進行了。PHP 是少數仍未實現 fsync() 和 fdatasync() 的主要編程語言之一——也就是說,直到 PHP 8.1。

fsync() 函數類似於 PHP 現有的 fflush() 函數,但在一個方面有很大不同。fflush() 將應用程序的內部緩衝區刷新到操作系統,而 fsync() 更進一步,確保將內部緩衝區刷新到物理存儲。這確保了完整且持久的寫入,以便您即使在應用程序或系統崩潰後也可以檢索數據。

這是一個如何使用它的示例。

$doc=”kinsta.txt”;

$kin = fopen($doc, ‘ki’);
fwrite($kin, ‘文檔信息’);
fwrite($kin, “rn”);
fwrite($kin, ‘更多信息’);

fsync($kin);
fclose($kin);

在最後添加 fsync() 調用可確保 PHP 或操作系統的內部緩衝區中保存的任何數據都被寫入存儲。在此之前,所有其他代碼執行都將被阻止。

其相關函數是fdatasync()。使用它來同步數據,但不一定是元數據。對於元數據不是必需的數據,此函數調用會使寫入過程更快一點。

但是,您應該注意 PHP 8.1 尚不完全支持 Windows 上的 fdatasync()。它只是作為 fsync() 的別名。在 POSIX 上,正確實現了 fdatasync()。

新的 array_is_list() 函數

PHP 數組可以保存整數和字元串鍵。這意味著您可以將它們用於多種用途,包括列表、哈希表、字典、集合、堆棧、隊列等等。您甚至可以在數組中包含數組,從而創建多維數組。

您可以有效地檢查特定條目是否為數組,但檢查它是否有任何缺失的數組偏移量、亂序鍵等就不是那麼容易了。簡而言之,您無法快速驗證數組是否為列表。

array_is_list() 函數檢查數組的鍵是否從 0 開始按順序排列,並且沒有間隙。如果滿足所有條件,它將返回 true。默認情況下,它也為空數組返回 true。

以下是在滿足 true 和 false 條件的情況下使用它的幾個示例:

// true array_is_list() 示例
array_is_list([]); // true
array_is_list([1, 2, 3]); // true
array_is_list([‘cats’, 2, 3]); // true
array_is_list([‘cats’, ‘dogs’]); // true
array_is_list([0 => ‘cats’, ‘dogs’]); // true
array_is_list([0 => ‘cats’, 1 => ‘dogs’]); // 真的

// false array_is_list() 示例
array_is_list([1 => ‘cats’, ‘dogs’]); // 因為第一個鍵不是 0
array_is_list([1 => ‘cats’, 0 => ‘dogs’]); // 鍵亂序
array_is_list([0 => ‘cats’, ‘bark’ => ‘dogs’]); // 非整數鍵
array_is_list([0 => ‘cats’, 2 => ‘dogs’]); // 鍵之間的間隙

帶有亂序鍵的 PHP 數組列表是潛在的錯誤來源。在繼續執行代碼之前,使用此函數強制嚴格遵守列表要求是對 PHP 的一個很好的補充。

新的鈉 XChaCha20 功能

鈉是一個現代的、易於使用的加密庫,用於加密、解密、密碼散列、簽名等。PECL libsodium 包為鈉添加了一個包裝器,以便 PHP 開發人員可以使用它。

即使是 Facebook、Discord、Malwarebytes 和 Valve 等領先的科技公司也使用 libsodium 來通過快速安全的連接來保護他們的用戶。

libsodium 支持 XChaCha20 加密演算法對數據進行加密和解密,特別是對流加密。同樣,PECL libsodium 擴展已經支持 XChaCha20,但僅支持 Poly1305 消息驗證代碼。

許多 PHP 應用程序直接使用 XChaCha20 進行流加密。為了讓事情變得更簡單,從 PHP 8.1 開始,您將擁有三個新函數,無需身份驗證即可使用 XChaCha20 加密或解密數據。這種模式稱為「分離模式」。

新推出的 XChaCha20 功能是:

  • sodium_crypto_stream_xchacha20_keygen:返回一個安全的隨機密鑰,用於sodium_crypto_stream_xchacha20。
  • sodium_crypto_stream_xchacha20:將密鑰和隨機數擴展為偽隨機位元組的密鑰流。
  • sodium_crypto_stream_xchacha20_xor:使用隨機數和密鑰(無身份驗證)加密消息。

此外,在全局命名空間中定義了兩個新的 PHP 常量:

  • SODIUM_CRYPTO_STREAM_XCHACHA20_KEYBYTES(分配 32)
  • SODIUM_CRYPTO_STREAM_XCHACHA20_NONCEBYTES(指定為 24)

不過請謹慎使用。由於沒有身份驗證,解密操作容易受到常見的密文攻擊。

您可以在 GitHub 頁面上閱讀有關其用法和要求的更多信息。

新的 IntlDatePatternGenerator 類

PHP 的底層 ICU 庫支持創建本地化的日期和時間格式,但不能完全自定義。

例如,如果您想在 PHP 8.0 之前創建特定於語言環境的數據和時間格式,您可以使用預定義的 IntlDateFormatter 常量以 6 種方式完成:

  • IntlDateFormatter::LONG:更長,例如 2017 年 11 月 10 日或晚上 11:22:33
  • IntlDateFormatter::MEDIUM:短一點,比如 2017 年 11 月 10 日
  • IntlDateFormatter::SHORT:只是數字,比如 10/11/17 或 11:22pm

其中每一個也有自己的 RELATIVE_ 變體,它在當前日期之前或之後的有限範圍內設置日期格式。在 PHP 中,值是昨天、今天和明天。

假設您想使用年份的長版本和月份的短版本,例如 10/11/2017。從 PHP 8.0 開始,你不能。

在 PHP 8.1+ 中,您可以使用新的 IntlDatePatternGenerator 類指定日期、月份和時間使用的格式。您可以將這些組件的確切順序留給格式化程序。

需要注意的是,雖然這個類中只有Date這個詞,但它和ICU的DateTimePatternGenerator是一致的。這意味著您還可以使用它來創建靈活的時間格式。為了簡化命名,PHP 團隊選擇使用較短的 IntlDatePatternGenerator 術語。

這是直接來自其 RFC 的示例:

$skeleton = “YYYYMMdd”;

$today = DateTimeImmutable::createFromFormat(‘Ym-d’, ‘2021-04-24’);

$dtpg = new IntlDatePatternGenerator(“de_DE”);
$pattern = $dtpg->getBestPattern($skeleton);
echo “de:”, IntlDateFormatter::formatObject($today, $pattern, “de_DE”), “n”;

$dtpg = new IntlDatePatternGenerator(“en_US”);
$pattern = $dtpg->getBestPattern($skeleton), “n”;
echo “en: “, IntlDateFormatter::formatObject($today, $pattern, “en_US”), “n”;

/*
de: 24.04.2021
en: 04/24/2021
*/

在上面的代碼中,骨架變數定義了要使用的特定日期或時間格式。但是,格式化程序處理最終結果的順序。

支持 AVIF 圖像格式

AVIF 或 AV1 圖像文件格式,是一種基於 AV1 視頻編碼格式的相對較新的免版稅圖像格式。除了提供更高的壓縮率(因此文件更小)之外,它還支持多種功能,例如透明度、HDR 等。

AVIF 格式最近才標準化(2021 年 6 月 8 日)。這為 Chrome 85+ 和 Firefox 86+ 等瀏覽器增加了對 AVIF 圖像的支持鋪平了道路。

PHP 8.1 的圖像處理和 GD 擴展增加了對 AVIF 圖像的支持。

但是,要包含此功能,您需要編譯具有 AVIF 支持的 GD 擴展。您可以通過運行以下命令來執行此操作。

對於 Debian/Ubuntu:

apt 安裝 libavif-dev

對於 Fedora/RHEL:

dnf 安裝 libavif-devel

這將安裝所有最新的依賴項。接下來,您可以通過使用 ./configure 腳本運行 –with-avif 標誌來編譯 AVIF 支持。

./buildconf –force
./configure –enable-gd –with-avif

如果您要從頭開始一個新環境,您還可以在此處啟用其他 PHP 擴展。

安裝後,您可以通過在 PHP 終端中運行以下命令來測試是否啟用了 AVIF 支持:

php -i | grep AVIF

如果您已正確安裝 AVIF,您將看到以下結果:

AVIF 支持 => 啟用

您還可以使用 gd_info() 調用來檢索 GD 功能列表,包括是否啟用了 AVIF 支持功能。

這個更新的 PHP 8.1 GD 擴展還添加了兩個用於處理 AVIF 圖像的新函數:imagecreatefromavif 和 imageavif。它們的工作方式與 JPEG 和 PNG 對應物類似。

imagecreatefromavif 函數從給定的 AVIF 圖像返回一個 GdImage 實例。然後,您可以使用此實例來編輯或轉換圖像。

另一個 imageavif 函數輸出 AVIF 圖像文件。例如,您可以使用它將 JPEG 轉換為 AVIF:

$image = imagecreatefromjpeg(‘image.jpeg’);
imageavif($image, ‘image.avif’);

您可以在其 GitHub 頁面上閱讀有關此新功能的更多信息。

新的 $_FILES:目錄上傳的 full_path 鍵

PHP 維護了大量預定義變數來跟蹤各種事物。其中之一是 $_FILES 變數保存通過 HTTP POST 方法上傳的項目的關聯數組。

大多數現代瀏覽器都支持使用 HTML 文件上傳欄位上傳整個目錄。甚至 PHP <8.1 也支持此功能,但有一個很大的警告。您無法上傳具有確切目錄結構或相對路徑的文件夾,因為 PHP 沒有將此信息傳遞給 $_FILES 數組。

這在 PHP 8.1 中發生了變化,在 $_FILES 數組中添加了一個名為 full_path 的新鍵。使用這些新數據,您可以在伺服器上存儲相對路徑或複製確切的目錄結構。

您可以通過使用 var_dump($_FILES); 輸出 $FILES 數組來測試此信息。命令。

但是,如果您正在使用此功能,請謹慎操作。確保您防範標準文件上傳攻擊。

對字元串鍵控數組的數組解包支持

PHP 7.4 添加了對使用數組展開運算符 (…) 進行數組解包的支持。它可以作為使用 array_merge() 函數的更快替代方法。但是,此功能僅限於數字鍵數組,因為在合併具有重複鍵的數組時,解包字元串鍵數組會導致衝突。

但是,PHP 8 增加了對命名參數的支持,消除了這個限制。因此,數組解包現在也支持使用相同語法的字元串鍵數組:

$array = […$array1, …$array2];

這個 RFC 示例說明了如何在 PHP 8.1 中處理合併具有重複字元串鍵的數組:

$array1 = [“a” => 1];
$array2 = [“a” => 2];
$array = [“a” => 0, …$array1, …$array2];
var_dump($array); // [“a” => 2]

在這裡,字元串鍵「a」在通過數組解包合併之前出現了三次。但只有屬於 $array2 的最後一個值獲勝。

顯式八進位數字表示法

PHP 支持各種數字系統,包括十進位 (base-10)、二進位 (base-2)、八進位 (base-8) 和十六進位 (base-16)。十進位數字系統是默認值。

如果你想使用任何其他數字系統,那麼你必須在每個數字前加上一個標準前綴:

  • 十六進位:0x 前綴。(例如 17 = 0x11)
  • 二進位:0b 前綴。(例如 3 = 0b11)
  • 八進位:0 前綴。(例如 9 = 011)

您可以看到八進位數字系統的前綴與其他系統的前綴有何不同。為了標準化這個問題,許多編程語言增加了對顯式八進位數字元號的支持:0o 或 0O。

從 PHP 8.1 開始,您可以在八進位數字系統中將上述示例(即以 10 為基數的數字 9)編寫為 0o11 或 0O11。

0o16 === 14; // 真
0o123 === 83; // 真的

0O16 === 14; // 真
0O123 === 83; // 真的

016 === 0o16;// 真
016 === 0O16; // 真的

此外,這個新特性也適用於 PHP 7.4 中引入的下劃線數字文字分隔符。

在其 RFC 中閱讀有關此新 PHP 8.1 功能的更多信息。

MurmurHash3 和 xxHash 哈希演算法支持

PHP 8.1 添加了對 MurmurHash3 和 xxHash 散列演算法的支持。它們不是為加密用途而設計的,但它們仍然提供令人印象深刻的輸出隨機性、分散性和唯一性。

這些新的散列演算法比大多數 PHP 現有的散列演算法都快。事實上,其中一些散列演算法的變體比 RAM 吞吐量更快。

訂閱時事通訊

想知道我們是如何將流量增加超過 1000% 的嗎?

加入 20,000 多名其他人,他們會收到我們的每周時事通訊,其中包含 WordPress 內幕技巧!

現在訂閱

由於 PHP 8.1 還增加了對聲明特定於演算法的 $options 參數的支持,您可以對這些新演算法執行相同的操作。這個新參數的默認值為 []。因此,它不會影響我們現有的任何哈希函數。

您可以在他們的 GitHub 頁面上閱讀有關這些 PHP 8.1 新功能的更多信息:MurmurHash3、xxHash、特定於演算法的 $options。

DNS-over-HTTPS (DoH) 支持

DNS-over-HTTPS (DoH) 是一種通過 HTTPS 協議進行 DNS 解析的協議。DoH 使用 HTTPS 加密客戶端和 DNS 解析器之間的數據,通過防止中間人攻擊來提高用戶隱私和安全性。

從 PHP 8.1 開始,您可以使用 Curl 擴展來指定 DoH 伺服器。它需要使用 libcurl 7.62+ 版本編譯 PHP。對於大多數流行的操作系統(包括 Linux 發行版)來說,這不是問題,因為它們通常包含 Curl 7.68+。

您可以通過指定 CURLOPT_DOH_URL 選項來配置 DoH 伺服器 URL。

$doh = curl_init(‘https://kinsta.com’);
curl_setopt($doh, CURLOPT_DOH_URL, ‘https://dns.google/dns-query’);
curl_exec($doh);

在上面的示例中,我們使用了 Google 的公共 DNS 伺服器。另外,請注意在所有使用的 URL 中使用 https://。確保完美地配置它,因為在 Curl 中沒有可以回退的默認 DNS 伺服器。

您還可以從 Curl 文檔中包含的公共 DoH 伺服器列表中進行選擇。

此外,Curl 文檔的 CURLOPT_DOH_URL 參考解釋了如何徹底使用其各種參數。

使用 CURLStringFile 從字元串上傳文件

PHP Curl 擴展支持帶有文件上傳的 HTTP(S) 請求。它使用 CURLFile 類來實現這一點,該類接受 URI 或文件路徑、mime 類型和最終文件名。

但是,使用 CURLFile 類,您只能接受文件路徑或 URI,而不能接受文件本身的內容。如果您已經將文件上傳到內存中(例如處理過的圖像、XML 文檔、PDF),您必須使用 data:// URI 和 Base64 編碼。

但是 libcurl 已經支持一種更簡單的方式來接受文件的內容。新的 CURLStringFile 類增加了對此的支持。

您可以閱讀其 GitHub 頁面以了解有關如何在 PHP 8.1 中實現的更多信息。

新的 MYSQLI_REFRESH_REPLICA 常量

PHP 8.1 的 mysqli 擴展添加了一個名為 MYSQLI_REFRESH_REPLICA 的新常量。它等效於現有的 MYSQLI_REFRESH_SLAVE 常量。

這個變化在 MySQL 8.0.23 中受到歡迎,以解決技術辭彙中的種族不敏感問題(最常見的例子包括「奴隸」和「主人」)。

您應該注意到舊的常量沒有被刪除或棄用。開發人員和應用程序可以繼續使用它。對於希望拋棄此類術語的開發人員和公司而言,此新常量只是一種選擇。

使用繼承緩存提高性能

繼承緩存是 opcache 的新增功能,可消除 PHP 類繼承開銷。

PHP 類由 opcache 單獨編譯和緩存。但是,它們已經在運行時針對每個請求進行了鏈接。這個過程可能涉及幾個兼容性檢查和從父類和特徵借用方法/屬性/常量。

因此,即使每個請求的結果都相同,這仍需要相當長的時間來執行。

繼承緩存鏈接所有唯一的依賴類(父類、介面、特徵、屬性類型、方法)並將結果存儲在 opcache 共享內存中。由於這種情況現在只發生一次,因此繼承需要較少的指令。

此外,它消除了對不可變類的限制,例如未解析的常量、類型化屬性和協變類型檢查。因此,存儲在 opcache 中的所有類都是不可變的,進一步減少了所需的指令數量。

總而言之,它有望帶來顯著的性能優勢。這個補丁的作者 Dimitry Stogov 發現它在基礎 Symfony 「Hello, World!」 上有 8% 的改進。程序。我們迫不及待地想在我們的以下 PHP 基準測試中測試它。

一流的可調用語法

PHP 8.1 添加了一流的可調用語法來取代使用字元串和數組的現有編碼。除了創建更清晰的閉包之外,靜態分析工具還可以訪問這種新語法並尊重聲明的範圍。

以下是一些取自 RFC 的示例:

$fn = Closure::fromCallable(‘strlen’);
$fn = strlen(…);

$fn = Closure::fromCallable([$this, ‘method’]);
$fn = $this->method(…)

$fn = Closure::fromCallable([Foo::class, ‘method’]);
$fn = Foo::method(…);

這裡,所有的表達式對都是等價的。三點 (…) 語法類似於參數解包語法 (…$args)。除了這裡,參數尚未填寫。

PHP 8.1 中的變化

PHP 8.1 還包括對其現有語法和功能的更改。讓我們來討論它們:

PHP Interactive Shell 需要 readline 擴展

PHP 的 readline 擴展支持互動式 shell 功能,例如導航、自動完成、編輯等。雖然它與 PHP 捆綁在一起,但默認情況下並未啟用。

您可以使用 PHP CLI 的 -a 命令行選項訪問 PHP 互動式 shell:

php -a

互動式外殼

php >
php > echo “你好”;
你好
php > function test() {
php { echo “Hello”;
php { }
php > 測試();
你好

在 PHP 8.1 之前,即使沒有啟用 readline 擴展,您也可以使用 PHP CLI 打開互動式 shell。正如預期的那樣,shell 的交互功能不起作用,使得 -a 選項毫無意義。

在 PHP 8.1 CLI 中,如果您沒有啟用 readline 擴展,互動式 shell 會退出並顯示錯誤消息。

php -a
互動式 shell (-a) 需要 readline 擴展。

MySQLi 默認錯誤模式設置為異常

在 PHP 8.1 之前,MySQLi 默認為靜默錯誤。這種行為通常會導致代碼不遵循嚴格的錯誤/異常處理。開發人員必須實現自己的顯式錯誤處理功能。

PHP 8.1 通過將 MySQLi 的默認錯誤報告模式設置為拋出異常來更改此行為。

致命錯誤:未捕獲的 mysqli_sql_exception:連接被拒絕…:…

由於這是一個重大更改,對於 PHP <8.1 版本,您應該在建立第一個 MySQLi 連接之前使用 mysqli_report 函數顯式設置錯誤處理模式。或者,您可以通過實例化 mysqli_driver 實例來選擇錯誤報告值來執行相同的操作。

RFC 遵循 PHP 8.0 中引入的類似更改。

CSV 寫入功能的可定製行尾

在 PHP 8.1 之前,PHP 的內置 CSV 寫入函數 fputcsv 和 SplFileObject::fputcsv 被硬編碼為在每行末尾添加 n(或換行符)。

PHP 8.1 為這些函數添加了對名為 eol 的新參數的支持。您可以使用它來傳遞可配置的行尾字元。默認情況下,它仍然使用 n 字元。因此,您可以繼續在現有代碼中使用它。

標準字元轉義規則適用於使用行尾字元。如果要將 r、n 或 rn 用作 EOL 字元,則必須將它們括在雙引號中。

這是跟蹤此新更改的 GitHub 頁面。

新 version_compare 運算符限制

PHP 的 version_compare() 函數比較兩個版本號字元串。此函數接受稱為 operator 的可選第三個參數來測試特定關係。

儘管文檔中沒有明確說明,但在 PHP 8.1 之前,您可以將此參數設置為部分值(例如 g、l、n)而不會出現錯誤。

PHP 8.1 對 version_compare() 函數的 operator 參數添加了更嚴格的限制以克服這種情況。您現在可以使用的唯一運算符是:

需要一個為您提供競爭優勢的託管解決方案?Kinsta 為您提供令人難以置信的速度、最先進的安全性和自動縮放功能。查看我們的計劃

  • ==、= 和 eq
  • !=、<> 和 ne
  • > 和 gt
  • >= 和 ge
  • < 和 lt
  • <= 和 le

沒有更多的部分運算符值。

ENT_QUOTES | ENT_SUBSTITUTE 用於 HTML 編碼和解碼功能

HTML 實體是字元的文本表示,否則會被解釋為 HTML。想想諸如 < 和 > 之類的用於定義 HTML 標籤的字元(例如 <a>、<h3>、<script>)。

< 的 HTML 實體是 <(小於符號),> 是 >(大於符號)。您可以在 HTML 文檔中安全地使用這些 HTML 實體,而無需觸發瀏覽器的渲染引擎。

例如,<script> 將在瀏覽器中顯示為 <script>,而不是被解釋為 HTML 標籤。

在 PHP 8.1 之前, htmlspecialchars() 和 htmlentities() 函數將「、<、> 和 &」等符號轉換為它們各自的 HTML 實體。但默認情況下,他們沒有將單引號字元 (‘) 轉換為其 HTML 實體。此外,如果文本中存在格式錯誤的 UTF-8,它們將返回一個空字元串。

在 PHP 8.1. 中,這些 HTML 編碼和解碼函數(及其相關函數)也會默認將單引號字元轉換為它們的 HTML 實體。

如果給定的文本包含無效字元,函數將用 Unicode 替換字元 ( ) 替換它們,而不是返回空字元串。PHP 8.1 通過將這些函數的簽名更改為 ENT_QUOTES | 來實現這一點。默認情況下是 ENT_SUBSTITUTE 而不是 ENT_COMPAT。

大多數框架已經使用 ENT_QUOTES 作為默認標誌值。因此,由於此更改,您不會看到太大差異。然而,新的 ENT_SUBSTITUTE 標誌並沒有被廣泛使用。PHP 8.1 將導致無效的 UTF-8 字元被替換為 字元而不是返回空字元串。

關於非法緊湊函數調用的警告

PHP 的 compact() 函數非常方便。您可以使用它來創建一個數組,其中包含使用名稱和值的變數。

例如,考慮以下代碼:

$animal=”貓”;
$sound = ‘喵’;
$region = ‘伊斯坦布爾’;
緊湊(’動物’,’聲音’,’區域’);
// [‘animal’ => “Cat”, ‘sound’ => “Meow”, ‘region’ => “Istanbul”]

緊湊函數的文檔說明它只接受字元串參數或帶有字元串值的數組值。但是,在 PHP 7.3 之前,任何未設置的字元串都將被悄悄跳過。

PHP 7.3 修改了 compact() 函數以在您使用未定義的變數時拋出一個通知。PHP 8.1 更進一步,並發出警告。

您可以閱讀其 GitHub 頁面以了解此更改是如何發生的。

從資源到類對象的新遷移

PHP 的長期目標之一是從資源轉向標準類對象。

由於歷史原因,資源對象在 PHP 應用程序中被廣泛使用。因此,資源向類對象的遷移需要儘可能減少破壞性。PHP 8.1 遷移了五個這樣的資源:

file_info 資源遷移到 finfo 對象

PHP 的 finfo 類為 fileinfo 函數提供了一個面向對象的介面。但是,使用 finfo 函數返回具有 file_info 類型的資源對象,而不是 finfo 類本身的實例。

PHP 8.1 修復了這個異常。

IMAP 資源到 IMAPConnection 類對象

根據資源到對象的遷移目標,當 PHP 最終修改類的實現細節時,新的 IMAPConnection 類最大限度地減少了潛在的破壞性更改。

這個新類也被聲明為 final,所以你不能擴展它。

在其 GitHub 頁面上閱讀有關其實現的更多信息。

FTP 連接資源是 FTPConnection 類對象

在 PHP <8.1 中,如果您使用 ftp_connect() 或 ftp_ssl_connect() 函數創建 FTP 連接,您將返回一個 ftp 類型的資源對象。

PHP 8.1 添加了新的 FTPConnection 類來解決這個問題。和 IMAPConnection 類一樣,它也被聲明為 final 以防止它被擴展。

在其 GitHub 頁面上閱讀有關其實現的更多信息。

字體標識符是 GdFont 類對象

PHP 的 GD 擴展提供了 imageloadfont() 函數來載入用戶定義的點陣圖並返回其字體標識符資源 ID(一個整數)。

在 PHP 8.1 中,此函數將改為返回 GdFont 類實例。此外,為了使遷移輕鬆自如,以前從 imageloadfont() 接受資源 ID 的所有函數現在都將採用新的 GdFont 類對象。

在其 GitHub 頁面上閱讀有關此遷移的更多信息。

LDAP 資源遷移到對象

LDAP 或輕量級目錄訪問協議,用於訪問「目錄伺服器」。就像硬碟目錄結構一樣,它是一個獨特的資料庫,以樹狀結構保存數據。

PHP 包含一個 LDAP 擴展,它接受或返回 PHP 8.1 之前的資源對象。但是,它們現在都已無縫遷移到新的類實例。已經過渡的資源類型有:

  • ldap 鏈接資源到 LDAPConnection 類對象
  • ldap 結果資源到 LDAPResult 類對象
  • ldap 結果條目資源到 LDAPResultEntry 類對象

瀏覽其 GitHub 頁面以更好地了解此遷移。

Pspell 資源現在是類對象

PHP 的 Pspell 擴展允許您檢查拼寫和單詞建議。

PHP <8.1 使用帶有整數標識符的 pspell 和 pspell 配置資源對象類型。這兩個資源對象現在替換為 PSpellDictionary 和 PSpellConfig 類對象。

與之前的遷移一樣,之前接受或返回資源對象標識符的所有 Pspell 函數都將採用新的類對象實例。

有關更多信息,請參閱其 GitHub 頁面。

PHP 8.1 中的棄用

PHP 8.1 棄用了許多以前的功能。以下列表簡要概述了 PHP 8.1 棄用的功能:

不能將 null 傳遞給不可為 Null 的內部函數參數

從 PHP 8.0 開始,它的內部函數即使對於不可為 null 的參數也默默地接受 null 值。這不適用於用戶定義的函數——它們只接受可空參數的 null。

例如,考慮這種用法:

var_dump(str_contains(“foobar”, null));
//布爾(真)

此處,空值被靜默轉換為空字元串。因此,結果返回 true。

該 RFC 旨在通過在 PHP 8.1 中拋出棄用警告來同步內部函數的行為。

var_dump(str_contains(“foobar”, null));
// 不推薦使用:不推薦將 null 傳遞給字元串類型的參數

在下一個主要的 PHP 版本(即 PHP >=9.0)中,棄用將成為 TypeError,使內部函數的行為與用戶定義的函數保持一致。

受限制的 $GLOBALS 使用

PHP 的 $GLOBALS 變數提供對其內部符號表的直接引用。支持此功能很複雜,並且會影響陣列操作性能。另外,它很少被使用。

根據 RFC,不再允許間接修改 $GLOBALS。此更改向後不兼容。

這種變化的影響相對較低:

在前 2k 個 composer 包中,我發現了 23 個使用 $GLOBALS 而不直接取消引用它的案例。根據粗略的檢查,只有兩種情況沒有以只讀方式使用 $GLOBALS。

但是,$GLOBALS 的只讀用法繼續照常工作。不再支持寫入整個 $GLOBALS。因此,您可以預期性能會有輕微的提升,尤其是在使用普通 PHP 數組時。

內部函數的返回類型聲明

PHP 8.0 允許開發人員為大多數內部函數和方法聲明參數和返回類型。這要歸功於各種 RFC,例如內部函數的一致類型錯誤、聯合類型 2.0 和混合類型 v2。

但是,在很多情況下可能會丟失類型信息。其中一些包括具有資源的類型、通過引用傳遞的參數、非最終方法的返回類型以及不根據一般規則解析參數的函數或方法。您可以在其 RFC 中閱讀確切的詳細信息。

此 RFC 僅解決非最終方法的返回類型的問題。然而,PHP 團隊並沒有立即完全淘汰它,而是提供了一個漸進的遷移路徑,以使用相關的方法返回類型更新您的代碼庫。

非最終的內部方法返回類型(如果可能)在 PHP 8.1 中暫時聲明,它們將在 PHP 9.0 中強制執行。這意味著在 PHP 8.x 版本中,當內部方法以返回類型不兼容的方式被覆蓋時,在繼承檢查期間會引發「棄用」通知,而 PHP 9.0 會使這些成為致命錯誤。

如果您在更新到 PHP 8.1 後看到此棄用通知,請確保更新您的方法的返回類型。

不推薦使用可序列化介面

PHP 7.4 引入了自定義對象序列化機制和兩個新的魔法方法:__serialize() 和 __unserialize()。這些新方法旨在最終替換損壞的 Serializable 介面。

該 RFC 提議通過制定最終刪除 Serializable 的計劃來最終確定該決定。

在 PHP 8.1 中,如果你實現了 Serializable 介面而沒有實現 __serialize() 和 __unserialize() 方法,PHP 會拋出「Deprecated」警告。

不推薦使用:不推薦使用 Serializable 介面。代替(或者另外,如果需要支持舊的 PHP 版本)在 … 在線 …

如果您支持 PHP <7.4 和 PHP >=7.4,您應該同時實現 Serializable 介面和新的魔法方法。在 PHP >=7.4 版本上,魔術方法將優先。

不兼容的 float 到 int 轉換已棄用

PHP 是一種動態類型語言。因此,在很多情況下自然會發生類型強制。大多數這些強制是無害的,而且超級方便。

但是,當浮點數轉換為整數時,通常會涉及數據丟失。例如,當浮點數 3.14 轉換為整數 3 時,它會丟失其小數值。

當浮點數超出平台整數範圍或浮點數字元串轉換為整數時,也會發生同樣的情況。

PHP 8.1 糾正了這種行為,並使其動態類型強制更符合大多數現代編程語言。目標是使這種強制可預測和直觀。

在 PHP 8.1 中,當不兼容的 float 被隱式強制轉換為 int 時,您將看到棄用通知。但是什麼構成了整數兼容的浮點數?RFC 對此做出了回答:

如果具有以下特徵,則稱浮點數與整數兼容:

  • 是一個數字(即不是 NaN 或無窮大)
  • 在 PHP 整數範圍內(取決於平台)
  • 沒有小數部分

此棄用通知將在下一個主要 PHP 版本(即 PHP 9.0)中升級為 TypeError。

mysqli::get_client_info 方法和 mysqli_get_client_info($param) 已棄用

MySQL 客戶端 API 定義了兩個常量:client_info(一個字元串)和 client_version(一個 int)。MySQL Native Driver (MySQLnd) 是官方 PHP 源代碼的一部分,並將這些常量與 PHP 版本掛鉤。在 libmysql 中,它們代表編譯時的客戶端庫版本。

在 PHP 8.1 之前,mysqli 以 4 種方式公開這些常量:mysqli_driver 屬性、mysqli 屬性、mysqli_get_client_info() 函數和 mysqli::get_client_info 方法。但是,client_version 沒有方法。

MySQLnd 以兩種方式向 PHP 公開這些常量:常量和函數調用。為了統一使用這兩個選項的 mysqli 訪問方法,PHP 8.1 棄用了其他兩個選項:

  • mysqli 類中的 get_client_info 方法。相反,您可以只使用 mysqli_get_client_info() 函數。
  • mysqli_get_client_info() 函數與參數。不帶任何參數調用函數以避免棄用通知。

在其 GitHub 頁面上閱讀有關此棄用的更多信息。

mhash*() 函數(散列擴展)已棄用

PHP 5.3 將 mhash*() 函數集成到 ext/hash 作為 ext/mhash 的兼容層。後來,PHP 7.0 刪除了 ext/mhash。

與 hash_*() 函數不同,mhash*() 函數並不總是可用。您必須在配置 PHP 時單獨啟用它們。

在 PHP 7.4 中,散列擴展與 PHP 捆綁在一起,使其成為 PHP 的默認擴展。但是,出於兼容性原因,它仍然支持啟用 –enable-mhash 選項。

PHP 團隊決定在 PHP 8.1 中棄用 mhash*() 函數,並在 PHP 9.0 中完全刪除它們。不推薦使用的函數是 mhash()、mhash_keygen_s2k()、mhash_count()、mhash_get_block_size() 和 mhash_get_hash_name()。您可以使用標準的 ext/hash 功能代替它們。

filter.default 和 filter.default_options INI 設置已棄用

PHP 的 filter.default INI 設置允許您將過濾器應用於所有 PHP 超級全局變數 — 即 GPCRS 數據($_GET、$_POST、$_COOKIE、$_REQUEST 和 $_SERVER)。

例如,您可以設置 filter.default=magic_quotes 或 filter.default=add_slashes(基於 PHP 版本)來複活 PHP 有爭議且不安全的魔術引號功能(在 PHP 5.4 中刪除)。

filter.default INI 設置通過允許更多過濾器來提供附加功能,使其更糟。例如,它的另一個選項 — filter.default=special_chars — 僅對 HTML 啟用魔術引號。對這些設置的了解要少得多。

如果 filter.default 設置為 unsafe_raw(默認值)以外的任何值,PHP 8.1 將拋出棄用警告。您不會看到單獨的 filter.default_options 棄用通知,但 PHP 9.0 將刪除這兩個 INI 設置。

作為替代方案,您可以開始使用 filter_var() 函數。它使用指定的過濾器過濾變數。

在 false 上棄用 autovivification

PHP 允許自動激活(從假值自動創建數組)。如果變數未定義,此功能非常有用。

儘管如此,當值為 false 或 null 時自動創建數組並不理想。

此 RFC 不允許從錯誤值自動激活。但是,請注意,仍然允許來自未定義變數和 null 的自動激活。

在 PHP 8.1 中,附加到 false 類型的變數將發出棄用通知:

已棄用:不推薦將 false 自動轉換為數組

PHP 9.0 同樣會拋出致命錯誤,這與其他標量類型相同。

不推薦使用 mysqli_driver->driver_version 屬性

MySQLi 擴展的 mysqli_driver->driver_version 屬性已經 13 年沒有更新了。儘管此後對驅動程序進行了許多更改,但它仍然返回舊的驅動程序版本值,使該屬性變得毫無意義。

在 PHP 8.1 中,不推薦使用 mysqli_driver->driver_version 屬性。

其他小改動

PHP 8.1 中有更多的棄用。將它們全部列出在這裡將是一項令人筋疲力盡的工作。我們建議您直接查看 RFC 以了解這些較小的棄用情況。 4

PHP 的 GitHub 頁面還包括一​​個 PHP 8.1 升級說明指南。它列出了在升級到 PHP 8.1 之前您應該考慮的所有重大更改。

概括

PHP 8.1 離我們不遠了。並且它已經承諾將其前身提升一倍,這是一項不小的壯舉。當它啟動時,您可以放心,Kinsta 將支持 PHP 8.1 用於實時和登台環境(包括在 DevKinsta 上)。

我們認為最令人興奮的 PHP 8.1 特性是枚舉、纖維、純交集類型及其許多性能改進。我們迫不及待地想讓 PHP 8.1 步入正軌,並對各種 PHP 框架和 CMS 進行基準測試。

請務必將此博文加入書籤,以備將來參考。

您最喜歡哪個 PHP 8.1 功能?在下面的評論部分與社區分享您的想法。

通過以下方式節省時間、成本並最大限度地提高站點性能:

  • 來自 WordPress 託管專家的即時幫助,24/7。
  • Cloudflare 企業集成。
  • 全球受眾覆蓋全球 28 個數據中心。
  • 使用我們內置的應用程序性能監控進行優化。

所有這些以及更多,在一個沒有長期合同、協助遷移和 30 天退款保證的計劃中。查看我們的計劃或與銷售人員交談以找到適合您的計劃。

相關文章

離頁SEO技術

離頁SEO: 頁面外SEO將有助於使您的網站成為網路主流,因此您可以獲得更高的知名度…
閱讀更多