1. 程式人生 > >PHP7 的抽象語法樹(AST)帶來的變化

PHP7 的抽象語法樹(AST)帶來的變化

轉載自:http://0x1.im/blog/php/changes-of-php7-because-of-ast.html

本文並不會告訴你抽象語法樹是什麼,這需要你自己去了解,這裡只是描述 AST 給 PHP 帶來的一些變化。

新的執行過程

PHP7 的核心中有一個重要的變化是加入了 AST。在 PHP5中,從 php 指令碼到 opcodes 的執行的過程是:

  1. Lexing:詞法掃描分析,將原始檔轉換成 token 流;
  2. Parsing:語法分析,在此階段生成 op arrays。

PHP7 中在語法分析階段不再直接生成 op arrays,而是先生成 AST,所以過程多了一步:

  1. Lexing:詞法掃描分析,將原始檔轉換成 token 流;
  2. Parsing:語法分析,從 token 流生成抽象語法樹;
  3. 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 的核心中有一個

五分鐘瞭解抽象語法ASTbabel是如何轉換的?

抽象語法樹 什麼是抽象語法樹? 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