PHP源代碼分析-echo實現詳解
echo,這(zhè)個(gè)是(shì)PHP運用得最多的(de)标記之(zhī)一(yī / yì /yí),算不(bù)上(shàng)是(shì)函數,PHP手冊裏這(zhè)麽寫的(de),因爲(wéi / wèi)它沒有返回值。今天好奇就(jiù)去看看PHP的(de)源代碼,因爲(wéi / wèi)echo不(bù)是(shì)一(yī / yì /yí)般的(de)函數,所以(yǐ)找起來(lái)比較費勁,一(yī / yì /yí)般的(de)函數隻要(yào / yāo)搜索PHP_FUNCTION(fun_name)基本就(jiù)能找着函數的(de)實現方式,但是(shì)PHP是(shì)一(yī / yì /yí)門腳本語言,所以(yǐ)的(de)符号都會先經過詞法解析和(hé / huò)語法解析階段,這(zhè)兩個(gè)階段是(shì)由lex&yacc實現的(de)。對應的(de)文件在(zài)php_source/Zend/目錄下面的(de)zend_language_parser.y及zend_language_scanner.l
首先看zend_language_scanner.l文件,1077行:
<ST_IN_SCRIPTING>“echo” {
return T_ECHO;
}
ZEND引擎在(zài)讀取一(yī / yì /yí)個(gè)PHP文件之(zhī)後會先進行詞法分析,就(jiù)是(shì)用lex掃描,把對應的(de)PHP字符轉換成相應的(de)标記(也(yě)叫token),比如你echo$a;在(zài)碰到(dào)這(zhè)句首先會匹配到(dào)echo,符合上(shàng)面的(de)規則,然後就(jiù)返回一(yī / yì /yí)個(gè)T_ECHO标記,這(zhè)個(gè)在(zài)後面的(de)語法分析會用上(shàng),也(yě)就(jiù)是(shì)在(zài)zend_language_parser.y文件中:
unticked_statement:
。。。。中間有省略
| T_GLOBAL global_var_list ‘;’
| T_STATIC static_var_list ‘;’
| T_ECHO echo_expr_list ‘;’
| T_INLINE_HTML { zend_do_echo(&$1 TSRMLS_CC); }
看到(dào)了(le/liǎo)T_ECHO,後面跟着echo_expr_list,再搜這(zhè)個(gè)字符串,找到(dào):
echo_expr_list:
echo_expr_list ‘,’ expr { zend_do_echo(&$3 TSRMLS_CC); } //第1行,
| expr { zend_do_echo(&$1 TSRMLS_CC); } //第2行
對于(yú)第1行就(jiù)像 echo $var_1,$var_2,
執行動作就(jiù)是(shì)zend_do_echo()函數,在(zài)Zend/目錄下面搜索一(yī / yì /yí)下這(zhè)個(gè)函數,就(jiù)能知道(dào)這(zhè)個(gè)函數是(shì)在(zài)zend_compile.c文件裏面實現的(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è)函數沒有做什麽真正的(de)輸出(chū)動作,隻是(shì)把這(zhè)個(gè)zend_op操作數的(de)類型置爲(wéi / wèi)ZEND_ECHO,把要(yào / yāo)輸出(chū)的(de)内容賦給opline->op1 = *arg;
真正的(de)輸出(chū)動作是(shì)由ZEND引擎實現的(de),要(yào / yāo)知道(dào)所有的(de)操作數都會被ZEND引擎執行。再搜索一(yī / yì /yí)下ZEND_ECHO,在(zài)zend_vm_def.h頭文件裏面找到(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();
}
看紅色的(de)兩個(gè)代碼段,如果遇到(dào)的(de)變量是(shì)一(yī / yì /yí)個(gè)對象,就(jiù)調用zend_std_cast_object_tostring把對象轉化爲(wéi / wèi)字符串,然後再調用zend_print_variable()輸出(chū)。
剩下的(de)工作就(jiù)是(shì)找出(chū)zend_print_variable的(de)實現了(le/liǎo)。不(bù)過我發現這(zhè)個(gè)函數還真的(de)隐藏得非常深,經過了(le/liǎo)一(yī / yì /yí)層又一(yī / yì /yí)層的(de)調用,最後給找了(le/liǎo)再來(lái)。
在(zài)/Zend/zend_variables.c下面實現了(le/liǎo)zend_print_variable函數:
ZEND_API int zend_print_variable(zval *var)
{
return zend_print_zval(var, 0);
}
在(zài)/Zend/zend.c文件裏面實現了(le/liǎo)zend_print_zval
ZEND_API int zend_print_zval(zval *expr, int indent)
{
return zend_print_zval_ex(zend_write, expr, indent);
}
ZEND_API int zend_print_zval_ex(zend_write_func_t write_func, zval *expr, int indent)
{
zval expr_copy;
int use_copy;
zend_make_printable_zval(expr, &expr_copy, &use_copy);
if (use_copy) {
expr = &expr_copy;
}
if (expr->value.str.len==0) { /* optimize away empty strings */
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)面函數标紅的(de)三個(gè)部分,
ZEND_API int zend_print_zval_ex(zend_write_func_t write_func, zval*expr, intindent)第一(yī / yì /yí)個(gè)參數是(shì)一(yī / yì /yí)個(gè)函數指針(忘了(le/liǎo)是(shì)不(bù)是(shì)這(zhè)樣叫,不(bù)明白的(de)可以(yǐ)百度一(yī / yì /yí)下),所以(yǐ)實際上(shàng)最後調用的(de)是(shì)zend_write(expr,indent);
zend_write也(yě)是(shì)一(yī / yì /yí)個(gè)函數指針,在(zài)/Zend/zend.c裏面:
typedef int (*zend_write_func_t)(const char *str, uint str_length);
ZEND_API zend_write_func_t zend_write;
而(ér)zend_write的(de)初始化是(shì)在(zài)zend_startup()函數裏面,這(zhè)是(shì)zend引擎啓動的(de)時(shí)候需要(yào / yāo)做的(de)一(yī / yì /yí)些初始化工作,有下面一(yī / yì /yí)句:
zend_write = (zend_write_func_t) utility_functions->write_function;
然後是(shì)在(zài)/main/目錄下面的(de)main.c文件裏面的(de)php_module_startup函數調用了(le/liǎo)zend_startup()函數,就(jiù)是(shì)說(shuō)PHP作爲(wéi / wèi)模塊啓動的(de)時(shí)候需要(yào / yāo)進行的(de)一(yī / yì /yí)些初始化動作都在(zài)這(zhè)裏執行了(le/liǎo),在(zài)這(zhè)個(gè)函數裏面調用了(le/liǎo)下面幾句:
zuf.write_function = php_body_write_wrapper;
zuf.fopen_function = php_fopen_wrapper_for_zend;
zuf.message_handler = php_message_handler_for_zend;
zuf.block_interruptions = sapi_module.block_interruptions;
zuf.unblock_interruptions = sapi_module.unblock_interruptions;
zuf.get_configuration_directive = php_get_configuration_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ī / yì /yí)個(gè)zend_utility_functions結構體,注意上(shàng)面紅色的(de)兩句,這(zhè)樣就(jiù)把php_body_write_wrapper函數傳給了(le/liǎo)zuf.write_function,後面還有好幾層包裝,最後的(de)實現是(shì)在(zài)/main/output.c文件裏面實現的(de),是(shì)下面這(zhè)個(gè)函數:
PHPAPI int php_default_output_func(const char *str, uint str_len TSRMLS_DC){
fwrite(str, 1, str_len, stderr);
return str_len;
}
可見,php裏面的(de)echo最後實際上(shàng)是(shì)通過調用C裏面的(de)fwrite函數實現的(de),隻是(shì)包裝了(le/liǎo)十幾層,暫時(shí)想不(bù)通爲(wéi / wèi)什麽要(yào / yāo)經過這(zhè)麽多層的(de)包裝,經過這(zhè)麽多層的(de)調用,難怪PHP的(de)性能沒法跟C比了(le/liǎo)。