原文連接: www.ffeeii.com/1075.html
下一個PHP 7里程版本PHP 7.4預計將於2019年11月28日正式發佈。所以,如今該讓咱們深刻研究一些最使人興奮的新增功能和新功能,這些功能將使PHP更快,更可靠。 。
php
實際上,即便PHP 7.4顯着提升了性能並提升了代碼的可讀性,PHP 8仍將是PHP性能的真正里程碑,由於JIT包含的建議已獲得批准。html
不管如何,今天咱們正在經歷一些咱們指望的PHP 7.4最有趣的功能和更改。 所以,在閱讀這篇文章以前,請確保保存如下日期:git
6月6日:PHP 7.4 Alpha 1github
7月18日:PHP 7.4 Beta 1 –功能凍結編程
11月28日:PHP 7.4 GA發佈c#
您能夠在RFC官方頁面上查看功能和添加項的完整列表。數組
PHP 7.4發佈日期:
PHP 7.4計劃於2019年11月28日發佈。它是下一個PHP 7次要版本,應再次提升性能並提升代碼的可讀性/可維護性。緩存
在本文中,咱們討論了PHP 7.4最終版本中應在語言中添加的一些更改和功能:bash
自 PHP 5.6 起可用,參數解包是將數組和 Traversable 解包爲參數列表的語法。要解壓一個數組或 Traversable,必須以 …(3 點)爲前綴,以下例所示:服務器
function test(...$args) { var_dump($args); }
test(1, 2, 3);複製代碼
然而 PHP 7.4 RFC 建議將此功能擴展到數組中去定義:
$arr = [...$args];複製代碼
Spread 運算符的第一個好處就是性能,RPC 文檔指出:
Spread 運算符應該比
array_merge
擁有更好的性能。這不只僅是 Spread 運算符是一個語法結構,而array_merge
是一個方法。仍是在編譯時,優化了高效率的常量數組
Spread 運算符的一個顯着優勢是它支持任何可遍歷的對象,而該 array_merge
函數僅支持數組。如下是數組中參數帶有 Spread 運算符的示例:
$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 聲明咱們能夠屢次擴展同一個數組。此外,咱們能夠在數組中的任何位置使用 Spread Operator 語法,由於能夠在 spread 運算符以前或以後添加常規元素。所以,如下代碼將按預期工做:
$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"
}複製代碼
在 PHP 中,匿名函數被認爲是很是冗長且難以實現和難以維護的,RFC 建議引入更簡單,更清晰的箭頭函數(或簡短閉包)語法,這樣咱們就能夠簡潔地編寫代碼。在 PHP 7.4 之前:
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
是等價的,而且不可能被父級所修改。新語法是對語言的一個很大改進,由於它容許咱們構建更易讀和可維護的代碼。
因爲平常使用中存在大量同時使用三元表達式和 isset () 的狀況, 咱們添加了 null 合併運算符 (??) 這個語法糖。若是變量存在且值不爲 NULL, 它就會返回自身的值,不然返回它的第二個操做數。
$username = $_GET['user'] ?? ‘nobody';複製代碼
這段代碼的做用很是簡單:它獲取請求參數並設置默認值(若是它不存在)。可是在 RFC 這個例子中,若是咱們有更長的變量名稱呢?
$this->request->data['comments']['user_id'] = $this->request->data['comments']['user_id'] ?? 'value';複製代碼
長遠來看,這段代碼可能難以維護。所以,旨在幫助開發人員編寫更直觀的代碼,這個 RFC 建議引入 null 合併等於運算符 (null_coalesce_equal_operator)??=
,因此咱們能夠敲下面這段代碼來替代上面的這段代碼:
$this->request->data['comments']['user_id'] ??= ‘value’;複製代碼
若是左側參數的值爲 null
,則使用右側參數的值。
注意,雖然 coalesce 運算符 ??
是一個比較運算符,但 ??=
它是賦值運算符。
類型的聲明,類型提示,以及指定肯定類型的變量傳遞給函數或類的方法。其中類型提示是在 PHP5 的時候有的一個功能,PHP 7.2 的時候添加了 object
的數據類型。而 PHP7.4 更是增長了主類屬性聲明,看下面的例子:
class User {
public int $id;
public string $name;
}複製代碼
除了 void
和 callable
外,全部的類型都支持
public int $scalarType;
protected ClassName $classType;
private ?ClassName $nullableClassType;複製代碼
爲何不支持 void
和 callable
?下面是 RFC 的解釋
The
void
type is not supported, because it is not useful and has unclear semantics.
不支持void
類型,是由於它沒用,而且語義不清晰。The
callable
type is not supported, because its behavior is context dependent.
不支持callable
類型,由於其行爲取決於上下文。
所以,咱們能夠放心使用 bool
,int
,float
,string
,array
,object
,iterable
,self
,parent
,固然還有咱們不多使用的 nullable
空容許 (?type
)
因此你能夠在 PHP7.4 中這樣敲代碼:
// 靜態屬性的類型
public static iterable $staticProp;
// var 中聲明屬性
var bool $flagl
// 設置默認的值
// 注意,只有 nullable 的類型,才能設置默認值爲 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 中,提議引入 WeakReference
這個類,弱引用容許編碼時保留對對象的引用,該引用不會阻止對象被破壞;這對於實現相似於緩存的結構很是有用。
該提案的做者 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_dump,當 object 被銷燬的時候,並不會拋出致命錯誤
NULL複製代碼
Invariant
的參數類型,而且大多數是 Invariant
的返回類型,這就意味着當我是 T 參數類型或者返回類型時,子類也必須是 T 的參數類型或者返回類型。可是每每會須要處理一些特殊狀況,好比具體的返回類型,或者通用的輸入類型。而 RFC 的這個提案就提議,PHP7.4 添加協變返回和逆變參數,如下是提案給出來的例子:協變返回:interface Factory {
function make(): object;
}
class UserFactory implements Factory {
// 將比較泛的 object 類型,具體到 User 類型
function make(): User;
}複製代碼
逆變參數:
interface Concatable {
function concat(Iterator $input);
}
class Collection implements Concatable {
// 將比較具體的 `Iterator`參數類型,逆變成接受全部的 `iterable`類型
function concat(iterable $input) {/* . . . */}
}複製代碼
這個 RFC 是由 Dmitry Stogov 提出的,預加載是在模塊初始化的時候,將庫和框架加載到 OPCache 中的過程,以下圖所示
引用他的原話:
On server startup – before any application code is run – we may load a certain set of PHP files into memory – and make their contents 「permanently available」 to all subsequent requests that will be served by that server. All the functions and classes defined in these files will be available to requests out of the box, exactly like internal entities.
服務器啓動時 – 在運行任何應用程序代碼以前 – 咱們能夠將一組 PHP 文件加載到內存中 – 並使得這些預加載的內容,在後續的全部請求中 「永久可用」。這些文件中定義的全部函數和類在請求時,就能夠開箱即用,與內置函數相同。
預加載由 php.ini
的 opcache.preload
進行控制。這個參數指定在服務器啓動時編譯和執行的 PHP 腳本。此文件可用於預加載其餘文件,或經過 opcache_compile_file()
函數
這在性能上有很大的提高,可是也有一個很明顯的缺點,RFC 提出來了
preloaded files remain cached in opcache memory forever. Modification of their corresponding source files won’t have any effect without another server restart.
預加載的文件會被永久緩存在 opcache 內存中。在修改相應的源文件時,若是沒有重啓服務,修改就不會生效。
這是尼基塔·波波夫(Nikita Popov)的另外一項建議 ,獲得了絕大多數票的批准。
當前,咱們有兩種不一樣的機制能夠在PHP中對對象進行自定義序列化:
__sleep()
和__wakeup()
魔術方法Serializable
接口根據Nikita的說法,這兩個選項都存在致使複雜且不可靠的代碼的問題。 您能夠在RFC中深刻研究此主題。 在這裏,我只提到新的序列化機制應該經過提供兩個結合了兩個現有機制的新魔術方法__serialize()
和__unserialize()
來防止這些問題。
該提案以20票對7票得到經過。
目前,在 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);複製代碼
這個提案分爲兩步走:
+
-
和 .
在沒有指明執行優先級時,會發出一個棄用通知。$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的Alpha預覽版性能狀態的好奇,我今天針對使用Git構建的PHP 7.3.六、7.2.1八、7.1.29和7.0.32運行了一些快速基準測試,而且每一個發行版均以相同的方式構建。
在此階段,PHPBench的7.4性能與PHP 7.3穩定版至關,已經比PHP 7.0快了約30%…固然,與PHP 5.5的舊時代相比,收益甚至更大。
在微基準測試中,PHP 7.4的運行速度僅比PHP 7.3快一點,而PHP-8.0的性能卻差很少,至少要等到JIT代碼穩定下來並默認打開爲止。
在Phoronix測試套件的內部PHP自基準測試中,PHP 7.4的確確實處於PHP 7.3性能水平之上-至少在此Alpha前狀態下。 自PHP 7.0起,取得了一些顯着的進步,而自PHP5發行緩慢以來,也取得了許多進步。
總結:PHP7.4是一個使人期待的版本,可是PHP8纔是整個PHP界最重大的事情。
參考連接: