## 概述
> PHP7將在2015年10月正式發布,PHP7 ,將會是PHP腳本語言的重大版本更新,同時將帶來大幅的性能改進和新的特性,以及改進一些過時功能。 該 發布版本將會專注在性能加強,源自PHP版本樹中的phpng分支。在硅谷公司的ZendCon會議,PHP工具廠商Zend技術官方討論phpng和 PHP7的進度。“(本次升級)真正專注于幫助業界的應用程序顯著加強執行速度,再加上,我們在PHP中的其他改進,”Zend的首席執行官安迪特曼斯 (曾參與了PHP語言的持續開發和發展)表示。
## 新特性
### PHP7引擎( What will be in PHP 7 / PHPNG )
- Performance Improvements with the addition of PHPNG engine.(使用PHPNG引擎來提升性能)
- PHPNG不再使用zval的二級指針。大多數出現的zval**變量和參數都將改變成zval*。相應的,使用在這些變量上的宏Z_*_PP()也需要變成Z_*_P()。
- More -> [https://wiki.php.net/phpng-upgrading](https://wiki.php.net/phpng-upgrading)
- JIT - Just in Time compiler
- 即時編譯 ([參考百度百科介紹](http://baike.baidu.com/link?url=sap20L79nr0CnNELWfluVvtP5v3-U3RBbDD-Q3RDKHhGZwQtSHJhAzmk5YPLyVfJPoFG7O-e2KutCXEZN2VuLK))
- Just In Time(即時編譯)是一種軟件優化技術,指在運行時才會去編譯字節碼為機器碼。從直覺出發,我們都很容易認為,機器碼是計算機能夠直接識別和執行的,比起Zend讀取opcode逐條執行效率會更高。其中,HHVM(HipHop Virtual Machine,HHVM是一個Facebook開源的PHP虛擬機)就采用JIT,讓他們的PHP性能測試提升了一個數量級,放出一個令人震驚的測試結果,也讓我們直觀地認為JIT是一項點石成金的強大技術。
- 通過JIT,可以降低VM的開銷,同時,通過指令優化,可以間接降低內存管理的開發,因為可以減少內存分配的次數。然而,對于真實的WordPress項目來說,CPU耗時只有25%在VM上,主要的問題和瓶頸實際上并不在VM上。因此,JIT的優化計劃,最后沒有被列入該版本的PHP7特性中。不過,它很可能會在更后面的版本中實現,這點也非常值得我們期待哈。
- Abstract Syntax Tree for compilation(抽象語法樹編譯)
- Asynchronous refactoring of the I/O layer. 對I/O層的異步重構
- Multi-threaded build in Web Server多線程構建Web服務器
- Expanded use of ->, [], (), {}, and :: operators 擴展使用 ->, [], (), {}, 和 :: 符號
- 100% increase in performance性能提升 100% (應該是QPS)
- Cool Name: PHPNG 酷名:PHPNG引擎
### Zval的改變
> PHP的各種類型的變量,其實,真正存儲的載體就是Zval,它特點是海納百川,有容乃大。從本質上看,它是C語言實現的一個結構體(struct)。對于寫PHP的同學,可以將它粗略理解為是一個類似array數組的東西。
PHP5的Zval,內存占據24個字節,PHP7的Zval,內存占據16個字節。
Zval從24個字節下降到16個字節,為什么會下降呢,這里需要補一點點的C語言基礎,輔助不熟悉C的同學理解。struct和union(聯合體)有點不同,Struct的每一個成員變量要各自占據一塊獨立的內存空間,而union里的成員變量是共用一塊內存空間(也就是說修改其中一個成員變量,公有空間就被修改了,其他成員變量的記錄也就沒有了)。因此,雖然成員變量看起來多了不少,但是實際占據的內存空間卻下降了。
> 不需要引用的類型:NULL、Boolean、Long、Double
需要引用的類型:String、Array、Object、Resource、Reference
### 內部類型zend_string
Zend_string是實際存儲字符串的結構體,實際的內容會存儲在val(char,字符型)中,而val是一個char數組,長度為1(方便成員變量占位)。
### PHP數組的變化(HashTable和Zend Array)
在編寫PHP程序過程中,使用最頻繁的類型莫過于數組,PHP5的數組采用HashTable實現。如果用比較粗略的概括方式來說,它算是一個支持雙向鏈表的HashTable,不僅支持通過數組的key來做hash映射訪問元素,也能通過foreach以訪問雙向鏈表的方式遍歷數組元素。
當我們通過key值訪問一個元素內容的時候,有時需要3次的指針跳躍才能找對需要的內容。而最重要的一點,就在于這些數組元素存儲,都是分散在各個不同的內存區域的。同理可得,在CPU讀取的時候,因為它們就很可能不在同一級緩存中,會導致CPU不得不到下級緩存甚至內存區域查找,也就是引起CPU緩存命中下降,進而增加更多的耗時。
新版本的數組結構,非常簡潔,讓人眼前一亮。最大的特點是,整塊的數組元素和hash映射表全部連接在一起,被分配在同一塊內存內。如果是遍歷一個整型的簡單類型數組,效率會非常快,因為,數組元素(Bucket)本身是連續分配在同一塊內存里,并且,數組元素的zval會把整型元素存儲在內部,也不再有指針外鏈,全部數據都存儲在當前內存區域內。當然,最重要的是,它能夠避免CPU Cache Miss(CPU緩存命中率下降)。
### Zend Array的變化:
(1) 數組的value默認為zval。
(2) HashTable的大小從72下降到56字節,減少22%。
(3) Buckets的大小從72下降到32字節,減少50%。
(4) 數組元素的Buckets的內存空間是一同分配的。
(5) 數組元素的key(Bucket.key)指向zend_string。
(6) 數組元素的value被嵌入到Bucket中。
(7) 降低CPU Cache Miss。
### 函數調用機制(Function Calling Convention)
PHP7改進了函數的調用機制,通過優化參數傳遞的環節,減少了一些指令,提高執行效率。
### 通過宏定義和內聯函數(inline),讓編譯器提前完成部分工作
PHP7在這方面做了不少的優化,將不少需要在運行階段要執行的工作,放到了編譯階段。例如參數類型的判斷(Parameters Parsing),因為這里涉及的都是固定的字符常量,因此,可以放到到編譯階段來完成,進而提升后續的執行效率。
### AST(Abstract Syntax Tree,抽象語法樹)
> AST在PHP編譯過程作為一個中間件的角色,替換原來直接從解釋器吐出opcode的方式,
讓解釋器(parser)和編譯器(compliler)解耦,可以減少一些Hack代碼,同時,讓實現更容易理解和可維護。
more -> [https://wiki.php.net/rfc/abstract_syntax_tree](https://wiki.php.net/rfc/abstract_syntax_tree)

### TLS(Native Thread local storage,原生線程本地存儲)
PHP在多線程模式下(例如,Web服務器Apache的woker和event模式,就是多線程),需要解決“線程安全”(TS,Thread Safe)的問題,因為線程是共享進程的內存空間的,所以每個線程本身需要通過某種方式,構建私有的空間來保存自己的私有數據,避免和其他線程相互污染。而PHP5采用的方式,就是維護一個全局大數組,為每一個線程分配一份獨立的存儲空間,線程通過各自擁有的key值來訪問這個全局數據組。
而這個獨有的key值在PHP5中需要傳遞給每一個需要用到全局變量的函數,PHP7認為這種傳遞的方式并不友好,并且存在一些問題。因而,嘗試采用一個全局的線程特定變量來保存這個key值。
### Combined comparison Operator (<=>) 結合比較運算符 (<=>)
~~~
// PHP 7之前的寫法:比較兩個數的大小
function order_func($a, $b) {
return ($a < $b) ? -1 : (($a > $b) ? 1 : 0);
}
// PHP新增的操作符 <=>,perfect
function order_func($a, $b) {
return $a <=> $b;
}
~~~
### Return Type Declarations 返回類型聲明 和Scalar Type Declarations 標量類型聲明
PHP語言一個非常重要的特點就是“弱類型”,它讓PHP的程序變得非常容易編寫,新手接觸PHP能夠快速上手,不過,它也伴隨著一些爭議。支持變量類型的定義,可以說是革新性質的變化,PHP開始以可選的方式支持類型定義。除此之外,還引入了一個開關指令declare(strict_type=1);,當這個指令一旦開啟,將會強制當前文件下的程序遵循嚴格的函數傳參類型和返回類型。
~~~
declare(strict_type=1);
function add(int $a, int $b): int{
return $a + $b
}
~~~
如果不開啟strict_type,PHP將會嘗試幫你轉換成要求的類型,而開啟之后,會改變PHP就不再做類型轉換,類型不匹配就會拋出錯誤。對于喜歡“強類型”語言的同學來說,這是一大福音。
更為詳細的介紹:
[https://wiki.php.net/rfc/scalar_type_hints_v5](https://wiki.php.net/rfc/scalar_type_hints_v5)
PHP7標量類型聲明RFC
## 新特性語法
### 標量類型聲明
> 標量類型聲明 有兩種模式: 強制 (默認) 和 嚴格模式。 現在可以使用下列類型參數(無論用強制模式還是嚴格模式): 字符串(string), 整數 (int), 浮點數 (float), 以及布爾值 (bool)。它們擴充了PHP5中引入的其他類型:類名,接口,數組和 回調類型。
~~~
<?php
// Coercive mode
function sumOfInts(int ...$ints)
{
return array_sum($ints);
}
var_dump(sumOfInts(2, '3', 4.1));
~~~
輸出:int(9)
要使用嚴格模式,一個 declare 聲明指令必須放在文件的頂部。這意味著嚴格聲明標量是基于文件可配的。 這個指令不僅影響參數的類型聲明,也影響到函數的返回值聲明(參見 返回值類型聲明, 內置的PHP函數以及擴展中加載的PHP函數)
**完整的標量類型聲明文檔和示例參見**
[http://php.net/manual/zh/functions.arguments.php#functions.arguments.type-declaration](http://php.net/manual/zh/functions.arguments.php#functions.arguments.type-declaration)
### 返回值類型聲明
> PHP 7 增加了對返回類型聲明的支持。 類似于參數類型聲明,返回類型聲明指明了函數返回值的類型。可用的類型與參數聲明中可用的類型相同。
~~~
<?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]));
~~~
輸出:
> Array
(
[0] => 6
[1] => 15
[2] => 24
)
### null合并運算符
> 由于日常使用中存在大量同時使用三元表達式和 isset()的情況, 我們添加了null合并運算符 (??) 這個語法糖。如果變量存在且值不為NULL, 它就會返回自身的值,否則返回它的第二個操作數。
~~~
<?php
// Fetches the value of $_GET['user'] and returns 'nobody'
// if it does not exist.
$username = $_GET['user'] ?? 'nobody';
// This is equivalent to:
$username = isset($_GET['user']) ? $_GET['user'] : 'nobody';
// Coalesces can be chained: this will return the first
// defined value out of $_GET['user'], $_POST['user'], and
// 'nobody'.
$username = $_GET['user'] ?? $_POST['user'] ?? 'nobody';
?>
~~~
### Spaceship operator 結合比較運算符 (<=>)
~~~
<?php
// Integers
echo 1 <=> 1; // 0
echo 1 <=> 2; // -1
echo 2 <=> 1; // 1
// Floats
echo 1.5 <=> 1.5; // 0
echo 1.5 <=> 2.5; // -1
echo 2.5 <=> 1.5; // 1
// Strings
echo "a" <=> "a"; // 0
echo "a" <=> "b"; // -1
echo "b" <=> "a"; // 1
?>
~~~
### Constant arrays using define() 的常量數組
> 現在可以用 define() 定義數組常量。在 PHP 5.6,只可以被定義與 const。
~~~
<?php
define('ANIMALS', [
'dog',
'cat',
'bird'
]);
echo ANIMALS[1]; // outputs "cat"
?>
~~~
### Anonymous classes 匿名類
> 通過新的類添加了用于匿名類的支持。這些可以用于代替完整的類定義一次性的對象
~~~
<?php
interface Logger {
public function log(string $msg);
}
class Application {
private $logger;
public function getLogger(): Logger {
return $this->logger;
}
public function setLogger(Logger $logger) {
$this->logger = $logger;
}
}
$app = new Application;
$app->setLogger(new class implements Logger {
public function log(string $msg) {
echo $msg;
}
});
var_dump($app->getLogger());
?>
~~~
輸出:
> object(class@anonymous)#2 (0) {
}
### Unicode codepoint escape syntax Unicode編碼轉移語法
> 這 Unicode 編碼以十六進制格式,并輸出該編碼在 UTF-8 為雙引號括起來的字符串或定界符。任何有效的編碼被接受。
~~~
echo "\u{aa}";
echo "\u{0000aa}";
echo "\u{9999}";
~~~
### Closure::call()
~~~
<?php
class A {private $x = 1;}
// Pre PHP 7 code
$getXCB = function() {return $this->x;};
$getX = $getXCB->bindTo(new A, 'A'); // intermediate closure
echo $getX();
// PHP 7+ code
$getX = function() {return $this->x;};
echo $getX->call(new A);
~~~
待續…
更多參考:[http://php.net/manual/zh/migration70.new-features.php](http://php.net/manual/zh/migration70.new-features.php)
- 前言
- PHP生成對象之設計模式—單例模式
- PHP生成對象之設計模式—工廠方法模式
- PHP之設計模式—適配器模式
- PHP之設計模式—建造者模式(通過選擇mysql,mongo數據庫鏈接類型做說明)
- PHP之設計模式—委托模式
- PHP面向對象學習一:對象基礎實踐
- PHP面向對象學習之二:深入了解面向對象高級特性
- PHP面向對象學習之三:抽象類和接口類的實際作用
- PHP解決問題進化論(整理筆記)
- PHP7新特性整理介紹篇
- php-fpm 與 Nginx優化總結
- Centos+Nginx+PHP7.0編譯安裝(和PHP5.6老版本共存)
- PHP7:Mongodb API使用
- PHP之include/require深入了解
- PHP內核了解:生命周期及運行模式