PHP7新特性


PHP7

2015.12.3 發生了兩件大事, PHP7 問世了, Swift 開源了。

最好的語言發布了新的版本,一個划時代的大版本: PHP7 。

PHP7 修復了大量 BUG ,新增了功能和語法糖。這些改動涉及到了核心包、 GD 庫、 PDO 、 ZIP 、 ZLIB 等熟悉和不熟悉的核心功能與擴展包。

PHP7 移除了已經被廢棄的函數,如 mysql_ 系列函數在 PHP5.5 被廢棄,在 PHP7 被刪除。

PHP7 的性能高於 HHVM 。並且是 PHP5.6 的兩倍。

http://php.net/archive/2015.php#id2015-12-03-1

2015 年 12 月 3 號

PHP 開發團隊宣布 PHP 7.0.0 即將上市。本次發布標志着新的重要的 PHP 7 系列的開始。

PHP 7.0.0 附帶了一個新版本的 Zend 引擎中,無數的改進和新功能,如

性能改善:

PHP 7 高達兩倍快的 PHP 5.6

顯著減少內存使用

抽象語法樹

一致的 64 位支持

改進的異常層次結構

許多轉化為異常致命錯誤

安全隨機數發生器

刪除舊的和不支持的 SAPIs 和擴展

空合並運算符(?)

返回和標量類型聲明

匿名類

零成本斷言

這是下一個主要版本的 PHP 。它的發布是近兩年的發展征程的結果。這是核心團隊的一個非常特殊的成就。而且,它是許多活躍的社區成員難以置信努力的結果。事實上,這是一個新的 PHP 一代的崛起與巨大潛力。

恭喜大家,這是一個壯觀的 PHP 的世界!


1.PHP7速度是 PHP5.6 的兩倍


2.JIT - Just in Time compiler (即時編輯器)
Just In Time(即時編譯)是一種軟件優化技術,指在運行時才會去編譯字節碼為機器碼。從直覺出發,我們都很容易認為,機器碼是計算機能夠直接識別和執行的,比起Zend讀取opcode逐條執行效率會更高。其中,HHVM(HipHop Virtual Machine,HHVM是一個Facebook開源的PHP虛擬機)就采用JIT,讓他們的PHP性能測試提升了一個數量級,放出一個令人震驚的測試結果,也讓我們直觀地認為JIT是一項點石成金的強大技術。

而實際上,在2013年的時候,鳥哥和Dmitry(PHP語言內核開發者之一)就曾經在PHP5.5的版本上做過一個JIT的嘗試(並沒有發布)。PHP5.5的原來的執行流程,是將PHP代碼通過詞法和語法分析,編譯成opcode字節碼(格式和匯編有點像),然后,Zend引擎讀取這些opcode指令,逐條解析執行。

而他們在opcode環節后引入了類型推斷(TypeInf),然后通過JIT生成ByteCodes,然后再執行。


3.Zval的改變

PHP的各種類型的變量,其實,真正存儲的載體就是Zval,它特點是海納百川,有容乃大。從本質上看,它是C語言實現的一個結構體(struct)。對於寫PHP的同學,可以將它粗略理解為是一個類似array數組的東西。

PHP5的Zval,內存占據24個字節:
PHP7的Zval,內存占據16個字節:

Zval從24個字節下降到16個字節,為什么會下降呢,這里需要補一點點的C語言基礎,輔助不熟悉C的同學理解。struct和union(聯合體)有點不同,Struct的每一個成員變量要各自占據一塊獨立的內存空間,而union里的成員變量是共用一塊內存空間(也就是說修改其中一個成員變量,公有空間就被修改了,其他成員變量的記錄也就沒有了)。因此,雖然成員變量看起來多了不少,但是實際占據的內存空間卻下降了。

除此之外,還有被明顯改變的特性,部分簡單類型不再使用引用。


4.內部類型zend_string

Zend_string是實際存儲字符串的結構體,實際的內容會存儲在val(char,字符型)中,而val是一個char數組,長度為1(方便成員變量占位)。

結構體最后一個成員變量采用char數組,而不是使用char*,這里有一個小優化技巧,可以降低CPU的cache miss。

如果使用char數組,當malloc申請上述結構體內存,是申請在同一片區域的,通常是長度是sizeof(_zend_string) + 實際char存儲空間。但是,如果使用char*,那個這個位置存儲的只是一個指針,真實的存儲又在另外一片獨立的內存區域內。

從邏輯實現的角度來看,兩者其實也沒有多大區別,效果很類似。而實際上,當這些內存塊被載入到CPU的中,就顯得非常不一樣。前者因為是連續分配在一起的同一塊內存,在CPU讀取時,通常都可以一同獲得(因為會在同一級緩存中)。而后者,因為是兩塊內存的數據,CPU讀取第一塊內存的時候,很可能第二塊內存數據不在同一級緩存中,使CPU不得不往L2(二級緩存)以下尋找,甚至到內存區域查到想要的第二塊內存數據。這里就會引起CPU Cache Miss,而兩者的耗時最高可以相差100倍。

另外,在字符串復制的時候,采用引用賦值,zend_string可以避免的內存拷貝。


5.PHP數組的變化(HashTable和Zend Array)

在編寫PHP程序過程中,使用最頻繁的類型莫過於數組,PHP5的數組采用HashTable實現。如果用比較粗略的概括方式來說,它算是一個支持雙向鏈表的HashTable,不僅支持通過數組的key來做hash映射訪問元素,也能通過foreach以訪問雙向鏈表的方式遍歷數組元素。

這個圖看起來很復雜,各種指針跳來跳去,當我們通過key值訪問一個元素內容的時候,有時需要3次的指針跳躍才能找對需要的內容。而最重要的一點,就在於這些數組元素存儲,都是分散在各個不同的內存區域的。同理可得,在CPU讀取的時候,因為它們就很可能不在同一級緩存中,會導致CPU不得不到下級緩存甚至內存區域查找,也就是引起CPU緩存命中下降,進而增加更多的耗時。

新版本的數組結構,非常簡潔,讓人眼前一亮。最大的特點是,整塊的數組元素和hash映射表全部連接在一起,被分配在同一塊內存內。如果是遍歷一個整型的簡單類型數組,效率會非常快,因為,數組元素(Bucket)本身是連續分配在同一塊內存里,並且,數組元素的zval會把整型元素存儲在內部,也不再有指針外鏈,全部數據都存儲在當前內存區域內。當然,最重要的是,它能夠避免CPU Cache Miss(CPU緩存命中率下降)。


6.函數調用機制(Function Calling Convention)
PHP7改進了函數的調用機制,通過優化參數傳遞的環節,減少了一些指令,提高執行效率。


7.通過宏定義和內聯函數(inline),讓編譯器提前完成部分工作
C語言的宏定義會被在預處理階段(編譯階段)執行,提前將部分工作完成,無需在程序運行時分配內存,能夠實現類似函數的功能,卻沒有函數調用的壓棧、彈棧開銷,效率會比較高。內聯函數也類似,在預處理階段,將程序中的函數替換為函數體,真實運行的程序執行到這里,就不會產生函數調用的開銷。

PHP7在這方面做了不少的優化,將不少需要在運行階段要執行的工作,放到了編譯階段。例如參數類型的判斷(Parameters Parsing),因為這里涉及的都是固定的字符常量,因此,可以放到到編譯階段來完成,進而提升后續的執行效率。

PHP7新特性 What will be in PHP 7/PHPNG
http://blog.csdn.net/hguisu/article/details/45094079

 


PHP7的一些新特性

1. 運算符(NULL 合並運算符)

把這個放在第一個說是因為我覺得它很有用。用法:

$a = $_GET['a'] ?? 1;

它相當於:

<php
$a = isset($_GET['a']) ? $_GET['a'] : 1;

我們知道三元運算符是可以這樣用的:

$a ?: 1

但是這是建立在 $a 已經定義了的前提上。新增的 ?? 運算符可以簡化判斷。

 

2. 函數返回值類型聲明

官方文檔提供的例子(注意 … 的邊長參數語法在 PHP 5.6 以上的版本中才有):

<php
function arraysSum(array ...$arrays): array
{
return array_map(function(array $array): int {
return array_sum($array);
}, $arrays);
}

print_r(arraysSum([1,2,3], [4,5,6], [7,8,9]));
從這個例子中可以看出現在函數(包括匿名函數)都可以指定返回值的類型。

這種聲明的寫法有些類似於 swift:

func sayHello(personName: String) -> String {
let greeting = "Hello, " + personName + "!"
return greeting
}
這個特性可以幫助我們避免一些 PHP 的隱式類型轉換帶來的問題。在定義一個函數之前就想好預期的結果可以避免一些不必要的錯誤。

不過這里也有一個特點需要注意。PHP 7 增加了一個 declare 指令:strict_types,既使用嚴格模式。

使用返回值類型聲明時,如果沒有聲明為嚴格模式,如果返回值不是預期的類型,PHP 還是會對其進行強制類型轉換。但是如果是嚴格模式, 則會出發一個 TypeError 的 Fatal error。

強制模式:

<php
function foo($a) : int
{
return $a;
}

foo(1.0);
以上代碼可以正常執行,foo 函數返回 int 1,沒有任何錯誤。

嚴格模式:

<php
declare(strict_types=1);

function foo($a) : int
{
return $a;
}

foo(1.0);
# PHP Fatal error: Uncaught TypeError: Return value of foo() must be of the type integer, float returned in test.php:6
在聲明之后,就會觸發致命錯誤。

是不是有點類似與 js 的 strict mode?

 

3. 標量類型聲明

PHP 7 中的函數的形參類型聲明可以是標量了。在 PHP 5 中只能是類名、接口、array 或者 callable (PHP 5.4,即可以是函數,包括匿名函數),現在也可以使用 string、int、float和 bool 了。

官方示例:

<php
// Coercive mode
function sumOfInts(int ...$ints)
{
return array_sum($ints);
}

var_dump(sumOfInts(2, '3', 4.1));
需要注意的是上文提到的嚴格模式的問題在這里同樣適用:強制模式(默認,既強制類型轉換)下還是會對不符合預期的參數進行強制類型轉換,嚴格模式下則觸發 TypeError 的致命錯誤。

 

4. use 批量聲明

PHP 7 中 use 可以在一句話中聲明多個類或函數或 const 了:

<php
use some/namespace/{ClassA, ClassB, ClassC as C};
use function some/namespace/{fn_a, fn_b, fn_c};
use const some/namespace/{ConstA, ConstB, ConstC};
但還是要寫出每個類或函數或 const 的名稱(並沒有像 python 一樣的 from some import * 的方法)。

需要留意的問題是:如果你使用的是基於 composer 和 PSR-4 的框架,這種寫法是否能成功的加載類文件?其實是可以的,composer 注冊的自動加載方法是在類被調用的時候根據類的命名空間去查找位置,這種寫法對其沒有影響。

 

5. 其他的特性

其他的一些特性我就不一一介紹了,有興趣可以查看官方文檔:http://php.net/manual/en/migration70.new-features.php

簡要說幾個:

PHP 5.3 開始有了匿名函數,現在又有了匿名類了;

define 現在可以定義常量數組;

閉包( Closure)增加了一個 call 方法;

生成器(或者叫迭代器更合適)可以有一個最終返回值(return),也可以通過 yield from 的新語法進入一個另外一個生成器中(生成器委托)。

生成器的兩個新特性(return 和 yield from)可以組合。具體的表象大家可以自行測試。PHP 7 現在已經到 RC5 了,最終的版本應該會很快到來。

 

延伸閱讀:
http://www.baidu.com/s?wd=php7%20新特性
http://www.sogou.com/web?query=php7%20新特性
https://www.so.com/s?q=php7新特性

http://developer.51cto.com/art/201510/494674.htm

http://www.tuicool.com/articles/yARJRjQ


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM