php7.4新特性
What’s New in PHP 7.4 (Features, Deprecations, Speed)
官網:https://wiki.php.net/rfc#php_74
PHP 7.4 中有哪些新功能?
在這篇文章中,我們將介紹 PHP 7.4 最終版本中應該添加到語言中的一些更改和功能:
忘記array_merge:PHP 7.4 在數組表達式中引入擴展運算符
自 PHP 5.6 起可用,參數解包是一種將數組和Traversable解包到參數列表中的語法。要解壓數組或 Traversable,必須在其前面加上…(3 個點),如下例所示:
function test(...$args) { var_dump($args); } test(1, 2, 3);現在這個 PHP 7.4 RFC提議將此功能擴展到數組定義:
$arr = [...$args];數組表達式中擴展運算符的第一個聲明的好處就是性能。事實上,RFC 文檔指出:
擴展運算符的性能應該比array_merge.?這不僅是因為展開運算符是一種語言結構,而array_merge它是一種函數,還因為編譯時優化可以對常量數組執行。
Spread 運算符的一個顯著優勢是它支持任何可遍歷的對象,而該array_merge函數僅支持數組。
以下是數組表達式中參數解包的示例:
$parts = ['apple', 'pear']; $fruits = ['banana', 'orange', ...$parts, 'watermelon']; var_dump($fruits);如果您在 PHP 7.3 或更早版本中運行此代碼,PHP 會拋出 Parse 錯誤:
Parse error: syntax error, unexpected '...' (T_ELLIPSIS), expecting ']' in /app/spread-operator.php on line 3相反,PHP 7.4 會返回一個數組:
array(5) {[0]=>string(6) "banana"[1]=>string(6) "orange"[2]=>string(5) "apple"[3]=>string(4) "pear"[4]=>string(10) "watermelon" }RFC 聲明我們可以多次擴展同一個數組。此外,我們可以在數組中的任何地方使用擴展運算符語法,因為可以在擴展運算符之前或之后添加普通元素。所以下面的代碼將像我們預期的那樣工作:
$arr1 = [1, 2, 3]; $arr2 = [4, 5, 6]; $arr3 = [...$arr1, ...$arr2]; $arr4 = [...$arr1, ...$arr3, 7, 8, 9];也可以將函數返回的數組直接解包到新數組中:
function buildArray(){return ['red', 'green', 'blue']; } $arr1 = [...buildArray(), 'pink', 'violet', 'yellow'];PHP 7.4 輸出以下數組:
array(6) {[0]=>string(3) "red"[1]=>string(5) "green"[2]=>string(4) "blue"[3]=>string(4) "pink"[4]=>string(6) "violet"[5]=>string(6) "yellow" }我們還可以使用生成器語法:
function generator() {for ($i = 3; $i <= 5; $i++) {yield $i;} } $arr1 = [0, 1, 2, ...generator()];但是我們不允許對通過引用傳遞的數組進行解包??紤]以下示例:
$arr1 = ['red', 'green', 'blue']; $arr2 = [...&$arr1];如果我們嘗試通過引用解包數組,PHP 會拋出以下 Parse 錯誤:
Parse error: syntax error, unexpected '&' in /app/spread-operator.php on line 3無論如何,如果第一個數組的元素是通過引用存儲的,那么它們也會通過引用存儲在第二個數組中。這是一個例子:
$arr0 = 'red'; $arr1 = [&$arr0, 'green', 'blue']; $arr2 = ['white', ...$arr1, 'black'];這是我們使用 PHP 7.4 得到的:
array(5) {[0]=>string(5) "white"[1]=>&string(3) "red"[2]=>string(5) "green"[3]=>string(4) "blue"[4]=>string(5) "black" }Spread 運營商提案以 43 票對 1 票通過。
箭頭函數 2.0(短閉包)
在 PHP 中,匿名函數被認為非常冗長且難以實現和維護。這個 RFC提議引入箭頭函數(或短閉包)的更短更清晰的語法,這應該允許我們顯著清理我們的 PHP 代碼。
考慮以下示例:
function cube($n){return ($n * $n * $n); } $a = [1, 2, 3, 4, 5]; $b = array_map('cube', $a); print_r($b);PHP 7.4 允許使用更簡潔的語法,上面的函數可以重寫如下:
$a = [1, 2, 3, 4, 5]; $b = array_map(fn($n) => $n * $n * $n, $a); print_r($b);目前,匿名函數(閉包)可以繼承父作用域中定義的變量,這要歸功于use語言結構,如下所示:
$factor = 10; $calc = function($num) use($factor){return $num * $factor; };但是在 PHP 7.4 中,定義在父作用域中的變量被值隱式捕獲(隱式按值作用域綁定)。所以我們可以將上面看到的整個函數寫在一行上:
$factor = 10; $calc = fn($num) => $num * $factor;在父作用域中定義的變量可以在箭頭函數中使用,就像我們使用 一樣,并且不能從父作用域修改變量。use($var)
新語法是對語言的極大改進,因為它允許我們構建更具可讀性和可維護性的代碼。我們還可以使用參數和返回類型、默認值、變長參數列表(可變參數函數),我們可以通過引用傳遞和返回等。最后,短閉包也可以用在類方法中,它們可以利用$this變量就像常規閉包一樣。
該 RFC 以 51 票對 8 票獲得批準,因此我們可以期待它成為 PHP 7.4 新增內容的一部分。
空合并賦值運算符
在 PHP 7 中添加,當我們需要將三元??運算符與isset().?如果存在且不存在,則返回第一個操作數NULL。否則,它返回第二個操作數。這是一個例子:
$username = $_GET['user'] ?? 'nobody';這段代碼的作用非常簡單:它獲取請求參數并設置一個默認值(如果它不存在)。那行的意思很清楚,但是如果我們有更長的變量名,就像 RFC 中的這個例子一樣呢?
$this->request->data['comments']['user_id'] = $this->request->data['comments']['user_id'] ?? 'value';從長遠來看,這段代碼可能有點難以維護。因此,為了幫助開發人員編寫更直觀的代碼,本 RFC提議引入空值合并賦值運算符(???=)。因此,我們可以編寫以下代碼,而不是編寫前面的代碼:
$this->request->data['comments']['user_id'] ??= 'value';如果左側參數null的值為 ,則使用右側參數的值。
請注意,雖然合并運算符是比較運算符,但??=也是賦值運算符。
類型化屬性 2.0
參數類型聲明或類型提示允許指定預期傳遞給函數或類方法的變量的類型。類型提示是 PHP 5 以來可用的功能,從PHP 7.2開始,我們可以將它們與object數據類型一起使用?,F在 PHP 7.4 通過添加對一流屬性類型聲明的支持,將類型提示向前推進了一步。這是一個非?;镜睦?#xff1a;
class User {public int $id;public string $name; }支持所有類型,除了void和callable:
public int $scalarType; protected ClassName $classType; private ?ClassName $nullableClassType;voidRFC 解釋了不支持和callable不支持的原因:
不支持 void 類型,因為它沒有用處并且語義不明確。
不支持可調用類型,因為它的行為取決于上下文。
所以我們可以安全地使用bool,?int,?float,?string,?array,?object,?iterable,?self,?parent, 任何類或接口名稱,以及可為空的?類型(??type)。
類型可用于靜態屬性:
public static iterable $staticProp;它們也可以使用以下var符號:
var bool $flag;可以設置默認屬性值,當然必須與聲明的屬性類型匹配,但只有可空屬性可以有默認null值:
public string $str = "foo"; public ?string $nullableStr = null;同一類型適用于單個聲明中的所有屬性:
public float $x, $y;如果我們在屬性類型上出錯會發生什么?考慮以下代碼:
class User {public int $id;public string $name; }$user = new User; $user->id = 10; $user->name = [];在上面的代碼中,我們聲明了一個字符串屬性類型,但是我們設置了一個數組作為屬性值。在這種情況下,我們會收到以下致命錯誤:
Fatal error: Uncaught TypeError: Typed property User::$name must be string, array used in /app/types.php:9弱引用
在這個 RFC中,PHP 7.4 引入了WeakReference類,它允許程序員保留對對象的引用,而不會阻止對象本身被破壞。
目前,PHP 通過使用像 pecl-weakref 這樣的擴展來支持弱引用。無論如何,新的 API 與文檔WeakRef類不同。
以下是該提案的作者 Nikita Popov的一個示例:
$object = new stdClass; $weakRef = WeakReference::create($object);var_dump($weakRef->get()); unset($object); var_dump($weakRef->get());第一個var_dump打印,而第二個打印,因為引用的對象已被銷毀。object(stdClass)#1 (0) {}var_dumpNULL
協變返回和逆變參數
方差是類層次結構的一個屬性,描述了類型構造函數的類型如何影響子類型。通常,類型構造函數可以是:
- Invariant:如果超類型的類型約束子類型的類型。
- Covariant:如果保留類型的順序(類型從更具體到更通用排序)。
- 逆變:如果它顛倒了順序(類型從更通用到更具體排序)。
目前,PHP 大多具有不變的參數和返回類型,只有少數例外。該 RFC 建議允許參數類型和返回類型的協變和逆變,還提供了幾個代碼示例。
這是協變返回類型的示例:
interface Factory {function make(): object; }class UserFactory implements Factory {function make(): User; }這是逆變參數類型的示例:
interface Concatable {function concat(Iterator $input); }class Collection implements Concatable {// accepts all iterables, not just Iteratorfunction concat(iterable $input) {/* . . . */} }有關PHP 7.4 中協變和逆變的詳細信息,請參閱RFC 。
該 RFC 以 39 票對 1 票通過。
預加載
Dmitry Stogov 的這個提議是我們最喜歡的提議之一,因為它應該會顯著提升性能。預加載是在模塊初始化時將庫和框架加載到OPCache的過程(閱讀有關PHP 生命周期的更多信息)。
?
PHP 生命周期(圖片來源:PHP Internals)
用 Dmitry 的話來說,預加載是如何工作的:
在服務器啟動時——在任何應用程序代碼運行之前——我們可能會將一組特定的 PHP 文件加載到內存中——并使它們的內容“永久可用”到將由該服務器提供服務的所有后續請求。這些文件中定義的所有函數和類都可用于開箱即用的請求,就像內部實體一樣。
這些文件在服務器啟動時加載,在任何應用程序之前執行,并且可用于任何未來的請求。這在性能方面很棒。
預加載由特定php.ini指令控制:opcache.preload.?該指令指定要在服務器啟動時編譯和執行的 PHP 腳本。該文件可用于預加載其他文件,包括它們或通過opcache_compile_file()函數(閱讀PHP 文檔的更多信息)。
但有一個缺點。事實上,RFC 明確指出:
預加載的文件永遠緩存在 opcache 內存中。如果不重新啟動另一臺服務器,則修改其相應的源文件不會產生任何影響。
但是,預加載文件中定義的所有函數都將永久加載到 PHP 函數和類表中,并且對于以后的每個請求都可用。這將導致良好的性能改進,即使這些改進可能會有很大的變化。
您可以在官方Preloading RFC 頁面上閱讀有關預加載的限制和例外的更多信息。
新的自定義對象序列化機制
這是Nikita Popov 的另一項提案,以多數票通過。
目前,我們有兩種不同的機制來自定義 PHP 中的對象序列化:
- 和__sleep()魔術__wakeup()方法
- Serializable界面_
根據 Nikita 的說法,這兩個選項都存在導致代碼復雜和不可靠的問題。您可以在RFC中深入研究該主題。在這里我只提到新的序列化機制應該通過提供兩個新的魔術方法來防止這些問題,__serialize()并且__unserialize()結合了兩個現有的機制。
棄用
PHP 7.4 將棄用以下函數/功能。如需更全面的棄用列表,請查看PHP 7.4 升級說明。
更改串聯運算符的優先級
目前,在 PHP 中,“+”和“-”算術運算符以及“.”?字符串運算符是左關聯的并且具有相同的優先級(閱讀有關運算符優先級的更多信息)。
例如,考慮以下行:
echo "sum: " . $a + $b;在 PHP 7.3 中,此代碼會產生以下警告:
Warning: A non-numeric value encountered in /app/types.php on line 4這是因為連接是從左到右評估的。這與編寫以下代碼相同:
echo ("sum: " . $a) + $b;該 RFC提議更改運算符的優先級,給出“.”?比“+”和“-”運算符的優先級低,因此加法和減法總是在字符串連接之前執行。該行代碼應等效于以下內容:
echo "sum: " . ($a + $b);這是一個兩步提案:
- 從 7.4 版開始,當遇到帶有“+”、“-”和“.”的無括號表達式時,PHP 應該發出棄用通知。
- 這些運算符的優先級的實際更改應在PHP 8中添加。
兩項提案均以多數票獲得通過。
棄用左結合三元運算符
在 PHP 中,與許多其他語言不同,三元運算符是左結合的。根據 Nikita Popof 的說法,這可能會讓在不同語言之間切換的程序員感到困惑。
目前,在 PHP 中,以下代碼是正確的:
$b = $a == 1 ? 'one' : $a == 2 ? 'two' : $a == 3 ? 'three' : 'other';它被解釋為:
$b = (($a == 1 ? 'one' : $a == 2) ? 'two' : $a == 3) ? 'three' : 'other';這可能會導致錯誤,因為這可能不是我們打算做的。因此,此 RFC 建議棄用并刪除對三元運算符的左關聯性的使用,并強制開發人員使用括號。
這是另一個兩步建議:
- 從 PHP 7.4 開始,沒有明確使用括號的嵌套三元組將引發棄用警告。
- 從 PHP 8.0 開始,會出現編譯運行時錯誤。
總結
- 上一篇: sanity测试_Sanity.io入门
- 下一篇: php英文文献翻译,php外文文献翻译_