<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                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行: <ST_IN_SCRIPTING>“echo” { return T_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 { zend_do_echo(&$1 TSRMLS_CC); } 看到了T_ECHO,后面跟著echo_expr_list,再搜這個字符串,找到: echo_expr_list: echo_expr_list ‘,’ expr { zend_do_echo(&$3 TSRMLS_CC); } //第1行, | expr { zend_do_echo(&$1 TSRMLS_CC); } //第2行 對于第1行就像 echo $var_1,$var_2, 執行動作就是zend_do_echo()函數,在Zend/目錄下面搜索一下這個函數,就能知道這個函數是在zend_compile.c文件里面實現的: 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); } 這個函數沒有做什么真正的輸出動作,只是把這個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) { 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(); } 看紅色的兩個代碼段,如果遇到的變量是一個對象,就調用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) { return zend_print_zval(var, 0); } 在/Zend/zend.c文件里面實現了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; } 注意上面函數標紅的三個部分, ZEND_API int zend_print_zval_ex(zend_write_func_t write_func, zval*expr, intindent)第一個參數是一個函數指針(忘了是不是這樣叫,不明白的可以百度一下), 所以實際上最后調用的是zend_write(expr,indent); zend_write也是一個函數指針,在/Zend/zend.c里面: typedef int (*zend_write_func_t)(const char *str, uint str_length); ZEND_API zend_write_func_t zend_write; 而zend_write的初始化是在zend_startup()函數里面,這是zend引擎啟動的時候需要做的一些初始化工作,有下面一句: zend_write = (zend_write_func_t) utility_functions->write_function; 然后是在/main/目錄下面的main.c文件里面的php_module_startup函數調用了zend_startup()函數,就是說PHP作為模塊啟動的時候需要進行的一些初始化動作都在這里執行了,在這個函數里面調用了下面幾句: 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是一個zend_utility_functions結構體,注意上面紅色的兩句,這樣就把php_body_write_wrapper函數傳給了zuf.write_function,后面還有好幾層包裝,最后的實現是在/main/output.c文件里面實現的,是下面這個函數: PHPAPI int php_default_output_func(const char *str, uint str_len TSRMLS_DC){ fwrite(str, 1, str_len, stderr); return str_len; } 可見,php里面的echo最后實際上是通過調用C里面的fwrite函數實現的,只是包裝了十幾層,暫時想不通為什么要經過這么多層的包裝,經過這么多層的調用,難怪PHP的性能沒法跟C比了。 出處:http://jackywdx.cn/2009/01/implement_of_php_echo
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看