<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國際加速解決方案。 廣告
                前面小節中對函數的內部表示以及參數的傳遞,返回值都有了介紹,那函數是怎么被調用的呢?內置函數和用戶定義函數在調用時會有什么不一樣呢?下面將介紹函數調用和執行的過程。 ## 函數的調用[]() 函數被調用需要一些基本的信息,比如函數的名稱,參數以及函數的定義(也就是函數的具體執行內容),從我們開發者的角度來看,定義了一個函數我們在執行的時候自然知道這個函數叫什么名字,以及調用的時候給傳遞了什么參數、函數的操作內容。但是對于Zend引擎不能像我們這樣能“看懂”php源代碼,他它需要對代碼進行處理以后才能執行。我們還是從以下兩個小例子開始: <?php function foo(){ echo "I'm foo!"; } foo(); ?> 下面我們先看一下其對應的opcodes: function name: (null) line # * op fetch ext return operands --------------------------------------------------------------------------------- DO_FCALL 0 'foo' NOP > RETURN 1 ? function name: foo line # * op fetch ext return operands --------------------------------------------------------------------------------- 4 0 > echo 'I%27m+foo%21' 5 1 > RETURN null 上面是去除了一些枝節信息的的opcodes,可以看到執行時函數部分的opcodes是單獨獨立出來的,這點對于函數的執行特別重要,下面的部分會詳細介紹。現在,我們把焦點放到對foo函數的調用上面。調用foo的OPCODE是“DO_FCALL“, DO_FCALL進行函數調用操作時,ZE會在function_table中根據函數名(如前所述,這里的函數名經過str_tolower的處理,所以PHP的函數名大小寫不敏感)查找函數的定義, 如果不存在,則報出“Call to undefined function xxx()"的錯誤信息; 如果存在,就返回該函數zend_function結構指針,然后通過function.type的值來判斷函數是內部函數還是用戶定義的函數,調用zend_execute_internal(zend_internal_function.handler)或者直接 調用zend_execute來執行這個函數包含的zend_op_array。 ## 函數的執行[]() 細心的讀者可能會注意到上面opcodes里函數被調用的時候以及函數定義那都有個"function name:",其實用戶定義函數的執行與其他語句的執行并無區別,在本質上看,其實函數中的php語句與函數外的php語句并無不同。函數體本身最大的區別,在于其執行環境的不同。這個“執行環境”最重要的特征就是變量的作用域。大家都知道,函數內定義的變量在函數體外是無法直接使用的,反之也是一樣。那么,在函數執行的時候,進入函數前的環境信息是必須要保存的。在函數執行完畢后,這些環境信息也會被還原,使整個程序繼續的執行下去。 內部函數的執行與用戶函數不同。用戶函數是php語句一條條“翻譯”成op_line組成的一個op_array,而內部函數則是用C來實現的,因為執行環境也是C環境,所以可以直接調用。如下面的例子: [php] <?php $foo = 'test'; print_r($foo); ?> 對應的opcodes也很簡單: line # * op fetch ext return operands --------------------------------------------------------------------------------- 2 0 > ASSIGN !0, 'test' 3 1 SEND_VAR !0 2 DO_FCALL 1 'print_r' 4 3 > RETURN 1 可以看出,生成的opcodes中,內部函數和用戶函數的處理都是由DO_FCALL來進行的。而在其具體實現的zend_do_fcall_common_helper_SPEC()中,則對是否為內部函數進行了判斷,如果是內部函數,則使用一個比較長的調用 ((zend_internal_function *) EX(function_state).function)->handler(opline->extended_value, EX_T(opline->result.u.var).var.ptr, EX(function_state).function->common .return_reference?&EX_T(opline->result.u.var).var.ptr:NULL, EX(object), RETURN_VALUE_USED(opline) TSRMLS_CC); 上面這種方式的內部函數是在zend_execute_internal函數沒有定義的情況下。而在而在Zend/zend.c文件的zend_startup函數中, zend_execute_internal = NULL; 此函數確實被賦值為NULL。于是我們在if (!zend_execute_internal)判斷時會成立,所以我們是執行那段很長的調用。那么,這段很長的調用到底是什么呢?以我們常用的 **count**函數為例。在[<<第一節 函數的內部結構>>](#)中,我們知道內部函數所在的結構體中有一個handler指針指向此函數需要調用的內部定義的C函數。這些內部函數在模塊初始化時就以擴展的函數的形式加載到EG(function_table)。其調用順序: php_module_startup --> php_register_extensions --> zend_register_internal_module --> zend_register_module_ex --> zend_register_functions ? zend_register_functions(NULL, module->functions, NULL, module->type TSRMLS_CC) 在standard擴展中。module的定義為: zend_module_entry basic_functions_module = { STANDARD_MODULE_HEADER_EX, NULL, standard_deps, "standard", /* extension name */ basic_functions, /* function list */ ... //省略 } 從上面的代碼可以看出,module->functions是指向basic_functions。在basic_functions.c文件中查找basic_functions的定義。 const zend_function_entry basic_functions[] = { ...// 省略 PHP_FE(count, arginfo_count) ...//省略 } ? #define PHP_FE ZEND_FE #define ZEND_FE(name, arg_info) ZEND_FENTRY(name, ZEND_FN(name), arg_info, 0) #define ZEND_FN(name) zif_##name #define ZEND_FENTRY(zend_name, name, arg_info, flags) { #zend_name, name, arg_info, (zend_uint) (sizeof(arg_info)/sizeof(struct _zend_arg_info)-1), flags }, 綜合上面的代碼,count函數最后調用的函數名為zif_count,但是此函數對外的函數名還是為count。調用的函數名name以第二個元素存放在zend_function_entry結構體數組中。對于zend_function_entry的結構 typedef struct _zend_function_entry { const char *fname; void (*handler)(INTERNAL_FUNCTION_PARAMETERS); const struct _zend_arg_info *arg_info; zend_uint num_args; zend_uint flags; } zend_function_entry; 第二個元素為handler。這也就是我們在執行內部函數時的調用方法。因此在執行時就會調用到對應的函數。 對于用戶定義的函數,在zend_do_fcall_common_helper_SPEC()函數中, if (EX(function_state).function->type == ZEND_USER_FUNCTION || EX(function_state).function->common.scope) { should_change_scope = 1; EX(current_this) = EG(This); EX(current_scope) = EG(scope); EX(current_called_scope) = EG(called_scope); EG(This) = EX(object); EG(scope) = (EX(function_state).function->type == ZEND_USER_FUNCTION || !EX(object)) ? EX(function_state).function->common.scope : NULL; EG(called_scope) = EX(called_scope); } 先將EG下的This,scope等暫時緩存起來(這些在后面會都恢復到此時緩存的數據)。在此之后,對于用戶自定義的函數,程序會依據zend_execute是否等于execute并且是否為異常來判斷是返回,還是直接執行函數定義的op_array: if (zend_execute == execute && !EG(exception)) { EX(call_opline) = opline; ZEND_VM_ENTER(); } else { zend_execute(EG(active_op_array) TSRMLS_CC); } 而在Zend/zend.c文件的zend_startup函數中,已將zend_execute賦值為: zend_execute = execute; 從而對于異常,程序會拋出異常;其它情況,程序會調用execute執行此函數中生成的opcodes。execute函數會遍歷所傳遞給它的zend_op_array數組,以方式 ret = EX(opline)->handler(execute_data TSRMLS_CC) 調用每個opcode的處理函數。而execute_data在execute函數開始時就已經給其分配了空間,這就是這個函數的執行環境。
                  <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>

                              哎呀哎呀视频在线观看