PHP 8,PHP 的一個新的大版本,預計將於 2020 年 12 月 3 日發布,這意味着將不會有 PHP 7.5 版本。PHP8 目前正處於非常活躍的開發階段,所以在接下來的幾個月里,情況可能會發生很大的變化。
我的PHP學習交流社區點擊此處。
在這篇文章中,我會維持一個最新的清單列表,列出預計會出現的新特性、性能提升和突破性的變化。由於 PHP 8 是一個新的大版本,因此您的代碼被破壞的可能性更高。如果您始終保持運行 PHP 的最新版本,那么升級相對來說就會輕松很多,因為在 7. * 版本中,大多數重大更改均已棄用。
除重大更改外,PHP 8 還帶來了一些不錯的新功能,比如說 JIT 編譯器 , 聯合類型 , 屬性,以及更多。
新特性
從新特性開始,請記住 PHP8 仍處於活動開發階段,因此此列表將隨着時間的推移而增長。
聯合類型 rfc
考慮到 PHP 動態語言類型的特性,現在很多情況下,聯合類型都是很有用的。聯合類型是兩個或者多個類型的集合,表示可以使用其中任何一個類型。
public function foo(Foo|Bar $input): int|float;
請注意,聯合類型中不包含 void
,因為 void
表示的含義是 “根本沒有返回值”。 另外,可以使用 |null
或者現有的 ?
表示法來表示包含 nullable
的聯合體 :
public function foo(Foo|null $foo): void;
public function bar(?Bar $bar): void;
JIT rfc
JIT — just in time — 編譯器雖然不總是在 Web 請求的上下文中,但是有望顯着地提高性能。目前還沒有完成任何准確的基准測試,但是肯定會到來。
如果您想進一步了解 JIT 對 PHP 的作用,可以閱讀我寫過的另一篇文章此處。
屬性 rfc
屬性在其他語言中通常被稱為 注解 ,提供一種在無需解析文檔塊的情況下將元數據添加到類中的方法。
快速瀏覽一下,這里有一份來自 RFC 的屬性示例:
use App\Attributes\ExampleAttribute;
<<ExampleAttribute>> class Foo { <<ExampleAttribute>> public const FOO = 'foo'; <<ExampleAttribute>> public $x; <<ExampleAttribute>> public function foo(<<ExampleAttribute>> $bar) { } }
<<PhpAttribute>> class ExampleAttribute { public $value; public function __construct($value) { $this->value = $value; } }
如果您想深入了解屬性如何工作以及如何構建自己的屬性,您可以在此博客上閱讀有關深入屬性的信息。
新增 static 返回類型 rfc
盡管已經可以返回 self,但是 static 直到 PHP 8 才是有效的返回類型 。考慮到 PHP 具有動態類型的性質,此功能對於許多開發人員將非常有用。
class Foo
{
public function test(): static
{
return new static();
}
}
新增 mixed
類型 rfc
有人可能將其稱為必要的邪惡:mixed
類型讓許多人感覺十分混亂。然而,有一個很好的論據支持去實現它:缺少類型在 PHP 中會導致很多情況:
- 函數不返回任何內容或返回空值
- 我們需要多種類型的一種類型
- 我們需要的是 PHP 中不能進行類型提示的類型
因為上述原因,添加 mixed
類型是一件很棒的事兒。mixed
本身代表下列類型中的任一類型:
array
bool
callable
int
float
null
object
resource
string
請注意,mixed 不僅僅可以用來作為返回類型,還可以用作參數和屬性類型。
另外,還需要注意,因為 mixed 類型已經包括了 null,因此 mixed 類型不可為空。下面的代碼會觸發致命錯誤:
// 致命錯誤:混合類型不能為空,null已經是混合類型的一部分。
function bar(): ?mixed {}
throw
表達式 rfc
該 RFC 將 throw
從一個語句更改為一個表達式,這使得可以在很多新地方拋出異常:
$triggerError = fn () => throw new MyError();
$foo = $bar['offset'] ?? throw new OffsetDoesNotExist('offset');
弱映射 rfc
基於在 PHP 7.4 中新增的 弱引用 RFC,PHP 8 中新增了 WeakMaps(弱映射)的實現。 WeakMaps(弱映射)在保持對一些對象的引用的同時,並不會組織這些對象被垃圾回收機制處理 。
以 ORM 為例,它們通常實現保存對實體類的引用的緩存,從而提高實體類之間關聯的性能。 只要緩存中存在對這些實體類的引用,那么這些類就無法被垃圾回收機制回收,盡管除了緩存中,已經沒有別處再引用這些實體類,它們依然不會被垃圾處理機制處理。
如果這個緩存層使用了弱引用和弱映射,那么 PHP 將會在這些實體類沒有任何其他引用時,對其進行垃圾回收。 尤其是對於 ORMs,它可以管理一個請求中的數百個 (如果不是數千個) 實體;弱映射可以提供一種更好的、對資源更友好的方式來處理這些對象。
下面是弱映射基本的例子,摘抄自 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);
Non-capturing catches rfc
在 PHP 8 之前,無論何時你想要捕獲一個異常,你都需要先將其存儲到一個變量中,不管這個變量你是否會用到。通過 Non-capturing catches 你可以忽略變量,所以替換下面的代碼:
try {
// Something goes wrong
} catch (MySpecialException $exception) {
Log::error("Something went wrong");
}
你現在可以這么做:
try {
// Something goes wrong
} catch (MySpecialException) {
Log::error("Something went wrong");
}
請注意,必須始終指定類型,不允許將 catch
留空,如果你想要捕獲所有類型的異常和錯誤,需要使用 Throwable
作為捕獲類型。
參數列表中的尾部逗號 rfc
當調用函數時已經支持尾部逗號,但是參數列表中仍然缺少尾隨逗號支持。現在 PHP8 中允許這樣做,這意味着您可以執行以下操作:
public function(
string $parameterA,
int $parameterB,
Foo $objectfoo,
) {
// …
}
從接口創建 DateTime 對象
你已經可以使用 DateTime::createFromImmutable($immutableDateTime) 從 DateTimeImmutable 對象創建一個 DateTime 對象, 而另一種方法則更加取巧。通過添加 DateTime::createFromInterface() 和 DatetimeImmutable::createFromInterface() 現在有一種通用的方法可以將 DateTime 和 DatetimeImmutable 對象相互轉換。
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')) { /* … */ }
新增 str_starts_with()
和 str_ends_with()
函數 rfc
這是另外兩個早該出現的函數,現在已在核心函數中添加了這兩個函數。
str_starts_with('haystack', 'hay'); // true
str_ends_with('haystack', 'stack'); // true
新增 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()
之間的差異的完整列表。
新增 get_resource_id()
函數 pr
資源是 PHP 中的特殊變量,指的是外部資源。一個示例是 MySQL 連接,另一個是文件句柄。
這些資源中的每一個都分配有一個 ID,然而在這之前,如果想獲取某資源的 ID,唯一方法是將資源轉換為 int
:
$resourceId = (int) $resource;
PHP 8 添加了 get_resource_id()
函數,使此操作更加明顯且類型安全:
$resourceId = get_resource_id($resource);
Traits 改進中的抽象方法 rfc
Traits 可以指定必須由使用它們的類所實現的抽象方法。需要注意的是: 在 PHP 8 之前,尚未驗證這些方法已經實現的標識。以下內容有效:
trait Test {
abstract public function test(int $input): int;
}
class UsesTrait
{
use Test;
public function test($input)
{
return $input;
}
}
當使用 Traits 並實現其抽象方法時,PHP 8 將執行適當的方法進行標識驗證抽象方法是否確實被實現。這意味着您需要編寫以下代碼:
class UsesTrait
{
use Test;
public function test(int $input): int
{
return $input;
}
}
token_get_all()
rfc 的對象實現
token_get_all() 函數返回一個值數組,該 RFC 使用 PhpToken :: getAll() 方法新增了 PhpToken 類。此實現適用於對象而不是普通值。它消耗更少的內存,並且更易於閱讀。
可變語法調整 rfc
在 RFC 中:“統一變量語法 RFC 解決了 PHP 變量語法中的許多不一致之處。該 RFC 旨在解決一小部分被忽略的情況。”
內部函數的類型注解 externals
許多人 投入 了為所有內部函數添加適當的類型注釋的工作。這是一個長期存在的問題,最終可以通過以前版本中對 PHP 所做的所有更改來解決。這意味着內部函數和方法將在反射中具有完整的類型信息。
重大變化
如前所述:這是一個重大更新,因此會有重大變化。最好的辦法是查看 升級 文檔中所列的重大變化的完整列表。
許多這些突破性的更改在以前的 7.* 版本中已被棄用,因此如果你多年來一直保持 PHP 在最新狀態,升級到 PHP 8 應該沒那么難。
一致的類型錯誤 rfc
之前版本在出現類型錯誤時,PHP 中的用戶定義函數已經會拋出 TypeErrors
,但是內部函數不會這么做,而是發出警告並返回 null
。從 PHP 8 開始,內部函數的行為已變得和用戶定義函數一致。
重新分類的引擎警告 rfc
許多以前僅觸發警告或通知的錯誤已轉換為適當的錯誤。以下警告已更改。
- 變量未定義:Error 異常代替通知
- 數組索引未定義:警告代替通知
- 除以零:DivisionByZeroError 異常代替警告
- 嘗試添加 / 移除非對象的屬性 '% s' :Error 異常代替警告
- 嘗試修改非對象的屬性 '% s' :Error 異常代替警告
- 嘗試分配非對象的屬性 '% s' :Error 異常代替警告
- 從空值創建默認對象:Error 異常代替警告
- 嘗試獲取非對象的屬性 '% s' :警告代替通知
- 未定義的屬性:% s::$% s:警告代替通知
- 無法添加元素到數組,因為下一個元素已被占用:Error 異常代替警告
- 無法在非數組變量中銷毀偏移量:Error 異常代替警告
- 無法將標量值用作數組:Error 異常代替警告
- 只有數組和 Traversables 可以被解包:TypeError 異常代替警告
- 為 foreach () 提供了無效的參數:TypeError 異常代替警告
- 偏移量類型非法:TypeError 異常代替警告
- isset 或 empty 中的偏移量類型非法:TypeError 異常代替警告
- unset 中的偏移量類型非法:TypeError 異常代替警告
- 數組到字符串的轉換:警告代替通知
- 資源 ID#% d 用作偏移量,轉換為整數 (% d):警告代替通知
- 發生字符串偏移量轉換:警告代替通知
- 未初始化的字符串偏移量:% d:警告代替通知
- 無法將空字符串分配給字符串偏移量:Error 異常代替警告
- 提供的資源不是有效的流資源:TypeError 異常代替警告
@ 運算符不再使致命錯誤不提醒
此更改可能會使 PHP 8 之前的版本被 @ 隱藏的錯誤再次顯示出來。請確保在生產服務器上設置了 display_errors=Off !
默認錯誤報告級別
現在的默認錯誤報告級別是 E_ALL 而不是之前的除 E_NOTICE 和 E_DEPRECATED 的所有內容。這意味着可能會彈出許多錯誤,這些錯誤以前曾被忽略,盡管在 PHP 8 之前的版本中可能已經存在。
默認 PDO 錯誤模式 rfc
根據 RFC:當前 PDO 的默認錯誤模式為靜默。這意味着當出現 SQL 錯誤時,除非開發人員實現了自己的顯式錯誤處
理,否則不會發出任何錯誤或警告,也不會引發任何異常。
此 RFC 將在 PHP 8 中將默認 PDO 錯誤模式 改為 PDO::ERRMODE_EXCEPTION
。
串聯優先級 rfc
在 PHP 7.4 中已廢棄的同時,此變更開始生效。如果你像這樣子書寫:
echo "sum: " . $a + $b;
PHP 以前會如是理解:
echo ("sum: " . $a) + $b;
PHP 8 將這么做故理解為此:
echo "sum: " . ($a + $b);
更嚴格的算術和位運算類型檢查 rfc
PHP 8 以前,算術或位運算符用於數組、資源或對象是可接受的。現在不再可接受,並會拋出一個 類型錯誤
:
[] % [42];
$object + 4;
反射方法簽名變更
反射類的 3 個方法簽名已變更:
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);
幾個棄用
在 PHP 7. * 的開發期間,添加了幾個棄用版本,這些棄用已於 PHP 8 最終確定。
原文地址:https://stitcher.io/blog/new-in-php-8
譯文地址:https://learnku.com/php/t/44904
PHP高級架構學習視頻:https://www.bilibili.com/video/BV1Jp4y1v7hG
更多PHP內容請訪問:
對PHP后端技術,對PHP架構技術感興趣的朋友,我的官方群點擊此處,一起學習,相互討論。
群內已經有管理將知識體系整理好(源碼,學習視頻等資料),歡迎加群免費領取。


