深入浅出PHP(Exploring PHP)
一直以來,橫觀國內的PHP現狀,很少有專門介紹PHP內部機制的書。呵呵,我會隨時記錄下研究的心得,有機會的時候,匯總成書。:)
今天這篇,我內心是想打算做為一個導論:
PHP是一個被廣泛應用的腳本語言,因為它的成功,所以很多時候,我們應用PHP的時候是更不不需要考慮底層到底是怎么實現的。我相信大多數的 PHP程序 員是不會去考慮這一點的。從我接觸PHP開始,到今天也就是3年,這三年里,前倆年我一直都是在”用”PHP,每次寫出來一段腳本,我就會想“恩,不用擔 心,PHP解釋器會知道我想做什么的”,直到去年來到雅虎,接受了一個工作,是做一個PHP的Extension,從這個時候開始,我就好奇于新接觸的一 大堆的新鮮事物,zend, TSRM, zval, hashtable, op_array…
于是我到處查閱資料,每次獲得一篇好的文章,或者一段好的文字我就會如獲珍寶,打印保存起來,細細研讀。我發現,國內關于PHP內部的資料真是少的 可憐, 不知道是因為懂得的人多但是不愿意分享,還是懂得的人本來就少,所以,這條路,我走的很辛苦。于是,就會有了這篇文章。
在這篇文章中,我會從整個PHP的執行期入手,大致的介紹下各個階段,詞法分析,語法分析,op code等等,以后的文章我會再詳細介紹每個階(當然,如果你急不可耐的想知道詳細,呵呵,那么可以直接聯系我)。
從最初我們編寫的PHP腳本->到最后腳本被執行->得到執行結果,這個過程,其實可以分為如下幾個階段(鄙視:CSDN不能上圖):
首先,Zend Engine(ZE),調用詞法分析器(Lex生成的,源文件在 Zend/zend_language_sanner.l), 將我們要執行的PHP源文件,去掉空格 ,注釋,分割成一個一個的token。
然后,ZE會將得到的token forward給語法分析器(yacc生成, 源文件在 Zend/zend_language_parser.y),生成一個一個的op code,opcode一般會以op array的形式存在,它是PHP執行的中間語言。
最后,ZE調用zend_executor來執行op array,輸出結果。
ZE是一個虛擬機,正是由于它的存在,所以才能使得我們寫PHP腳本,完全不需要考慮所在的操作系統類型是什么。ZE是一個CISC(復雜指令處理器), 它支持150條指令(具體指令在 Zend/zend_vm_opcodes.h),包括從最簡單的ZEND_ECHO(echo)到復雜的 ZEND_INCLUDE_OR_EVAL(include,require),所有我們編寫的PHP都會最終被處理為這150條指令(op code)的序列,從而最終被執行。
那有什么辦法可以看到我們的PHP腳本,最終被“翻譯”成什么樣的呢? 也就是說,op code張的什么樣子呢? 呵呵,達到這個,我們需要重新編譯PHP,修改它的compile_file和zend_execute函數。不過,在PECL中已經有這樣的模塊,可以 讓我們直接使用了,那就是由 Derick Rethans開發的VLD (Vulcan Logic Dissassembler)模塊。你只要下載這個模塊,并把他載入PHP中,就可以通過簡單的設置,來得到腳本翻譯的結果了。具體關于這個模塊的使用說 明-雅虎一下,你就知道^_^。
接下來,讓我們嘗試用VLD來查看一段簡單的PHP腳本的中間語言。
原始代碼:
<?php $i = “This is a string“; //I am comments echo $i.‘ that has been echoed to screen‘; ?>采用VLD得到的op codes:
filename:/home/Desktop/vldOutOne.php function name: (null) number of ops: 7 line #? op? ? ? ? ? ? ? ?? fetch? ? ?? ext? operands——————————————————————————————————————————-
2 0 FETCH_W local $0, ‘i‘ 1 ASSIGN $0, ‘This+is+a+string‘ 4 2 FETCH_R local $2, ‘i‘ 3 CONCAT ~3, $2,‘+that+has+been+echoed+to+screen‘ 4 ECHO ~3 6 5 RETURN 1 6 ZEND_HANDLE_EXCEPTION我們可以看到,源文件中的注釋,在op code中,已經沒有了,所以不用擔心注釋太多會影響你的腳本執行時間(實際上,它是會影響ZE的詞法處理階段的用時而已)。
現在我們來一條一條的分析這段op codes,每一條op code 又叫做一條op_line,都由如下7個部分,在zend_compile.h中,我們可以看到如下定義:
struct _zend_op { opcode_handler_t handler; znode result; znode op1; znode op2; ulong extended_value; uint lineno; zend_uchar opcode; };其中,opcode字段指明了這操作類型,handler指明了處理器,然后有倆個操作數,和一個操作結果。
- FETCH_W, 是以寫的方式獲取一個變量,此處是獲取變量名”i”的變量于$0(*zval)。
- 將字符串”this+is+a+string”賦值(ASSIGN)給$0
- 字符串連接
- 顯示
可以看出,這個很類似于很多同學大學學習編譯原理時候的三元式,不同的是,這些中間代碼會被Zend VM(Zend虛擬機)直接執行。
真正負責執行的函數是,zend_execute, 查看zend_execute.h:
- ZEND_API extern void (*zend_execute)(zend_op_array *op_array TSRMLS_DC);
可以看出, zend_execute接受zend_op_array*作為參數。
- ?struct _zend_op_array {
- ????/* Common elements */
- ????zend_uchar type;
- ????char *function_name;
- ????zend_class_entry *scope;
- ????zend_uint fn_flags;
- ????union _zend_function *prototype;
- ????zend_uint num_args;
- ????zend_uint required_num_args;
- ????zend_arg_info *arg_info;
- ????zend_bool pass_rest_by_reference;
- ????unsigned char return_reference;
- ????/* END of common elements */
- ?
- ????zend_uint *refcount;
- ?
- ????zend_op *opcodes;
- ????zend_uint last, size;
- ?
- ????zend_compiled_variable *vars;
- ????int last_var, size_var;
- ?
- ????zend_uint T;
- ?
- ????zend_brk_cont_element *brk_cont_array;
- ????zend_uint last_brk_cont;
- ????zend_uint current_brk_cont;
- ?
- ????zend_try_catch_element *try_catch_array;
- ????int last_try_catch;
- ?
- ????/* static variables support */
- ????HashTable *static_variables;
- ?
- ????zend_op *start_op;
- ????int backpatch_count;
- ?
- ????zend_bool done_pass_two;
- ????zend_bool uses_this;
- ?
- ????char *filename;
- ????zend_uint line_start;
- ????zend_uint line_end;
- ????char *doc_comment;
- ????zend_uint doc_comment_len;
- ?
- ????void *reserved[ZEND_MAX_RESERVED_RESOURCES];
- };
可以看到,zend_op_array的結構和zend_function的結構很像(參看我的其他文章), 對于在全局作用域的代碼,就是不包含在任何function內的op_array,它的function_name為NULL。結構中的opcodes保 存了屬于這個op_array的op code數組,zend_execute會從start_op開始,逐條解釋執行傳入的每條op code, 從而實現我們PHP腳本想要的結果。
下一次,我將介紹PHP變量的靈魂 – zval, 你將會看到PHP是如何實現它的變量傳遞,類型戲法,等等。總結
以上是生活随笔為你收集整理的深入浅出PHP(Exploring PHP)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 黄山风景区离哪个站近
- 下一篇: 来亦来去难去是什么歌啊