PHP 7.4 ,下一個 PHP 7 較小的發佈版,指望在 2019 年 11 月 28 日發佈。所以,如今是時候讓你們深刻了解這個版本添加哪些新特性使 PHP 更快、更可靠。
雖然 PHP 7.4 顯著地提高了性能和提升代碼可讀性,PHP 8 纔將會是 PHP 性能真正的里程碑,這在 JIT inclusion 的提案顯示已充分證實。
如今去無償遷移php
總之,今天咱們將概覽 PHP 7.4 最矚目的特性和性能提高。在繼續探索以前,你最好記住如下重要的時間節點:html
你可從 the official RFC page 查看所有新特性和功能。git
QQ交流羣github
PHP 7.4 將於 2019 年 11 月 28 日發佈。這是 PHP 7 的下一個小版本,會再次提高性能,提升代碼的可讀性和可維護性。docker
在這邊文章中,咱們將討論 PHP 7.4 最終版本中應該增長一些變化和特性:數組
從 PHP 5.6 開始,參數解析 是一種解析數組並遍歷到參數列表中的語法。要解析一個或遍歷一個數組,必須以 ...(三個點)做爲前綴,以下所示:瀏覽器
function test(...$args) { var_dump($args); }
test(1, 2, 3);複製代碼
如今 PHP 7.4 的 RFC 建議將這個功能擴展到數組定義中:安全
$arr = [...$args];複製代碼
擴展操做符在數組表達式 第一個明顯的優勢是性能。RFC 文檔:bash
擴展操做符應該比array_merge
性能更好。不只是由於擴展操做符是一種語言結構,array_merge
是一個函數,還由於能夠優化編譯常量數組的性能。
擴展操做符一個重要的優勢是支持任何可遍歷的對象, array_merge
函數只支持數組。服務器
下面是數組表達式中的參數解析示例:
$parts = ['apple', 'pear'];
$fruits = ['banana', 'orange', ...$parts, 'watermelon'];
var_dump($fruits);複製代碼
若是你在 PHP 7.3 或更早版本中運行此代碼,PHP 將拋出解析錯誤:
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 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"
}複製代碼
The Spread operator 提案以 43 票對 1 票得到經過。
對於 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(變量)
同樣,直接使用在父做用域裏定義的變量,而且它也不會修改父做用域的變量。
新的語法對咱們構建更可讀可維護的代碼帶來了極大的改善。咱們也可使用參數和返回類型、默認值、變長參數列表(可變函數),能夠傳遞或返回引用等等。而後呢,短閉包還能夠被用做類方法,能夠像常規同樣使用 $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
,則使用右側參數的值。請注意,當合並運算符是比較運算符時,??=
就是一個賦值運算符。
這項建議以 37:4 的票數比例得到經過。
參數類型聲明(或類型提示)容許對將要傳遞給函數或者類方法的變量類型進行限定。該功能自 PHP 5 起可用,PHP 7.2 起可使用對象做爲數據類型。如今 PHP 7.4 經過添加 類屬性類型聲明 進一步擴展了類型提示。如下是一個基本的示例:
class User {
public int $id;
public string $name;
}複製代碼
支持 void
與 callable
之外的全部類型
public int $scalarType;
protected ClassName $classType;
private ?ClassName $nullableClassType;複製代碼
這項 RFC 解釋了爲何不支持 void
和 callable
返回值的緣由:
不支持void
類型,由於它沒有用到而且語義不明確。
不支持callable
類型,由於其行爲取決於上下文。
這樣咱們就能夠安全地使用 bool
, int
, float
, string
, array
, object
, iterable
, self
, parent
, 任何類或接口名稱,而且能夠爲空 types (?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 已以 70 票對 1 票得到批准。
在這項 RFC 中,PHP 7.4 引入了 WeakReference (弱引用) 類型,這樣開發者就能夠保留對對象的引用,而這不會阻止對象自己被破壞。
目前,PHP 經過使用諸如 pecl-weakref 之類的擴展名來支持弱引用。 不管如何,新的 API 與記錄的 WeakRef
類不一樣。
這是 一份簡單的 demo 來自這項提議的做者 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
打印引用爲 NULL
,由於所引用的對象已被銷燬。
該 RFC 以 28 票對 5 票得到經過。
方差 是類層次結構的一個屬性,描述了類型構造函數的類型如何影響 subtypes。 一般,類型構造函數能夠是:
目前,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 Iterator
function concat(iterable $input) {/* . . . */}
}複製代碼
請參閱 RFC 以更詳細地瞭解 PHP 7.4 協變量返回和協變量參數。
該 RFC 以 39 對 1 票得到經過。
這項提議 來自 Dmitry Stogov
,這是咱們的受支持的提議之一,由於它能夠顯着提升 PHP 的性能。預加載 是在模塊初始化時將庫和框架加載到 OPCache 的過程,詳細瞭解 PHP 生命週期 。
PHP 生命週期 (資源鏡像: PHP Internals)
用 Dmitry
的話來講,預加載是這樣工做的:
在服務器啓動時(在運行任何應用程序代碼以前),咱們能夠將一組 PHP 文件加載到內存中,並使它們的內容
永久可用
給該服務器將服務的全部後續請求。 與內部實體徹底同樣,這些文件中定義的全部函數和類也可用於開箱即用的請求。
這些文件在服務器啓動時加載,在任何應用程序以前執行,而且對之後的任何請求都可用。 就性能而言,這很棒。
預加載由特定的 php.ini
指令控制:opcache.preload
。 該指令指定在服務器啓動時要編譯和執行的 PHP 腳本。 此文件可用於預加載其餘文件,包括它們或經過 opcache_compile_file()
函數(有關更多信息,請參見 [PHP 文檔](www.php.net/manual/en/f… -file.php))。
可是有一個缺點。 實際上,RFC 裏有明確聲明:
預加載的文件將永遠保留在 opcache 內存中。 不從新啓動另外一臺服務器,對其相應源文件的修改將不會生效。
可是,在預加載的文件中定義的全部函數將被永久加載到 PHP 函數和類表中,而且對於之後的每一個請求都可用。 即便這些改進可能有很大的不一樣,也會帶來良好的性能改進。
您能夠在官方的 預加載 RFC 頁面 上閱讀有關預加載的限制和例外的更多信息。
這是 Nikita Popov
的另外一項提議
當前,咱們有兩種不一樣的機制能夠在 PHP 中對對象進行自定義序列化:
__sleep()
和 __wakeup()
魔術方法Serializable
接口根據 Nikita
的說法,這兩個選項都存在致使複雜且不可靠的代碼的問題。 您能夠在 RFC 中深刻研究此主題。 在這裏我只是提到新的序列化機制應該經過提供兩種新的魔術方法__serialize()
和 __unserialize()
來解決這些問題,這兩種方法結合了兩個現有機制。
該提案以 20 票對 7 票得到經過。
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);複製代碼
這是一個兩步建議:
+
,-
和 .
的非括號表達式時應發出棄用通知。兩項提議均以絕大多數票得到批准。
在 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 建議棄用並刪除三元運算符的左關聯性,並強制開發人員使用括號。
這是另外兩個步驟的建議:
該提案以 35 到 10 票得到批准。
想在 Docker 上試試嗎?幸運的是在 Docker 環境下你不須要再手動編譯和配置 PHP 7.4 了 。若是你已經安裝了 Docker , 那隻須要花幾秒鐘安裝這個非官方的 PHP-FPM 7.4 Docker 鏡像 就能夠在命令行中進行測試了。
若是你想要運行 PHP 7.4 的代碼到你的瀏覽器中,那你還須要給 Docker 安裝 Nginx 或 Apache 鏡像。不用擔憂,只要按照開發指南。 將示例命令拷貝粘貼到命令行中並運行,就能夠了。
但願以上內容能幫助到你們,若是喜歡個人文章,想與一羣資深開發者一塊兒交流學習的話,獲取更多學習資料歡迎加入個人學習交流羣677079770一塊兒學習成長,一樣也能夠關注一下個人專欄。謝謝~