深入浅出PHP(Exploring PHP)
一直以來(lái),橫觀國(guó)內(nèi)的PHP現(xiàn)狀,很少有專(zhuān)門(mén)介紹PHP內(nèi)部機(jī)制的書(shū)。呵呵,我會(huì)隨時(shí)記錄下研究的心得,有機(jī)會(huì)的時(shí)候,匯總成書(shū)。:)
今天這篇,我內(nèi)心是想打算做為一個(gè)導(dǎo)論:
PHP是一個(gè)被廣泛應(yīng)用的腳本語(yǔ)言,因?yàn)樗某晒?#xff0c;所以很多時(shí)候,我們應(yīng)用PHP的時(shí)候是更不不需要考慮底層到底是怎么實(shí)現(xiàn)的。我相信大多數(shù)的 PHP程序 員是不會(huì)去考慮這一點(diǎn)的。從我接觸PHP開(kāi)始,到今天也就是3年,這三年里,前倆年我一直都是在”用”P(pán)HP,每次寫(xiě)出來(lái)一段腳本,我就會(huì)想“恩,不用擔(dān) 心,PHP解釋器會(huì)知道我想做什么的”,直到去年來(lái)到雅虎,接受了一個(gè)工作,是做一個(gè)PHP的Extension,從這個(gè)時(shí)候開(kāi)始,我就好奇于新接觸的一 大堆的新鮮事物,zend, TSRM, zval, hashtable, op_array…
于是我到處查閱資料,每次獲得一篇好的文章,或者一段好的文字我就會(huì)如獲珍寶,打印保存起來(lái),細(xì)細(xì)研讀。我發(fā)現(xiàn),國(guó)內(nèi)關(guān)于PHP內(nèi)部的資料真是少的 可憐, 不知道是因?yàn)槎玫娜硕嗟遣辉敢夥窒?#xff0c;還是懂得的人本來(lái)就少,所以,這條路,我走的很辛苦。于是,就會(huì)有了這篇文章。
在這篇文章中,我會(huì)從整個(gè)PHP的執(zhí)行期入手,大致的介紹下各個(gè)階段,詞法分析,語(yǔ)法分析,op code等等,以后的文章我會(huì)再詳細(xì)介紹每個(gè)階(當(dāng)然,如果你急不可耐的想知道詳細(xì),呵呵,那么可以直接聯(lián)系我)。
從最初我們編寫(xiě)的PHP腳本->到最后腳本被執(zhí)行->得到執(zhí)行結(jié)果,這個(gè)過(guò)程,其實(shí)可以分為如下幾個(gè)階段(鄙視:CSDN不能上圖):
首先,Zend Engine(ZE),調(diào)用詞法分析器(Lex生成的,源文件在 Zend/zend_language_sanner.l), 將我們要執(zhí)行的PHP源文件,去掉空格 ,注釋,分割成一個(gè)一個(gè)的token。
然后,ZE會(huì)將得到的token forward給語(yǔ)法分析器(yacc生成, 源文件在 Zend/zend_language_parser.y),生成一個(gè)一個(gè)的op code,opcode一般會(huì)以op array的形式存在,它是PHP執(zhí)行的中間語(yǔ)言。
最后,ZE調(diào)用zend_executor來(lái)執(zhí)行op array,輸出結(jié)果。
ZE是一個(gè)虛擬機(jī),正是由于它的存在,所以才能使得我們寫(xiě)PHP腳本,完全不需要考慮所在的操作系統(tǒng)類(lèi)型是什么。ZE是一個(gè)CISC(復(fù)雜指令處理器), 它支持150條指令(具體指令在 Zend/zend_vm_opcodes.h),包括從最簡(jiǎn)單的ZEND_ECHO(echo)到復(fù)雜的 ZEND_INCLUDE_OR_EVAL(include,require),所有我們編寫(xiě)的PHP都會(huì)最終被處理為這150條指令(op code)的序列,從而最終被執(zhí)行。
那有什么辦法可以看到我們的PHP腳本,最終被“翻譯”成什么樣的呢? 也就是說(shuō),op code張的什么樣子呢? 呵呵,達(dá)到這個(gè),我們需要重新編譯PHP,修改它的compile_file和zend_execute函數(shù)。不過(guò),在PECL中已經(jīng)有這樣的模塊,可以 讓我們直接使用了,那就是由 Derick Rethans開(kāi)發(fā)的VLD (Vulcan Logic Dissassembler)模塊。你只要下載這個(gè)模塊,并把他載入PHP中,就可以通過(guò)簡(jiǎn)單的設(shè)置,來(lái)得到腳本翻譯的結(jié)果了。具體關(guān)于這個(gè)模塊的使用說(shuō) 明-雅虎一下,你就知道^_^。
接下來(lái),讓我們嘗試用VLD來(lái)查看一段簡(jiǎn)單的PHP腳本的中間語(yǔ)言。
原始代碼:
<?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中,已經(jīng)沒(méi)有了,所以不用擔(dān)心注釋太多會(huì)影響你的腳本執(zhí)行時(shí)間(實(shí)際上,它是會(huì)影響ZE的詞法處理階段的用時(shí)而已)。
現(xiàn)在我們來(lái)一條一條的分析這段op codes,每一條op code 又叫做一條op_line,都由如下7個(gè)部分,在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字段指明了這操作類(lèi)型,handler指明了處理器,然后有倆個(gè)操作數(shù),和一個(gè)操作結(jié)果。
- FETCH_W, 是以寫(xiě)的方式獲取一個(gè)變量,此處是獲取變量名”i”的變量于$0(*zval)。
- 將字符串”this+is+a+string”賦值(ASSIGN)給$0
- 字符串連接
- 顯示
可以看出,這個(gè)很類(lèi)似于很多同學(xué)大學(xué)學(xué)習(xí)編譯原理時(shí)候的三元式,不同的是,這些中間代碼會(huì)被Zend VM(Zend虛擬機(jī))直接執(zhí)行。
真正負(fù)責(zé)執(zhí)行的函數(shù)是,zend_execute, 查看zend_execute.h:
- ZEND_API extern void (*zend_execute)(zend_op_array *op_array TSRMLS_DC);
可以看出, zend_execute接受zend_op_array*作為參數(shù)。
- ?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的結(jié)構(gòu)和zend_function的結(jié)構(gòu)很像(參看我的其他文章), 對(duì)于在全局作用域的代碼,就是不包含在任何function內(nèi)的op_array,它的function_name為NULL。結(jié)構(gòu)中的opcodes保 存了屬于這個(gè)op_array的op code數(shù)組,zend_execute會(huì)從start_op開(kāi)始,逐條解釋執(zhí)行傳入的每條op code, 從而實(shí)現(xiàn)我們PHP腳本想要的結(jié)果。
下一次,我將介紹PHP變量的靈魂 – zval, 你將會(huì)看到PHP是如何實(shí)現(xiàn)它的變量傳遞,類(lèi)型戲法,等等。總結(jié)
以上是生活随笔為你收集整理的深入浅出PHP(Exploring PHP)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 黄山风景区离哪个站近
- 下一篇: 来亦来去难去是什么歌啊