深入理解PHP原理之 echo的實現

2021-06-28 22:30:03 字數 2778 閱讀 1908

php源**分析-echo實現詳解

原諒出處:

echo,這個是php運用得最多的標記之一,算不上是函式,php手冊裡這麼寫的,因為它沒有返回值。今天好奇就去看看php的源**,因為echo不是一般的函式,所以找起來比較費勁,一般的函式只要搜尋php_function(fun_name)基本就能找著函式的實現方式,但是php是一門指令碼語言,所以的符號都會先經過詞法解析和語法解析階段,這兩個階段是由lex&yacc實現的。對應的檔案在php_source/zend/目錄下面的zend_language_parser.y及zend_language_scanner.l

首先看zend_language_scanner.l檔案,1077行:

「echo」

zend引擎在讀取乙個php檔案之後會先進行詞法分析,就是用lex掃瞄,把對應的php字元轉換成相應的標記(也叫token),比如你echo$a;在碰到這句首先會匹配到echo,符合上面的規則,然後就返回乙個t_echo標記,這個在後面的語法分析會用上,也就是在zend_language_parser.y檔案中:

unticked_statement:

。。。。中間有省略

|        t_global global_var_list 『;』

|        t_static static_var_list 『;』

|        t_echo echo_expr_list 『;』

|        t_inline_html                        

看到了t_echo,後面跟著echo_expr_list,再搜這個字串,找到:

echo_expr_list:

echo_expr_list 『,』 expr             //第1行,

|        expr                                           //第2行

對於第1行就像 echo $var_1,$var_2,

執行動作就是zend_do_echo()函式,在zend/目錄下面搜尋一下這個函式,就能知道這個函式是在zend_compile.c檔案裡面實現的:

void zend_do_echo(znode *arg tsrmls_dc)

這個函式沒有做什麼真正的輸出動作,只是把這個zend_op運算元的型別置為zend_echo,把要輸出的內容賦給opline->op1 = *arg;

真正的輸出動作是由zend引擎實現的,要知道所有的運算元都會被zend引擎執行。再搜尋一下zend_echo,在zend_vm_def.h標頭檔案裡面找到它的定義:

zend_vm_handler(40, zend_echo, const|tmp|var|cv, any)

else

free_op1();

zend_vm_next_opcode();

}看紅色的兩個**段,如果遇到的變數是乙個物件,就呼叫zend_std_cast_object_tostring把物件轉化為字串,然後再呼叫zend_print_variable()輸出。

剩下的工作就是找出zend_print_variable的實現了。不過我發現這個函式還真的隱藏得非常深,經過了一層又一層的呼叫,最後給找了再來。

在/zend/zend_variables.c下面實現了zend_print_variable函式:

zend_api int zend_print_variable(zval *var)

在/zend/zend.c檔案裡面實現了zend_print_zval

zend_api int zend_print_zval(zval *expr, int indent)

zend_api int zend_print_zval_ex(zend_write_func_t write_func, zval *expr, int indent)

if (expr->value.str.len==0)

return 0;

}write_func(expr->value.str.val, expr->value.str.len);

if (use_copy)

return expr->value.str.len;

}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;

phpapi int php_default_output_func(const char *str, uint str_len tsrmls_dc)

可見,php裡面的echo最後實際上是通過呼叫c裡面的fwrite函式實現的,只是包裝了十幾層,暫時想不通為什麼要經過這麼多層的包裝,經過這麼多層的呼叫,難怪php的效能沒法跟c比了。

原文**:

深入理解PHP原理之PHP指令碼執行原理 1

php是乙個被廣泛應用的指令碼語言,因為它的成功,所以很多時候,我們應用php的時候是不需要考慮底層到底是怎麼實現的,我相信大多數的php程式設計師是不會去考慮這一點的,在這篇文章中,我會從整個php的執行期入手,大致的介紹下各個階段,包括詞法分析 語法分析和opcode。從最初我們編寫的php指令...

深入理解PHP原理之靜態變數

通常意義上靜態變數是靜態分配的,他們的生命週期和程式的生命週期一樣,只有在程式退出時才結束期生命週期,這和區域性變數相反。靜態變數的型別可以分為靜態全域性變數 靜態區域性變 靜態成員變數,最常見的是靜態區域性變數及靜態成員變數,先看看如下區域性變數的使用 function t t t t 上述的程式...

深入理解PHP之require include順序

在大型的web專案中,include path是乙個模組化設計的根本中的根本 當然,現在也有很多基於autoload的設計,這個不影響本文的 但是正是因為include path,經常會讓我們遇到一些因為沒有找到正確的檔案而導致的看似 詭異 的問題.也就有了如下的疑問 include path是怎麼...