php是乙個被廣泛應用的指令碼語言,因為它的成功,所以很多時候,我們應用php的時候是不需要考慮底層到底是怎麼實現的,我相信大多數的php程式設計師是不會去考慮這一點的,在這篇文章中,我會從整個php的執行期入手,大致的介紹下各個階段,包括詞法分析、語法分析和opcode。從最初我們編寫的php指令碼->到最後指令碼被執行->得到執行結果,這個過程可以分為如下幾個階段:
1. zend engine(ze)呼叫詞法分析器(lex生成的,原始碼路徑:php/zend/zend_language_sanner.l), 將我們要執行的php原始檔去掉空格和注釋後,分割成乙個個的token;
2. ze會將得到的token forward給語法分析器(yacc生成, 原始碼路徑:php/zend/zend_language_parser.y),將tokens轉換成簡單而有意義的表示式;
3. ze會將轉換後的表示式,編譯為乙個個opcode,opcode一般會以op_array的形式存在,它是php執行的中間語言。
4. ze呼叫zend_executor來執行op_array,輸出結果。
下面是php指令碼執行的整個流程:
ze是乙個虛擬機器,正是由於它的存在,所以才能使得我們寫php指令碼,完全不需要考慮所在的作業系統型別是什麼。ze是乙個cisc(複雜指令處理器),它支援173條指令(原始碼路徑:php/zend/zend_vm_opcodes.h),包括從最簡單的zend_echo(echo)到複雜的 zend_include_or_eval(include,require),所有我們編寫的php都會最終被處理為這173條指令(op code)的序列,從而最終被執行。
上面初步講解了語法分析、詞法分析和php指令碼執行的整個流程,下面讓我看具體看看opcode的結構( 原始碼路徑:php/zend/zend_compile.h):
struct _zend_op ;
該結構有乙個標示指令欄位zend_op.opcode,以及這個opcode所操作的運算元op1、op2,extended_value欄位儲存了指令碼實際執行的時候可能還需要的其他資訊,result欄位儲存該指令執行完成後的結果,handler欄位指明了對應的處理函式,例如如下**是在編譯器遇到print語句的時候進行編譯的函式:
void zend_do_print(znode *result,const znode *arg tsrmls_dc)
當遇到print時,該函式先建立乙個zend_op(整個是opcode的最小片段),然後將zend_op.result型別設定為臨時變數is_tmp_var,並為該臨時變數申請空間,隨後指定zend_op.opcode的值為zend_print(很奇怪,該值在opcode的指令庫中沒有查詢到),並將傳遞進來的引數賦值給zend_op.op1,這樣在最終執行這條opcode的時候,zend引擎能獲取到對應的資訊以便內容輸出。
生成的opcode最終會儲存在op_array陣列中,然後交給zend處理,op_array的結構如下(原始碼路徑:php/zend/zend_compile.h):
struct _zend_op_array ;
zend_op_array.opcodes儲存了屬於這個op_array的opcode陣列,zend會根據將zend_op_array作為入參,通過下面的執行函式execute,逐條解釋執行每條opcode, 從而實現我們php指令碼想要的結果。
zend_api void execute(zend_op_array *op_array tsrmls_dc)
總結一下,php指令碼經過語法分析和詞法分析,生產一條條op_line,zend將每條op_line編譯成opcode,就產生大量opcode,這些opcode會通過一定形式儲存在陣列zend_op_array中, 最後以該陣列為入參,通過zend提供的方法execute(zend_op_array *op_array tsrmls_dc)得到我們想要的結果。
參考2:
深入理解PHP之require include順序
在大型的web專案中,include path是乙個模組化設計的根本中的根本 當然,現在也有很多基於autoload的設計,這個不影響本文的 但是正是因為include path,經常會讓我們遇到一些因為沒有找到正確的檔案而導致的看似 詭異 的問題.也就有了如下的疑問 include path是怎麼...
深入理解PHP原理之 echo的實現
php源 分析 echo實現詳解 原諒出處 echo,這個是php運用得最多的標記之一,算不上是函式,php手冊裡這麼寫的,因為它沒有返回值。今天好奇就去看看php的源 因為echo不是一般的函式,所以找起來比較費勁,一般的函式只要搜尋php function fun name 基本就能找著函式的實...
深入理解PHP原理之靜態變數
通常意義上靜態變數是靜態分配的,他們的生命週期和程式的生命週期一樣,只有在程式退出時才結束期生命週期,這和區域性變數相反。靜態變數的型別可以分為靜態全域性變數 靜態區域性變 靜態成員變數,最常見的是靜態區域性變數及靜態成員變數,先看看如下區域性變數的使用 function t t t t 上述的程式...