經過前面的學習,咱們瞭解到一個PHP文件在服務器端的執行過程包括如下兩個大的過程: php
在第二步中,詞法分析、語法分析,編譯中間代碼,執行中間代碼等各個部分統稱爲Zend虛擬機。 與Java、C#等編譯型語言相比,PHP少了一個手動編譯的過程,它們無需編譯便可運行,咱們稱其爲解釋性語言。 Java有本身的Java虛擬機,它在多個平臺上實現統一語言; C#有本身的.NET虛擬機,它在單一平臺實現多種語言; PHP跟他們同樣,也有屬於本身的Zend虛擬機。它們在本質是相同的,它們都是抽象的計算機。 這些虛擬機都是在某種較底層的語言上抽象出另一種語言,有本身的指令集,有本身的內存管理體系。 它們最終都會將抽象級別較高的語言實現轉化爲抽象級別較低的語言實現, 而且實現其它輔助功能,如內存管理,垃圾回收等機制, 以減小程序員在具體實現上的工做,從而能夠將更多的時間和精力投入到業務邏輯中。 從抽象層次看,Zend虛擬機比Java等語言更高級一些,這裏的高級不是說功能更強大或效率更高, 簡單點說,Zend虛擬機離真正的機器實現更遠一些。 最近這些年,語言的發展只是不斷的抽象,不斷的遠離機器,沒有根本性的變化。 程序員
這裏咱們從虛擬機的前世此生講起,敘述Zend虛擬機的實現原理,關鍵的數據結構, 並其中穿插一個關於語法實現的示例和源碼加密解密的過程說明。 算法
在wiki中虛擬機的定義是: 虛擬機(Virtual Machine),在計算機科學中的體系結構裏,是指一種特殊的軟件, 他能夠在計算機平臺和終端用戶之間建立一種環境,而終端用戶則是基於這個軟件所建立的環境來操做軟件。 在計算機科學中,虛擬機是指能夠像真實機器同樣運行程序的計算機的軟件實現。 數組
虛擬機是一種抽象的計算機,它有本身的指令集,有本身的內存管理體系。 在此類虛擬機上實現的語言比較低抽象層次的語言更加明瞭,更加簡單易學。 緩存
PHP文件是如何被解析的,生成的中間代碼表示什麼,生成的中間代碼與實際的PHP代碼是如何對應的,生成的中間代碼如何被執行的? 在執行過程當中會將會哪些中間的數據?整個虛擬機是否能夠優化?如何優化? 服務器
從概念層將Zend虛擬機的實現進行抽象,咱們能夠將Zend虛擬機的體系結構分爲:解釋層、執行引擎、中間數據層。 數據結構
Zend虛擬機體系結構圖 閉包
當一段PHP代碼進入Zend虛擬機,它會被執行兩步操做:編譯和執行。 對於一個解釋性語言來講,這是一個創造性的舉動,可是,如今的實現並不完全。 如今當PHP代碼進入Zend虛擬機後,它雖然會被執行這兩步操做,可是這兩步操做對於一個常規的執行過程來講倒是連續的, 也就是說它並無轉變成和Java這種編譯型語言同樣:生成一箇中間文件存放編譯後的結果。 若是每次執行這樣的操做,對於PHP腳本的性能來講是一個極大的損失。 雖然有相似於APC,eAccelerator等緩存解決方案。可是其本質上是沒有變化的,而且不能將兩個步驟分離,各自發展壯大。 函數
解釋層是Zend虛擬機執行編譯過程的位置。它包括詞法解析、語法解析和編譯生成中間代碼三個部分。 詞法分析就是將咱們要執行的PHP源文件,去掉空格,去掉註釋,切分爲一個個的標記(token), 而且處理程序的層級結構(hierarchical structure)。 源碼分析
語法分析就是將接受的標記(token)序列,根據定義的語法規則,來執行一些動做,Zend虛擬機如今使用的Bison使用巴科斯範式(BNF) 來描述語法。 編譯生成中間代碼是根據語法解析的結果對照Zend虛擬機制定的opcode生成中間代碼, 在PHP5.3.1中,Zend虛擬機支持135條指令(見Zend/zend_vm_opcodes.h文件), 不管是簡單的輸出語句仍是程序複雜的遞歸調用,Zend虛擬機最終都會將全部咱們編寫的PHP代碼轉化成這135條指令的序列, 以後在執行引擎中按順序執行。
當Zend虛擬機執行一個PHP代碼時,它須要內存來存儲許多東西, 好比,中間代碼,PHP自帶的函數列表,用戶定義的函數列表,PHP自帶的類,用戶自定義的類, 常量,程序建立的對象,傳遞給函數或方法的參數,返回值,局部變量以及一些運算的中間結果等。 咱們把這些全部的存放數據的地方稱爲中間數據層。
若是PHP以mod擴展的方式依附於Apache2服務器運行,中間數據層的部分數據可能會被多個線程共享,若是PHP自帶的函數列表等。 若是隻考慮單個進程的方式,當一個進程被建立時它就會被加載PHP自帶的各類函數列表,類列表,常量列表等。 當解釋層將PHP代碼編譯完成後,各類用戶自定義的函數,類或常量會添加到以前的列表中, 只是這些函數在其自身的結構中某些字段的賦值是不同的。
當執行引擎執行生成的中間代碼時,會在Zend虛擬機的棧中添加一個新的執行中間數據結構(zend_execute_data), 它包括當前執行過程的活動符號列表的快照、一些局部變量等。
Zend虛擬機的執行引擎是一個很是簡單的實現,它只是依據中間代碼序列(EX(opline)),一步一步調用對應的方法執行。 在執行引擎中沒並有相似於PC寄存器同樣的變量存放下一條指令,當Zend虛擬機執行到某條指令時,當它全部的任務都執行完了, 這條指令會本身調用下一條指令,即將序列的指針向前移動一個位置,從而執行下一條指令,而且在最後執行return語句,如此反覆。 這在本質上是一個函數嵌套調用。
回到開頭的問題,PHP經過詞法分析、語法分析和中間代碼生成三個步驟後,PHP文件就會被解析成PHP的中間代碼opcode。 生成的中間代碼與實際的PHP代碼之間並無徹底的一一對應關係。只是針對用戶所給的PHP代碼和PHP的語法規則和一些內部約定生成中間代碼, 而且這些中間代碼還須要依靠一些全局變量中轉數據和關聯。至於生成的中間代碼的執行過程是依據中間代碼的順利, 依賴於執行過程當中的全局變量,一步步執行。固然,在遇到一些函數跳轉也會發生偏移,可是最終仍是會回到偏移點。
此文章所在專題列表以下: