TIME
25.5ms |
22.8ms |
-11.8% |
|
MEMORY
##2360kB |
#2482kB |
5.1% |
|
#測試表明,使用AST 之後程式的執行時間整體上大概有10% 到15% 的提升,但是記憶體消耗也有增加,在大檔案單次編譯中增加明顯,但是在整個專案執行過程中並不是很嚴重的問題。
還有註意的是以上的結果都是在沒有 Opcache 的情況下,生產環境中開啟 Opcache 的情況下,記憶體的消耗增加也不是很大的問題。
語意上的改變
如果只是時間上的最佳化,似乎也不是使用 AST 的充足理由。其實實現 AST 並不是基於時間優化上的考慮,而是為了解決語法上的問題。下面來看一下語意上的一些變化。
yield 不需要括號
在PHP5 的實作中,如果在一個表達式上下文(例如在一個賦值表達式的右側)中使用yield,你必須在yield 申明兩邊使用括號:
<?php
$result = yield fn(); // 不合法的
$result = (yield fn()); // 合法的
登入後複製
這種行為僅僅是因為PHP5 的實現方式的限制,在PHP7 中,括號不再是必須的了。所以下面這些寫法也都是合法的:
<?php
$result = yield;
$result = yield $v;
$result = yield $k => $v;
登入後複製
當然了,還要遵循 yield 的應用場景才行。
括號不影響行為
在PHP5 中,($foo)['bar'] = 'baz' 和 $foo['bar'] = 'baz ' 兩個語句的意思不一樣。事實上前一種寫法是不合法的,你會得到下面這樣的錯誤:
<?php
($foo)['bar'] = 'baz';
# PHP Parse error: Syntax error, unexpected '[' on line 1
登入後複製
但是在 PHP7 中,兩個寫法表示同樣的意思。
同樣,如果函數的參數被括號包裹,類型檢查存在問題,在PHP7 中這個問題也得到了解決:
<?php
function func() {
return [];
}
function byRef(array &$a) {
}
byRef((func()));
登入後複製
以上代碼在PHP5 中不會告警,除非使用 byRef (func()) 的方式調用,但是在PHP7 中,不管 func() 兩邊有沒有括號都會產生以下錯誤:
PHP Strict standards: Only variables should be passed by reference ...
登入後複製
list() 的變化
#list 關鍵字的行為改變了很多。 list 給變數賦值的順序(等號左右同時的順序)以前是從右至左,現在是從左到右:
<?php
list($array[], $array[], $array[]) = [1, 2, 3];
var_dump($array);
// PHP5: $array = [3, 2, 1]
// PHP7: $array = [1, 2, 3]
# 注意这里的左右的顺序指的是等号左右同时的顺序,
# list($a, $b) = [1, 2] 这种使用中 $a == 1, $b == 2 是没有疑问的。
登入後複製
產生上面變化的原因正是因為在PHP5 的賦值過程中, 3 會先被填入數組,1 最後,但是現在順序改變了。
同樣的變化還有:
<?php
$a = [1, 2];
list($a, $b) = $a;
// PHP5: $a = 1, $b = 2
// PHP7: $a = 1, $b = null + "Undefined index 1"
登入後複製
這是因為先前的賦值過程中 $b 先得到 2,然後 $a 的值變成 1,但現在 $a 先變成了 1,不再是數組,所以 $b 就成了 null。
list 現在只會存取每個偏移量一次:
<?php
list(list($a, $b)) = $array;
// PHP5:
$b = $array[0][1];
$a = $array[0][0];
// PHP7:
// 会产生一个中间变量,得到 $array[0] 的值
$_tmp = $array[0];
$a = $_tmp[0];
$b = $_tmp[1];
登入後複製
空的list 成員現在是全部禁止的,以前只是在某些情況下:
<?php
list() = $a; // 不合法
list($b, list()) = $a; // 不合法
foreach ($a as list()) // 不合法 (PHP5 中也不合法)
登入後複製
引用賦值的順序
引用賦值的順序在PHP5 中是從右到左的,現在式從左到右:
<?php
$obj = new stdClass;
$obj->a = &$obj->b;
$obj->b = 1;
var_dump($obj);
// PHP5:
object(stdClass)#1 (2) {
["b"] => &int(1)
["a"] => &int(1)
}
// PHP7:
object(stdClass)#1 (2) {
["a"] => &int(1)
["b"] => &int(1)
}
登入後複製
__clone 方法可以直接呼叫
現在可以直接使用 $obj->__clone() 的寫法去呼叫 __clone 方法。 __clone 是之前唯一被禁止直接呼叫的魔術方法,之前你會得到一個這樣的錯誤:
Fatal error: Cannot call __clone() method on objects - use 'clone $obj' instead in ...
登入後複製
變數語法一致性
AST 也解決了一些語法一致性的問題,這些問題是在另一個RFC 中被提出的:https://wiki.php.net/rfc/uniform_variable_syntax.
在新的實作上,以前的一些語法表達的意義和現在有些不同,具體的可以參考下面的表格:
Expression |
PHP5 |
PHP7 |
$$foo['bar']['baz'] |
${$foo['bar']['baz']} |
($$foo)['bar']['baz'] |
$foo->$bar['baz'] |
#$foo->{$bar['baz']} |
($foo->$bar)['baz'] |
$foo->$ bar['baz']() |
$foo->{$bar['baz']}() |
##($ foo->$bar)['baz']()
|
#Foo::$bar['baz']() |
Foo::{$bar['baz']}()
|
#(Foo::$bar)['baz']()
|
整體上還是以前的順序是從右到左,現在從左到右,同時也遵循括號不影響行為的原則。這些複雜的變數寫法是在實際開發中需要注意的。 推薦教學:《PHP7》