不久前,PHP 8.0大張旗鼓地釋出了。它帶來了許多新特性、效能增強和變化——其中最令人興奮的是新的JIT編譯器。
技術世界總是在向前發展,PHP也是如此。
PHP 8.1也快來了,包含了幾個令人興奮的特性。它定於今年晚些時候於2021年11月25日釋出。
在本文中,我們將詳細介紹PHP 8.1將帶來哪些新的東西。從其新特性和效能改進到重大更改和棄用,我們將深入介紹它們。
PHP 8.1中的新特性
讓我們首先介紹PHP 8.1中的所有新特性。這是一個相當多的清單。
注意:隨著PHP 8.1釋出日期的臨近,此列表可能會增加或縮小。我們將致力於使其保持最新狀態。
- 純交集型別
- 列舉
- 永不返回型別
- Fibers
- 新的只讀屬性
- 定義最終類常量
- 新的fsync()和fdatasync()函式
- 新的array_is_list()函式
- 新的Sodium XChaCha20函式
- 新的IntlDatePatternGenerator類
- 支援AVIF影象格式
- 新的$_FILES:目錄上傳的full_path鍵
- 對字串鍵控陣列的陣列解包支援
- 顯式八進位制數字表示法
- MurmurHash3和xxHash雜湊演算法支援
- DNS-over-HTTPS (DoH) 支援
- 使用CURLStringFile從字串上傳檔案
- 新的MYSQLI_REFRESH_REPLICA常量
- 使用繼承快取提高效能
- 一流的可呼叫語法
純交集型別
PHP 8.1新增了對交集型別的支援。它類似於PHP 8.0 中引入的聯合型別,但它們的預期用途恰恰相反。
為了更好地理解它的用法,讓我們回顧一下PHP中型別宣告的工作原理。
本質上,您可以向函式引數、返回值和類屬性新增型別宣告。這種分配稱為型別提示,並確保值在呼叫時是正確的型別。否則,它會立即丟擲一個TypeError。反過來,這可以幫助您更好地除錯程式碼。
但是,宣告單一型別有其侷限性。聯合型別通過允許您宣告具有多種型別的值來幫助您克服這個問題,並且輸入必須至少滿足宣告的型別之一。
另一方面,RFC將交集型別描述為:
“交集型別”需要一個值來滿足多個型別約束而不是單個約束。
…純交集型別使用語法 T1&T2&… 指定,可用於當前接受型別的所有位置…
請注意使用&
(AND) 運算子來宣告交集型別。相反,我們使用|
(OR) 運算子來宣告聯合型別。
在交集型別中使用大多數標準型別將導致永遠無法滿足的型別(例如整數和字串)。因此,交集型別只能包括類型別(即介面和類名)。
以下是如何使用交集型別的示例程式碼:
class A { private Traversable&Countable $countableIterator; public function 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; case Diamonds; case Clubs; case Spades; }
在這裡,Suit列舉定義了四個可能的值:Hearts、Diamonds、Clubs和Spades。您可以直接訪問使用語法這些值:Suit::Hearts
,Suit::Diamonds
,Suit::Clubs
,和Suit::Spades
。
這種用法可能看起來很熟悉,因為列舉是建立在類和物件之上的。它們的行為相似並且具有幾乎完全相同的要求。列舉與類、介面和特徵共享相同的名稱空間。
上面提到的列舉稱為Pure Enums。
如果您想為任何情況提供標量等效值,您還可以定義支援列舉。但是,支援的列舉只能有一種型別,int
或者string
(永遠不會)。
enum Suit: string { case Hearts = 'H'; case Diamonds = 'D'; case Clubs = 'C'; case Spades = 'S'; }
此外,支援列舉的所有不同情況都必須具有唯一值。你永遠不能混合純列舉和支援列舉。
RFC進一步深入探討了列舉方法、靜態方法、常量、常量表示式等等。涵蓋所有這些超出了本文的範圍。您可以參考文件以熟悉它的所有優點。
never
返回型別
PHP 8.1新增了一個名為never
. 在 alwaysthrow
或exit
.
根據它的RFC,URL重定向函式總是exit
(顯式或隱式)是其使用的一個很好的例子:
function redirect(string $uri): never { header('Location: ' . $uri); exit(); } function redirectToLoginPage(): never { redirect('/login'); }
一個never
-declared函式應滿足三個條件:
- 它不應該有
return
明確定義的語句。 - 它不應該有
return
隱式定義的語句(例如if-else語句)。 - 它必須以
exit
語句(顯式或隱式)結束其執行。
上面的URL重定向示例顯示了never
返回型別的顯式和隱式用法。
在never
返回型別共享與許多相似之處void
返回型別。它們都確保函式或方法不返回值。但是,它的不同之處在於執行更嚴格的規則。例如,void
-declared 函式仍然可以return
沒有顯式值,但您不能對never
-declared 函式執行相同的操作。
根據經驗,void
當您希望 PHP 在函式呼叫後繼續執行時,請繼續執行。never
當你想要相反的時候就去吧。
此外,never
定義為“bottom”型別。因此,任何宣告的類方法never
都“never”不能將其返回型別更改為其他型別。但是,您可以使用void
-declared 方法擴充套件never
-declared 方法。
info: 原始RFC將
never
返回型別列為noreturn
,這是兩個PHP靜態分析工具(即Psalm和PHPStan)已經支援的返回型別。由於這是由Psalm和PHPStan的作者自己提出的,因此他們保留了其術語。但是,由於命名約定,PHP團隊對noreturn
vsnever
進行了民意調查,never
最終成為永遠的贏家。因此,對於PHP 8.1+版本,始終使用never
代替noreturn
.
Fibers
從歷史上看,PHP程式碼幾乎一直是同步程式碼。程式碼執行暫停,直到返回結果,即使是 I/O 操作。您可以想象為什麼這個過程可能會使程式碼執行速度變慢。
有多種第三方解決方案可以克服這一障礙,允許開發人員非同步編寫PHP程式碼,尤其是併發 I/O 操作。一些流行的示例包括amphp、ReactPHP和Guzzle。
但是,在PHP中沒有處理此類例項的標準方法。此外,在同一個呼叫堆疊中處理同步和非同步程式碼會導致其他問題。
Fibers是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(); echo "Value from fiber suspending: ", $value, "\n"; $fiber->resume('test');
你在上面的程式碼中建立了一個“fiber”,並立即用字串掛起它fiber
。該echo
宣告用作fiber恢復的視覺提示。
您可以通過呼叫$fiber->start()
檢索此字串值。
然後,使用字串“test”恢復fiber,該字串是對Fiber::suspend()
的呼叫返回的。完整程式碼執行會產生如下輸出:
Value from fiber suspending: fiber Value used to resume fiber: test
這是PHP Fibers工作的準系統教科書示例。這是執行七個非同步GET請求的另一個Fibers示例。
儘管如此,大多數PHP開發人員永遠不會直接處理Fibers。RFC甚至提出了同樣的建議:
Fibers是大多數使用者不會直接使用的高階功能。此功能主要針對庫和框架作者,以提供事件迴圈和非同步程式設計 API。Fibers允許在任何時候將非同步程式碼執行無縫整合到同步程式碼中,而無需修改應用程式呼叫堆疊或新增樣板程式碼。
Fiber API不應直接在應用程式級程式碼中使用。Fibers提供了一個基本的、低階別的流控制API來建立更高階別的抽象,然後在應用程式程式碼中使用這些抽象。
考慮到它的效能優勢,您可以期待PHP庫和框架能夠利用這一新特性。看看他們如何在他們的生態系統中實施Fibers會很有趣。
新的readonly
屬性
PHP 8.1新增了對readonly
屬性的支援。它們只能從它們被宣告的作用域初始化一次。初始化後,您永遠無法修改它們的值。這樣做會引發錯誤異常。
RFC寫道:
一個只讀屬性只能使用一次,並且只能從已申報範圍的初始化。對該屬性的任何其他分配或修改都將導致Error異常。
這是一個如何使用它的示例:
class Test { public readonly string $kinsta; public function __construct(string $kinsta) { // Legal initialization. $this->kinsta = $kinsta; } }
一旦初始化,就再也回不去了。將此功能融入PHP會大大減少通常用於啟用此函式的樣板程式碼。
readonly
屬性在類內部和外部都提供了強大的不變性保證。中間執行什麼程式碼並不重要。呼叫readonly
屬性將始終返回相同的值。
但是,readonly
在特定用例中使用該屬性可能並不理想。例如,您只能將它們與型別化屬性一起使用,因為沒有型別的宣告是隱式的null
,不能是readonly
。
此外,設定readonly
屬性不會使物件不可變。該readonly
屬性將儲存相同的物件,但該物件本身可以更改。
此屬性的另一個小問題是您無法克隆它。已經有針對此特定用例的解決方法。如有必要,請檢視它。
定義final
類常量
從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"; } // Fatal error: Meow::M cannot override final constant 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, 'doc info'); fwrite($kin, "\r\n"); fwrite($kin, 'more info'); 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() examples 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']); // true // false array_is_list() examples array_is_list([1 => 'cats', 'dogs']); // as first key isn't 0 array_is_list([1 => 'cats', 0 => 'dogs']); // keys are out of order array_is_list([0 => 'cats', 'bark' => 'dogs']); // non-integer keys array_is_list([0 => 'cats', 2 => 'dogs']); // gap in between keys
帶有亂序鍵的PHP陣列列表是潛在的錯誤來源。在繼續執行程式碼之前,使用此函式強制嚴格遵守列表要求是對PHP的一個很好的補充。
新的Sodium XChaCha20函式
Sodium是一個現代的、易於使用的加密庫,用於加密、解密、密碼雜湊、簽名等。該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
:更長,例如November 10, 2017 or 11:22:33pmIntlDateFormatter::MEDIUM
:短一點,比如November 10, 2017IntlDateFormatter::SHORT
:只是數字,比如10/11/17 or 11:22pm
其中每一個也有自己的RELATIVE_
變體,將日期格式設定在當前日期之前或之後的有限範圍內。在 PHP 中,值是yesterday, today和tomorrow。
假設您想使用年份的長版本和月份的短版本,例如10/11/2017。但在PHP 8.0,你不能這樣做。
在PHP 8.1+中,您可以使用新的IntlDatePatternGenerator類指定日期、月份和時間使用的格式。您可以將這些元件的確切順序留給格式化程式。
您應該注意到,雖然這個類中只有Date一詞,但它與ICU的DateTimePatternGenerator一致。這意味著您還可以使用它來建立靈活的時間格式。為了簡化命名,PHP團隊選擇使用較短的IntlDatePatternGenerator術語。
這是直接來自其RFC的示例:
$skeleton = "YYYYMMdd"; $today = \DateTimeImmutable::createFromFormat('Y-m-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 */
在上面的程式碼中,skeleton變數定義了要使用的特定日期或時間格式。但是,格式化程式處理最終結果的順序。
支援AVIF影象格式
AVIF或AV1影象檔案格式,是一種基於AV1視訊編碼格式的相對較新的免版稅影象格式。除了提供更高的壓縮率(因此檔案更小)之外,它還支援多種特性,例如透明度、HDR等。
AVIF格式最近才標準化(2021年6月8日)。這為Chrome 85+和Firefox 86+等瀏覽器增加了對AVIF影象的支援鋪平了道路。
PHP 8.1的影象處理和GD擴充套件增加了對AVIF影象的支援。
但是,要包含此特性,您需要編譯具有AVIF支援的GD擴充套件。您可以通過執行以下命令來執行此操作。
對於Debian/Ubuntu:
apt install libavif-dev
對於Fedora/RHEL:
dnf install libavif-devel
這將安裝所有最新的依賴項。接下來,您可以通過--with-avif
使用./configure
指令碼執行標誌來編譯AVIF支援。
./buildconf --force ./configure --enable-gd --with-avif
如果您要從頭開始一個新環境,您還可以在此處啟用其他PHP擴充套件。
安裝後,您可以通過在PHP終端中執行以下命令來測試是否啟用了AVIF支援:
php -i | grep AVIF
如果您已正確安裝AVIF,您將看到以下結果:
AVIF Support => enabled
您還可以使用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.1full_path
的$_FILES
陣列。使用這些新資料,您可以在伺服器上儲存相對路徑或複製確切的目錄結構。
您可以通過$FILES
使用var_dump($_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; // true 0o123 === 83; // true 0O16 === 14; // true 0O123 === 83; // true 016 === 0o16; // true 016 === 0O16; // true
此外,這個新特性也適用於PHP 7.4中引入的下劃線數字文字分隔符。
在其RFC中閱讀有關此新PHP 8.1特徵的更多資訊。
MurmurHash3和xxHash雜湊演算法支援
PHP 8.1新增了對MurmurHash3和xxHash雜湊演算法的支援。它們不是為加密用途而設計的,但它們仍然提供令人印象深刻的輸出隨機性、分散性和唯一性。
這些新的雜湊演算法比大多數PHP現有的雜湊演算法都快。事實上,其中一些雜湊演算法的變體比RAM吞吐量更快。
由於PHP 8.1還增加了對宣告特定於演算法的$options
引數的支援,您可以對這些新演算法執行相同的操作。這個新引數的預設值為[]
。因此,它不會影響我們現有的任何雜湊函式。
您可以在他們的GitHub頁面上閱讀有關這些PHP 8.1新功能的更多資訊:MurmurHash3、xxHash、Algorithm-specific $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://www.wbolt.com'); curl_setopt($doh, CURLOPT_DOH_URL, 'https://dns.google/dns-query'); curl_exec($doh);
在上面的示例中,我們使用了Google的公共DNS伺服器。另外,請注意https://
在所有使用的URL中的使用。確保完美地配置它,因為在Curl中沒有可以回退的預設DNS伺服器。
您還可以從Curl文件中包含的公共DoH伺服器列表中進行選擇。
此外,Curl文件的CURLOPT_DOH_URL 參考解釋瞭如何徹底使用其各種引數。
使用CURLStringFile從字串上載檔案
PHP Curl擴充套件支援帶有檔案上傳的HTTP(S)請求。它使用CURLFile類來實現這一點,該類接受URI或檔案路徑、mime型別和最終檔名。
但是,使用CURLFile類,您只能接受檔案路徑或URI,而不能接受檔案本身的內容。如果您已經將檔案上傳到記憶體中(例如處理過的影象、XML文件、PDF),您必須使用data://
Base64編碼的URI。
但是libcurl已經支援一種更簡單的方式來接受檔案的內容。新的CURLStringFile類增加了對此的支援。
您可以閱讀其GitHub頁面以瞭解有關如何在PHP 8.1中實現的更多資訊。
新的MYSQLI_REFRESH_REPLICA
常數
PHP 8.1的mysqli擴充套件新增了一個名為MYSQLI_REFRESH_REPLICA
. 它相當於現有的MYSQLI_REFRESH_SLAVE
常數。
這個變化在MySQL 8.0.23中受到歡迎,以解決技術詞彙中的種族不敏感問題(最常見的例子包括“奴隸”和“主人”)。
您應該注意到舊的常量沒有被刪除或棄用。開發人員和應用程式可以繼續使用它。對於希望拋棄此類術語的開發人員和公司而言,此新常量只是一種選擇。
使用繼承快取提高效能
繼承快取(Inheritance Cache )是opcache的新增功能,可消除PHP類繼承開銷。
PHP類由opcache單獨編譯和快取。但是,它們已經在執行時針對每個請求進行了連結。這個過程可能涉及幾個相容性檢查和從父類和特徵借用方法/屬性/常量。
因此,即使每個請求的結果都相同,這仍需要相當長的時間來執行。
繼承快取連結所有唯一的依賴類(父類、介面、特徵、屬性型別、方法)並將結果儲存在opcache共享記憶體中。由於這種情況現在只發生一次,因此繼承需要較少的指令。
此外,它消除了對不可變類的限制,例如未解析的常量、型別化屬性和協變型別檢查。因此,儲存在opcache中的所有類都是不可變的,進一步減少了所需的指令數量。
總而言之,它有望帶來顯著的效能優勢。該補丁的作者Dimitry Stogov發現它在基礎Symfony “Hello, World!” 上有8%的改進。程式。我們迫不及待地想在我們的以下PHP基準測試中測試它。
First-Class可呼叫語法
PHP 8.1新增了First-Class可呼叫語法來取代使用字串和陣列的現有編碼。除了建立更乾淨的Closure 之外,靜態分析工具也可以訪問這種新語法並尊重宣告的範圍。
以下是一些取自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擴充套件
- MySQLi預設錯誤模式設定為異常
- CSV寫入函式的可定製行尾
- 新version_compare運算子限制
- HTML編碼和解碼函式現在使用ENT_QUOTES | ENT_SUBSTITUTE
- 關於非法緊湊函式呼叫的警告
- 新的從資源到類物件的遷移
PHP Interactive Shell需要readline擴充套件
PHP的readline擴充套件支援互動式shell功能,例如導航、自動完成、編輯等。雖然它與PHP捆綁在一起,但預設情況下並未啟用。
您可以使用PHP CLI的-a
命令列選項訪問PHP互動式shell:
php -a Interactive shell php > php > echo "Hello"; Hello php > function test() { php { echo "Hello"; php { } php > test(); Hello
在PHP 8.1之前,即使沒有啟用readline擴充套件,您也可以使用PHP CLI開啟互動式shell 。正如預期的那樣,shell的互動功能不起作用,使該-a
選項變得毫無意義。
在PHP 8.1 CLI中,如果您沒有啟用readline擴充套件,互動式shell會退出並顯示錯誤訊息。
php -a Interactive shell (-a) requires the readline extension.
MySQLi預設錯誤模式設定為異常
在PHP 8.1之前,MySQLi預設為靜默錯誤。這種行為通常會導致程式碼不遵循嚴格的錯誤/異常處理。開發人員必須實現自己的顯式錯誤處理功能。
PHP 8.1通過將MySQLi的預設錯誤報告模式設定為丟擲異常來更改此行為。
Fatal error: Uncaught mysqli_sql_exception: Connection refused in ...:...
由於這是一個重大更改,對於PHP<8.1版本,您應該mysqli_report
在建立第一個MySQLi連線之前使用該函式顯式設定錯誤處理模式。或者,您可以通過例項化一個mysqli_driver
例項來選擇錯誤報告值來執行相同的操作。
RFC遵循PHP 8.0中引入的類似更改。
CSV寫入函式的可定製行尾
在PHP 8.1之前,PHP的內建CSV寫入函式fputcsv
和SplFileObject::fputcsv
被硬編碼為\n
在每行末尾新增(或換行符)。
PHP8.1為這些函式新增了對名為eol
的新引數的支援。您可以使用它來傳遞可配置的行尾字元。預設情況下,它仍然使用\n
字元。因此,您可以繼續在現有程式碼中使用它。
標準字元轉義規則適用於使用行尾字元。如果您想使用\r
、\n
或\r\n
作為EOL字元,您必須將它們括在雙引號中。
這是跟蹤此新更改的GitHub頁面。
新的 version_compare
運算子限制
PHP的version_compare()
函式比較兩個版本號字串。此函式接受一個可選的第三個引數,operator
用於測試特定關係。
儘管文件中沒有明確說明,但在PHP 8.1之前,您可以將此引數設定為部分值(例如g
, l
, n
)而不會出現錯誤。
PHP 8.1對version_compare()
函式的operator
引數新增了更嚴格的限制來克服這種情況。您現在可以使用的唯一運算子是:
- ==、=和eq
- !=、<>和ne
- >和gt
- >=和ge
- <和lt
- <=和le
HTML編碼和解碼函式改用 ENT_QUOTES | ENT_SUBSTITUTE
HTML實體是字元的文字表示,否則會被解釋為HTML。想想諸如<
和>
用於定義HTML 標籤的字元(例如<a>
、<h3>
、<script>
)。
HTML實體<
是& lt;
(小於符號)和>
是& gt;
(大於符號)。
注意:刪除“&”和“amp”之間的空格。
您可以在HTML文件中安全地使用這些HTML實體,而無需觸發瀏覽器的渲染引擎。
例如,& lt;script& gt;
將在瀏覽器中顯示為<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 = 'Cat'; $sound = 'Meow'; $region = 'Istanbul'; compact('animal', 'sound', 'region'); // ['animal' => "Cat", 'sound' => "Meow", 'region' => "Istanbul"]
緊湊型函式的文件指出,這將只接受字串引數或字串值陣列值。但是,在PHP 7.3之前,任何未設定的字串都將被悄悄跳過。
PHP7.3修改了compact()
函式,以便在使用未定義變數時發出通知。PHP 8.1更進一步,併發出警告。
您可以閱讀其GitHub 頁面以瞭解此更改是如何發生的。
新的從資源到類物件遷移
PHP的長期目標之一是從資源轉向標準類物件。
由於歷史原因,資源物件在PHP應用程式中被廣泛使用。因此,資源向類物件的遷移需要儘可能減少破壞性。PHP 8.1遷移了五個這樣的資源:
將file_info
資源遷移到finfo
物件
PHP的finfo 類為函式提供了一個物件導向的介面fileinfo
。但是,使用finfo
函式返回resource
具有file_info
型別的物件而不是finfo
類本身的例項。
遷移到IMAP\Connection
類物件的IMAP資源
根據資源到物件的遷移目標,當PHP最終修改類的實現細節時,新的IMAP\Connection
類將潛在的破壞性更改降至最低。
這個新類也被宣告final
,所以你不被允許extend
。
在其GitHub頁面上閱讀有關其實現的更多資訊。
FTP連線資源現在為FTP\Connection
類物件
在PHP<8.1中,如果您使用或函式ftp_connect()
或者ftp_ssl_connect()
建立FTP連線,您將返回一個ftp型別的資源物件。
PHP 8.1新增了新的FTP\Connection
類來糾正這個問題。和IMAP\Connection
類一樣,它也被宣告final
為防止它被擴充套件。
在其GitHub頁面上閱讀有關其實現的更多資訊。
字型識別符號遷移到GdFont
類物件
PHP的GD擴充套件提供了imageloadfont() 函式來載入使用者定義的點陣圖並返回其字型識別符號資源 ID(一個整數)。
在PHP 8.1中,此函式將改為返回GdFont類例項。此外,為了使遷移輕鬆自如,以前從imageloadfont()
接受資源ID的所有函式現在都將採用新的GdFont類物件。
在其GitHub頁面上閱讀有關此遷移的更多資訊。
LDAP資源遷移到物件
LDAP或輕量級目錄訪問協議,用於訪問“目錄伺服器”。就像硬碟目錄結構一樣,它是一個獨特的資料庫,以樹狀結構儲存資料。
PHP包含一個LDAP擴充套件,它接受或返回PHP 8.1之前的資源物件。但是,它們現在都已無縫遷移到新的類例項。已經過渡的資源型別有:
ldap link
資源到\LDAP\Connection
類物件ldap result
資源到\LDAP\Result
類物件ldap result entry
資源到\LDAP\ResultEntry
類物件
瀏覽其GitHub頁面以更好地瞭解此遷移。
Pspell資源現在為類物件
PHP的Pspell擴充套件允許您檢查拼寫和單詞建議。
PHP<8.1使用pspell
和pspell config
帶有整數識別符號的資源物件型別。這兩個資源物件現在替換為PSpell\Dictionary
和PSpell\Config
類物件。
與之前的遷移一樣,之前接受或返回資源物件識別符號的所有Pspell函式都將採用新的類物件例項。
有關更多資訊,請參閱其GitHub頁面。
PHP 8.1中的棄用
PHP 8.1棄用了許多以前的功能。以下列表簡要概述了PHP 8.1棄用的功能:
- 不能將null傳遞給不可為Null的內部函式引數
- 受限制的$GLOBALS使用
- 內部函式的返回型別宣告
- 不推薦使用可序列化介面
- 不相容的float到int轉換已棄用
- mysqli::get_client_info方法和mysqli_get_client_info($param) 已棄用
- 不推薦使用所有mhash*() 函式(雜湊擴充套件)
- filter.default和filter.default_options INI設定已棄用
- 在false上棄用autovivification
- 不推薦使用mysqli_driver->driver_version屬性
不能將null
傳遞給不可為Null的內部函式引數
從PHP 8.0開始,它的內部函式null
即使對於不可為null的引數也默默地接受值。這不適用於使用者定義的函式——它們只接受null
可為空的引數。
例如,考慮這種用法:
var_dump(str_contains("foobar", null)); // bool(true)
在這裡,null
值被靜默轉換為空字串。因此,結果返回true
。
該RFC旨在通過在PHP 8.1中丟擲棄用警告來同步內部函式的行為。
var_dump(str_contains("foobar", null)); // Deprecated: Passing null to argument of type string is deprecated
在下一個主要的 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。
但是,在很多情況下可能會丟失型別資訊。其中一些包括具有資源型,out pass-by-ref 引數,非final方法返回型別,函式或不按一般規則解析引數的方法。您可以在其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介面,PHP將丟擲“Deprecated”警告。
在PHP8.1中,如果在沒有實現__serialize()
和__unserialize()
方法的情況下實現Serializable介面,PHP將丟擲“Deprecated”警告。
Deprecated: The Serializable interface is deprecated. Implement __serialize() and __unserialize() instead (or in addition, if support for old PHP versions is necessary) in ... on line ...
如果你支援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
(整數)。MySQL Native Driver (MySQLnd) 是官方PHP原始碼的一部分,並將這些常量與PHP版本掛鉤。在libmysql中,它們代表編譯時的客戶端庫版本。
在PHP8.1之前,mysqli以4種方式公開這些常量:mysqli_driver
屬性、mysqli
屬性、mysqli_get_client_info()
函式和mysqli::get_client_info
方法。但是,對於client_version
沒有方法。
MySQLnd 以兩種方式向PHP公開這些常量:常量和函式呼叫。為了統一使用這兩個選項的mysqli訪問方法,PHP 8.1棄用了其他兩個選項:
get_client_info
mysqli類中的方法。相反,您可以只使用該mysqli_get_client_info()
函式。mysqli_get_client_info()
帶引數的函式。不帶任何引數呼叫函式以避免棄用通知。
在其GitHub 頁面上閱讀有關此棄用的更多資訊。
不推薦使用所有mhash*()
函式(雜湊擴充套件)
PHP5.3將mhash*()
函式整合到ext/hash中,作為ext/mhash
的相容層。後來,PHP7.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超全域性-即GPCR的資料($_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
(預設值)以外的任何值,PHP8.1將丟擲一個棄用警告。您不會看到ilter.default_options
的單獨棄用通知,但PHP9.0將刪除這兩個INI設定。
作為替代方案,您可以開始使用filter_var()函式。它使用指定的過濾器過濾變數。
棄用autovivification
的false
PHP允許自動啟用(從假值自動建立陣列)。如果變數未定義,此功能非常有用。
儘管如此,當值為false或null時自動建立陣列並不理想。
此RFC不允許從錯誤值自動啟用。但是,請注意,仍然允許來自未定義變數和null的自動啟用。
在PHP 8.1中,附加到false型別的變數將發出棄用通知:
Deprecated: Automatic conversion of false to array is deprecated in
PHP 9.0同樣會丟擲致命錯誤,這與其他標量型別相同。
mysqli_driver->driver_version
屬性已棄用
MySQLi 擴充套件的mysqli_driver->driver_version屬性已經13年沒有更新了。儘管此後對驅動程式進行了許多更改,但它仍然返回舊的驅動程式版本值,使該屬性變得毫無意義。
在PHP 8.1中,mysqli_driver->driver_version屬性已棄用。
其他小改動
PHP 8.1中有更多的棄用。將它們全部列出在這裡將是一項令人筋疲力盡的工作。我們建議您直接檢視RFC以瞭解這些較小的棄用情況。
PHP的GitHub頁面還包括一個PHP 8.1升級說明指南。它列出了在升級到PHP 8.1之前您應該考慮的所有重大更改。
小結
PHP 8.1離我們不遠了。並且它已經承諾將其前身提升一倍,這是不小的壯舉。
我們認為最令人興奮的PHP 8.1特性是列舉、纖維、純交集型別及其許多效能改進。此外,我們迫不及待地要讓PHP 8.1步入正軌,並對各種PHP框架和CMS進行基準測試。
評論留言