PHP7 的抽象語法樹(AST)帶來的變化
轉載自:http://0x1.im/blog/php/changes-of-php7-because-of-ast.html
本文並不會告訴你抽象語法樹是什麼,這需要你自己去了解,這裡只是描述 AST 給 PHP 帶來的一些變化。
新的執行過程
PHP7 的核心中有一個重要的變化是加入了 AST。在 PHP5中,從 php 指令碼到 opcodes 的執行的過程是:
- Lexing:詞法掃描分析,將原始檔轉換成 token 流;
- Parsing:語法分析,在此階段生成 op arrays。
PHP7 中在語法分析階段不再直接生成 op arrays,而是先生成 AST,所以過程多了一步:
- Lexing:詞法掃描分析,將原始檔轉換成 token 流;
- Parsing:語法分析,從 token 流生成抽象語法樹;
- Compilation:從抽象語法樹生成 op arrays。
執行時間和記憶體消耗
從以上的步驟來看,這比之前的過程還多了一步,所以按常理來說這反而會增加程式的執行時間和記憶體的使用。但事實上記憶體的使用確實增加了,但是執行時間上卻有所降低。
每個檔案編譯 100 次的執行時間(注意文章的測試結果時間是 14 年,PHP7 還叫 PHP-NG 的時候):
php-ng | php-ast | diff | |
---|---|---|---|
SMALL | 0.180s | 0.160s | -12.5% |
MEDIUM | 1.492s | 1.268s | -17.7% |
LARGE | 6.703s | 5.736s | -16.9% |
單次編譯中的記憶體峰值:
php-ng | php-ast | diff | |
---|---|---|---|
SMALL | 378kB | 414kB | +9.5% |
MEDIUM | 507kB | 643kB | +26.8% |
LARGE | 1084kB | 1857kB | +71.3% |
單次編譯的測試結果可能並不能代表實際使用的情況,以下是使用 PhpParser 進行完整專案測試得到的結果:
php-ng | php-ast | diff | |
---|---|---|---|
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 ...
變數語法一致性
在新的實現上,以前的一些語法表達的含義和現在有些不同,具體的可以參照下面的表格:
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 的抽象語法樹(AST)帶來的變化
轉載自:http://0x1.im/blog/php/changes-of-php7-because-of-ast.html 本文並不會告訴你抽象語法樹是什麼,這需要你自己去了解,這裡只是描述 AST 給 PHP 帶來的一些變化。 新的執行過程 PHP7 的核心中有一個
五分鐘瞭解抽象語法樹(AST)babel是如何轉換的?
抽象語法樹 什麼是抽象語法樹? It is a hierarchical program representation that presents source code structure according to the grammar of a programming language, each A
自制指令碼語言(10) 抽象語法樹AST與三地址線性IR
摘要:介紹了YF language程式語言的AST及IR表示 根據前面定義的語法,基本上AST就已經被決定了。因為語法每reduce一次,要麼組建一個新AST,要麼在已有的AST上新增資料。這裡照慣例採用了訪問者模式,將Code Generator設為訪問者,遍歷
從零寫一個編譯器(九):語義分析之構造抽象語法樹(AST)
專案的完整程式碼在 C2j-Compiler 前言 在上一篇完成了符號表的構建,下一步就是輸出抽象語法樹(Abstract Syntax Tree,AST) 抽象語法樹(abstract syntax tree 或者縮寫為 AST),是原始碼的抽象語法結構的樹狀表現形式,這裡特指程式語言的原始碼。樹上的
正則表示式引擎的構建——基於編譯原理DFA(龍書第三章)——2 構造抽象語法樹
簡要介紹 構造抽象語法樹是構造基於DFA的正則表示式引擎的第一步。目前在我實現的這個正則表示式的雛形中,正則表示式的運算子有3種,表示選擇的|運算子,表示星號運算的*運算子,表示連線的運算子cat(在實際正則表示式中被省去)。 例如對於正則表示式a*b|c,在a*
PHP7語法知識(二):流程控制語句
bre each循環 if條件 HP Go 條件控制語句 語句 跳轉 AC 一、條件控制語句 1、if條件控制語句; 2、switch分支語句 二、循環控制語句 1、while循環; 2、do while循環; 3、for循環; 4、foreach循環; 三、跳轉語句 1、
AST 抽象語法樹學習
閱讀原文 Abstract Syntax Tree 抽象語法樹簡介 在使用前端許多工具外掛的時候,我們大多知道每個工具庫、每個外掛能做什麼,不過很多同學其實並不清楚背後用到的技術,如webpack、rollup、UglifyJS、Lint等很多的工具和庫
C 語言 抽象語法樹AST
抽象語法樹簡介 (一)簡介 抽象語法樹(abstract syntax code,AST)是原始碼的抽象語法結構的樹狀表示,樹上的每個節點都表示原始碼中的一種結構,這所以說是抽象的,是因為抽象語法樹並不會表示出真實語法出現的每一個細節,比如說,巢狀括號被隱含在樹的
二叉搜索樹(模板)
int ret class get name cnblogs clu space tin #include<cstdio> using namespace std; const int M=9999; struct tr{ int l,r,x,size,nu
C++__二叉樹(練習)
efi fine main enqueue and class con sem pre 二叉樹 文件結構:二叉樹→TREE→TREE.h、TREE.cpp →QUEUE→QUEUE.h、QUEUE.cpp →main.cpp queue
如何打印一棵樹(Java)
.get stat color util emp println style ldl 多叉樹 有一棵多叉樹,將它打印出來。 import java.util.LinkedList; /** * 需求:按層打印一棵樹 * 說明:樹是保存在一個鏈表中 *
Kotlin VS Java:基本語法差異(轉載)
允許 接收 point this view 學習 替換 同時 ons 5月18號,goole宣布Kotlin成為官方支持的開發語言以來,Kotlin語言社區,公眾號,qq群等全面轟炸,本文是一篇譯文,來自國外的一個用戶,將給大家介紹,基礎語法部分Kotlin和java之間的
監督式學習 -- 分類決策樹(一)
cte 求解 分支 基本概念 tracking 它的 解決 mat 這就是 決策樹(decision tree)是一種基本的分類與回歸方法。其表示的樹型結構,能夠覺得是if-else規則的集合。基本的長處是分類可讀性好,速度快。一般會有三個步驟:特征選擇、決策樹的生成
線段樹(二)
ref class 搜索 turn 們的 highlight print log max-width 轉自:http://blog.csdn.net/liujian20150808/article/details/51137749 1.線段樹的定義: 線段樹是一種二叉搜
數據結構之二叉樹(二)
創建 int iter out for 結點 spa left nbsp 輸出二叉樹中所有從根結點到葉子結點的路徑 1 #include <iostream> 2 #include <vector> 3 us
數據結構之二叉樹(一)
reorder system style 序列 urn creat 編寫程序 space ont 設計和編寫程序,按照輸入的遍歷要求(即先序、中序和後序)完成對二叉樹的遍歷,並輸出相應遍歷條件下的樹結點序列。 1 //遞歸實現 2 #include
js基本語法總結(一)
向上取整 取余 轉字符串 結果 lin ase 調試 錯誤 進行 1.js簡介 a)js是一種網頁腳本語言,使得瀏覽器可以與網頁互動。 js的一種基於對象和事件驅動,具有安全性能的腳本語言,腳本語言就是在客戶端的瀏覽器就可以互動響應處理程序的語言,而不需要服務器的處理和響應
【經典數據結構】B樹與B+樹(轉)
linux 每分鐘 www 數據 csapp png 感知 轉動 繼續 本文轉載自:http://www.cnblogs.com/yangecnu/p/Introduce-B-Tree-and-B-Plus-Tree.html 維基百科對B樹的定義為“在計算機科學中,B
二叉樹(11)----求二叉樹的鏡像,遞歸和非遞歸方式
temp right 二叉樹 -a data nbsp rac art urn 1、二叉樹定義: typedef struct BTreeNodeElement_t_ { void *data; } BTreeNodeElement_t; type
算法總結——主席樹(poj2104)
() key orm 關於 data 分享 ogr d+ put 題目: Description You are working for Macrohard company in data structures department. After failing your