新聞資訊

PHP源代碼分(fēn)析-echo實現(xiàn)詳解

echo,這(zhè)個(gè)是(shì)PHP運用(yòng)得(de)最多(duō)的(de)标記之一(yī),算(suàn)不(bù)上(shàng)是(shìλ)函數(shù),PHP手冊裡(lǐ)這(zhè)麽寫的(de),因為(wèi)它沒有(yǒu)返回值。今天好§(hǎo)奇就(jiù)去(qù)看(kàn)看(kàn)PHP的(de)源代碼,因為(wèi)echo不(bù)是(shì)一(yī)般的(de)₽函數(shù),所以找起來(lái)比較費(fèi)勁,一(yī)般的(de)函數(shù)隻要(yào)搜索PHP_FUNCTION(fun_naλme)基本就(jiù)能(néng)找著(zhe)函數(shù)的(de)實現(xiàn)方式,但(dàn)是(shì)PHP♠是(shì)一(yī)門(mén)腳本語言,所以的(de)符号都(dōu)會(huì)先經過詞法解析和(hé)語法解析階段,這(zhè)兩個(gè)階段是(s₹hì)由lex&yacc實現(xiàn)的(de)。對(duì)應的(de)文(wén)件(jiàn)在php_source/Zend/目錄下(xi±à)面的(de)zend_language_parser.y及zend_language_scanner.l
首先看(kàn)zend_language_scanner.l文(wén)件(jiàn),1077行(x£íng):
<ST_IN_SCRIPTING>“echo” {
return T_ECHO;
}
ZEND引擎在讀(dú)取一(yī)個(gè)PHP文(wén)件(jiàn)之後會(huì)先進行(xíng)詞法分(fēn)析,就(jiù)是(shì)用(yβòng)lex掃描,把對(duì)應的(de)PHP字符轉換成相(xiàng)應的(de)标記(也(yě)叫token),比如(rú)你(nǐ)echo$a;在碰到(dào)這(zhè)句首先會(h>uì)匹配到(dào)echo,符合上(shàng)面的(de)規則,然後就(jiù)返回一(yī)個(gè)T_ECHO标記,這(zhè)個(gè)在後面的(de)語法分(fēn)析會(huì)用•(yòng)上(shàng),也(yě)就(jiù)是(shì)在zend_language_parser.y文(wén)件(jiàn)中:
unticked_statement:
。。。。中間(jiān)有(yǒu)省略
|        T_GLOBAL global_var_list ‘;’
|        T_STATIC static_var_l¥ist ‘;’
|        T_ECHO echo_expr_list ‘;’
|        T_INLINE_HTML                ‍        { zend_do_echo(&$1 TSRMLS_CC); }
看(kàn)到(dào)了(le)T_ECHO,後面跟著(zhe)echo_expr_list,再搜這(zhè)個(gè)字符串,找到(βdào):
echo_expr_list:
echo_expr_list ‘,’ expr { zend_do_echo(&$3 TSRMLS≈_CC); }            //第1行(xíng),
|        expr                   &₹nbsp;                   &βnbsp;{ zend_do_echo(&$1 TSRMLS_CC); }   //第2行(xíng)
對(duì)于第1行(xíng)就(jiù)像 echo $var_1,$var_2,
執行(xíng)動作(zuò)就(jiù)是(shì)zend_do_echo()函數(shù),在Zend/目錄下(xià)面搜索一(yī)下(xià)這(zhè)個(gè)函數(shù),就(jεiù)能(néng)知(zhī)道(dào)這(zhè)個(gè)函數(shù)是(shì)在zend_compile.c文(wén)件(jiàn✔)裡(lǐ)面實現(xiàn)的(de):
void zend_do_echo(znode *arg TSRMLS_DC)
{
        zend_op *opline = get_next_≈op(CG(active_op_array) TSRMLS_CC);
        opline->opcode = ZEND_ECHO;
        opline->op1 = *arg;
        SET_UNUSED(opline->op2);
}


這(zhè)個(gè)函數(shù)沒有(yǒu)做(zuò)什(shén)麽真正的(de)輸出動作(zuò),隻是(shì)把這(zhè)個(gè)zen<d_op操作(zuò)數(shù)的(de)類型置為(wèi)ZEND_ECHO,把要(yào)輸出的(de)內'(nèi)容賦給opline->op1 = *arg;
真正的(de)輸出動作(zuò)是(shì)由ZEND引擎實現(xiàn)的(de),要(yào)知(zhī)道(dào)所有(yǒu)的(de)操作(zuò)數(shù)都(dφōu)會(huì)被ZEND引擎執行(xíng)。再搜索一(yī)下(xià)ZEND_ECHO,在zend_vm_def.h頭文(wén)件(jiàn)裡(lǐ)面找到(dào)它的★(de)定義:
ZEND_VM_HANDLER(40, ZEND_ECHO, CONST|TMP|VAR|CV, ANY)
{
zend_op *opline = EX(opline);
zend_free_op free_op1;
zval z_copy;
zval *z = GET_OP1_ZVAL_PTR(BP_VAR_R);
if (Z_TYPE_P(z) == IS_OBJECT && Z_OBJ_HT_P(z)->get_method != NULL &&'
zend_std_cast_object_tostring(z, &z_copy, IS_STRING TSRMLS_CC) == SUCCESS) {
zend_print_variable(&z_copy);
zval_dtor(&z_copy);
} else {
zend_print_variable(z);
}
FREE_OP1();
ZEND_VM_NEXT_OPCODE();
}
看(kàn)紅(hóng)色的(de)兩個(gè)代碼段,如(rú)果遇到(dào)的(de)變量是(shì)一(yī)個(gè)對(duì✔)象,就(jiù)調用(yòng)zend_std_cast_object_tostring把對(duì)象轉化(huà)為(wèi)字符串,然後再♦調用(yòng)zend_print_variable()輸出。
剩下(xià)的(de)工(gōng)作(zuò)就(jiù)是(shì)找出zend_print_variable的(de)實現(xiàn)♦了(le)。不(bù)過我發現(xiàn)這(zhè)個(gè)函數(shù)還(hái)真的(de)隐藏得(de)非常深,經過了(le)一(yī)層又(yòu)一(yī)層的(de♣)調用(yòng),最後給找了(le)再來(lái)。
在/Zend/zend_variables.c下(xià)面實現(xiàn)了(le)zend_print_variable函數(s≠hù):
ZEND_API int zend_print_variable(zval *var)
{
        return zend_print_zval(var, 0);
}


在/Zend/zend.c文(wén)件(jiàn)裡(lǐ)面實現(xiàn)了(le)zend_print_zval
ZEND_API int zend_print_zval(zval *expr, int indent)
{
        return zend_print_zval_ex(zend_write, expr, ind"ent);
}
ZEND_API int zend_print_zval_ex(zend_write_func_t write_func, zval *expr, int in♥dent)
{
        zval expr_copy;
        int use_copy;
        zend_make_printable_zval(expr, &expr_copy, &aλmp;use_copy);
        if (use_copy) {
                expr = &exp♥r_copy;
        }
        if (expr->value.str.len==0) { /* optimize away empty str§ings */
                if (use_copy) {
               £;         zval_dtor(expr);
                }
               ♦; return 0;
        }
        write_func(expr->value.str.val, expr->value.str.len);
        if (use_copy) {
                zval_dtor(expr);
        }
        return expr->value.str.len;
}


注意上(shàng)面函數(shù)标紅(hóng)的(de)三個(gè)部分(fēn),
ZEND_API int zend_print_zval_ex(zend_write_func_t £write_func, zval*expr, intindent)第一(yī)個(gè)參數(shù)是(shì)一(yī)個(gè)函數(sh↕ù)指針(忘了(le)是(shì)不(bù)是(shì)這(zhè)樣叫,不(bù)明(míng)白(bái)的(de)可(kě)以百度一(yī)下(xià)),所以實際上(shàng)最後調≠用(yòng)的(de)是(shì)zend_write(expr,indent);
zend_write也(yě)是(shì)一(yī)個(gè)函數(shù)指針,在/Zend/zend.c裡(lǐ)面:
typedef int (*zend_write_func_t)(const char *str, uint str_length);
ZEND_API zend_write_func_t zend_write;
而zend_write的(de)初始化(huà)是(shì)在zend_startup()函數(shù)裡(lǐ)面,這(zhè)是(shì)zend引擎啓動的(de)時(shí)候需要(yào)做÷(zuò)的(de)一(yī)些(xiē)初始化(huà)工(gōng)作(zuò),有(yǒu)下(xià)面一(×yī)句:
zend_write = (zend_write_func_t) utility_functions->write_function;
然後是(shì)在/main/目錄下(xià)面的(de)main.c文(wén)件(jiàn)裡(lǐ)面的(de)php_modu→le_startup函數(shù)調用(yòng)了(le)zend_startup()函數(shù),就(jiù)是(shì)說(shuō)PHP作(zuò)為(wèi)♣模塊啓動的(de)時(shí)候需要(yào)進行(xíng)的(de)一(yī)些(xiē)初始化(huà)動作(zuò)都(dōu)在這(zhè)"裡(lǐ)執行(xíng)了(le),在這(zhè)個(gè)函數(shù)裡(lǐ)面調用(yòng✔)了(le)下(xià)面幾句:


zuf.write_function = php_body_write_wrapper;
zuf.fopen_function = php_fopen_wrapper_for_zend;
zuf.message_handler = php_message_handler_for_zend;     &n↕bsp; 
zuf.block_interruptions = sapi_module.block_interruptions;
zuf.unblock_interruptions = sapi_module.unblock_interruptions;
zuf.get_configuration_directive = php_get_configuratio♦n_directive_for_zend;
zuf.ticks_function = php_run_ticks;
zuf.on_timeout = php_on_timeout;
zuf.stream_open_function = php_stream_open_for_zend;
zuf.vspprintf_function = vspprintf;
zuf.getenv_function = sapi_getenv;
zend_startup(&zuf, NULL, 1);


zuf是(shì)一(yī)個(gè)zend_utility_functions結構體(tǐ),注意上(shàng)面紅(hóng)色的(de)兩句,這(zhè)樣✔就(jiù)把php_body_write_wrapper函數(shù)傳給了(le)zuf.write_function,後面還(hái)有©(yǒu)好(hǎo)幾層包裝,最後的(de)實現(xiàn)是(shì)在/main/output.c文(wén)件(jiàn)裡(lǐ)面實現(xi§àn)的(de),是(shì)下(xià)面這(zhè)個(gè)函數(shù):


PHPAPI int php_default_output_func(const char *str★, uint str_len TSRMLS_DC){
        fwrite(str, 1, str_len, stderr);
        return str_len;
}


可(kě)見(jiàn),php裡(lǐ)面的(de)echo最後實際上(shàng)是(shì)通(tōng)過調用(yòng)C裡(lǐ)面的♠(de)fwrite函數(shù)實現(xiàn)的(de),隻是(shì)包裝了(le)十幾層,暫時(shí)想不(bù)通(tō∏ng)為(wèi)什(shén)麽要(yào)經過這(zhè)麽多(duō)層的(de)包裝,經過這(zhè)麽多(duō)層的(de)調用(yòng),難怪PHP的(de)性能(néng)沒法 跟C比了(le)。