PHP 8,PHP 的一個新的大版本,預計將於2020年12月3日發布,這意味著將不會有 PHP 7.5 版本。PHP8目前正處於非常活躍的開發階段,所以在接下來的幾個月裡,情況可能會發生很大的變化。
在這篇文章中,我會維持一個最新的清單列表,列出預計會出現的新特性、效能提升和突破性的變化。由於 PHP 8 是一個新的大版本,因此您的程式碼被破壞的可能性更高。如果您始終保持執行 PHP 的最新版本,那麼升級相對來說就會輕鬆很多,因為在7. *版本中,大多數重大更改均已棄用。
除重大更改外,PHP 8還帶來了一些不錯的新功能,比如說 JIT編譯器, 聯合型別, 屬性,以及更多。
從新特性開始,請記住 PHP8 仍處於活動開發階段,因此此列表將隨著時間的推移而增長。
考慮到 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 — just in time — 編譯器雖然不總是在 Web 請求的上下文中,但是有望顯著地提高效能。目前還沒有完成任何準確的基準測試,但是肯定會到來。
如果您想進一步了解JIT對PHP的作用,可以閱讀我寫過的另一篇文章此處。
屬性在其他語言中通常被稱為 註解 ,提供一種在無需解析文件塊的情況下將後設資料新增到類中的方法。
快速瀏覽一下,這裡有一份來自 RFC 的屬性範例:
use AppAttributesExampleAttribute; <<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
返回型別儘管已經可以返回 self
,但是 static
直到 PHP 8 才是有效的返回型別 。考慮到 PHP 具有動態型別的性質,此功能對於許多開發人員將非常有用。
class Foo { public function test(): static { return new static(); } }
mixed
型別有人可能將其稱為必要的邪惡:mixed
型別讓許多人感覺十分混亂。然而,有一個很好的論據支援去實現它:缺少型別在 PHP 中會導致很多情況:
函數不返回任何內容或返回空值
我們需要多種型別的一種型別
我們需要的是PHP中不能進行型別提示的型別
因為上述原因,新增 mixed
型別是一件很棒的事兒。mixed
本身代表下列型別中的任一型別:
array
bool
callable
int
float
null
object
resource
string
請注意,mixed
不僅僅可以用來作為返回型別,還可以用作引數和屬性型別。
另外,還需要注意,因為 mixed
型別已經包括了 null
,因此 mixed
型別不可為空。下面的程式碼會觸發致命錯誤:
// 致命錯誤:混合型別不能為空,null已經是混合型別的一部分。 function bar(): ?mixed {}
throw
表示式該RFC將throw
從一個語句更改為一個表示式,這使得可以在很多新地方丟擲異常:
$triggerError = fn () => throw new MyError(); $foo = $bar['offset'] ?? throw new OffsetDoesNotExist('offset');
基於在 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
一個很小但是很有用的新特性:現在可以在物件上使用 :: class
,而不必在物件上使用 get_class()
,它的工作方式跟 get_class()
相同。
$foo = new Foo(); var_dump($foo::class);
在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
作為捕獲型別。
當呼叫函數時已經支援尾部逗號,但是參數列中仍然缺少尾隨逗號支援。現在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
介面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()
函數這是另外兩個早該出現的函數,現在已在核心函數中新增了這兩個函數。
str_starts_with('haystack', 'hay'); // true str_ends_with('haystack', 'stack'); // true
fp()
函數新的fp()
函數的作用類似於fmod()
和intp()
函數,它們可以除以0。視情況而定,將得到INF
,-INF
或NAN
。
get_debug_type()
函數get_debug_type()
返回變數的型別,聽起來好像跟 gettype()
的作用一樣啊?get_debug_type()
可以為陣列,字串,匿名類和物件返回更有用的輸出資訊。
例如,在類 Foo Bar
上呼叫gettype()
將返回object
,而使用get_debug_type()
將返回類名。
如下表:
Value | get_debug_type() | gettype() |
---|---|---|
0 | int | integer |
0.1 | float | double |
true | bool | boolean |
false | bool | boolean |
「hello」 | string | |
[] | array | |
null | null | NULL |
A class with name 「FooBar」 | FooBar | object |
An anonymous class | [email protected] | object |
A resource | resource (xxx) | resource |
A closed resource | resource (closed) |
可以在RFC中找到get_debug_type()
和gettype()
之間的差異的完整列表。
get_resource_id()
函數資源是PHP中的特殊變數,指的是外部資源。一個範例是MySQL連線,另一個是檔案控制代碼。
這些資源中的每一個都分配有一個ID,然而在這之前,如果想獲取某資源的ID,唯一方法是將資源轉換為int
:
$resourceId = (int) $resource;
PHP 8新增了get_resource_id()
函數,使此操作更加明顯且型別安全:
$resourceId = get_resource_id($resource);
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解決了PHP變數語法中的許多不一致之處。該RFC旨在解決一小部分被忽略的情況。」
許多人 投入 了為所有內部函數新增適當的型別注釋的工作。這是一個長期存在的問題,最終可以通過以前版本中對PHP所做的所有更改來解決。這意味著內部函數和方法將在反射中具有完整的型別資訊。
如前所述:這是一個重大更新,因此會有重大變化。最好的辦法是檢視 升級 文件中所列的重大變化的完整列表。
許多這些突破性的更改在以前的 7.* 版本中已被棄用,因此如果你多年來一直保持 PHP 在最新狀態,升級到 PHP 8 應該沒那麼難。
之前版本在出現型別錯誤時,PHP 中的使用者定義函數已經會丟擲 TypeErrors
,但是內部函數不會這麼做,而是發出警告並返回 null
。從 PHP 8 開始,內部函數的行為已變得和使用者定義函數一致。
許多以前僅觸發警告或通知的錯誤已轉換為適當的錯誤。以下警告已更改。
變數未定義:Error
異常代替通知
陣列索引未定義:警告代替通知
除以零:pisionByZeroError
異常代替警告
嘗試新增/移除非物件的屬性 '%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 之前的版本中可能已經存在。
根據RFC:當前 PDO 的預設錯誤模式為靜默。這意味著當出現 SQL 錯誤時,除非開發人員實現了自己的顯式錯誤處理,否則不會發出任何錯誤或警告,也不會引發任何異常。
此 RFC 將在 PHP 8 中將預設 PDO 錯誤模式 改為PDO::ERRMODE_EXCEPTION
。
在 PHP 7.4 中已廢棄的同時,此變更開始生效。如果你像這樣子書寫:
echo "sum: " . $a + $b;
PHP 以前會如是理解:
echo ("sum: " . $a) + $b;
PHP 8 將這麼做故理解為此:
echo "sum: " . ($a + $b);
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 最終確定。
以上就是PHP 8 中新特性以及重大調整的詳細內容,更多請關注TW511.COM其它相關文章!