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

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                ### 8.2.2 內部實現 命名空間的實現實際比較簡單,當聲明了一個命名空間后,接下來編譯類、函數和常量時會把類名、函數名和常量名統一加上命名空間的名稱作為前綴存儲,也就是說聲明在命名空間中的類、函數和常量的實際名稱是被修改過的,這樣來看他們與普通的定義方式是沒有區別的,只是這個前綴是內核幫我們自動添加的,例如: ```php //ns_define.php namespace com\aa; const MY_CONST = 1234; function my_func(){ /* ... */ } class my_class { /* ... */ } ``` 最終MY_CONST、my_func、my_class在EG(zend_constants)、EG(function_table)、EG(class_table)中的實際存儲名稱被修改為:com\aa\MY_CONST、com\aa\my_func、com\aa\my_class。 下面具體看下編譯過程,namespace語法被編譯為ZEND_AST_NAMESPACE類型的語法樹節點,它有兩個子節點:child[0]為命名空間的名稱、child[1]為通過{}方式定義時包裹的語句。 ![](../img/ast_namespace.png) 此節點的編譯函數為zend_compile_namespace(): ```c void zend_compile_namespace(zend_ast *ast) { zend_ast *name_ast = ast->child[0]; zend_ast *stmt_ast = ast->child[1]; zend_string *name; zend_bool with_bracket = stmt_ast != NULL; //檢查聲明方式,不允許{}與非{}混用 ... if (FC(current_namespace)) { zend_string_release(FC(current_namespace)); } if (name_ast) { name = zend_ast_get_str(name_ast); if (ZEND_FETCH_CLASS_DEFAULT != zend_get_class_fetch_type(name)) { zend_error_noreturn(E_COMPILE_ERROR, "Cannot use '%s' as namespace name", ZSTR_VAL(name)); } //將命名空間名稱保存到FC(current_namespace) FC(current_namespace) = zend_string_copy(name); } else { FC(current_namespace) = NULL; } //重置use導入的命名空間符號表 zend_reset_import_tables(); ... if (stmt_ast) { //如果是通過namespace xxx { ... }這種方式聲明的則直接編譯{}中的語句 zend_compile_top_stmt(stmt_ast); zend_end_namespace(); } } ``` 從上面的編譯過程可以看出,命名空間定義的編譯過程非常簡單,最主要的操作是把FC(current_namespace)設置為當前定義的命名空間名稱,FC()這個宏為:CG(file_context),前面曾介紹過,file_context是在編譯過程中使用的一個結構: ```c typedef struct _zend_file_context { zend_declarables declarables; znode implementing_class; //當前所屬namespace zend_string *current_namespace; //是否在namespace中 zend_bool in_namespace; //當前namespace是否為{}定義 zend_bool has_bracketed_namespaces; //下面這三個值在后面介紹use時再說明,這里忽略即可 HashTable *imports; HashTable *imports_function; HashTable *imports_const; } zend_file_context; ``` 編譯完namespace聲明語句后接著編譯下面的語句,此后定義的類、函數、常量均屬于此命名空間,直到遇到下一個namespace的定義,接下來繼續分析下這三種類型編譯過程中有何不同之處。 __(1)編譯類、函數__ 前面章節曾詳細介紹過函數、類的編譯過程,總結下主要分為兩步:第1步是編譯函數、類,這個過程將分別生成一條ZEND_DECLARE_FUNCTION、ZEND_DECLARE_CLASS的opcode;第2步是在整個腳本編譯的最后執行zend_do_early_binding(),這一步相當于執行ZEND_DECLARE_FUNCTION、ZEND_DECLARE_CLASS,函數、類正是在這一步注冊到EG(function_table)、EG(class_table)中去的。 在生成ZEND_DECLARE_FUNCTION、ZEND_DECLARE_CLASS兩條opcode時會把函數名、類名的存儲位置通過操作數記錄下來,然后在zend_do_early_binding()階段直接獲取函數名、類名作為key注冊到EG(function_table)、EG(class_table)中,定義在命名空間中的函數、類的名稱修改正是在生成ZEND_DECLARE_FUNCTION、ZEND_DECLARE_CLASS時完成的,下面以函數為例看下具體的處理: ```c //函數的編譯方法 void zend_compile_func_decl(znode *result, zend_ast *ast) { ... //生成函數聲明的opcode:ZEND_DECLARE_FUNCTION zend_begin_func_decl(result, op_array, decl); //編譯參數、函數體 ... } ``` ```c static void zend_begin_func_decl(znode *result, zend_op_array *op_array, zend_ast_decl *decl) { ... //獲取函數名稱 op_array->function_name = name = zend_prefix_with_ns(unqualified_name); lcname = zend_string_tolower(name); if (FC(imports_function)) { //如果通過use導入了其他命名空間則檢查函數名稱是否已存在 } .... //生成一條opcode:ZEND_DECLARE_FUNCTION opline = get_next_op(CG(active_op_array)); opline->opcode = ZEND_DECLARE_FUNCTION; //函數名的存儲位置記錄在op2中 opline->op2_type = IS_CONST; LITERAL_STR(opline->op2, zend_string_copy(lcname)); ... } ``` 函數名稱通過zend_prefix_with_ns()方法獲取: ```c zend_string *zend_prefix_with_ns(zend_string *name) { if (FC(current_namespace)) { //如果當前是在namespace下則拼上namespace名稱作為前綴 zend_string *ns = FC(current_namespace); return zend_concat_names(ZSTR_VAL(ns), ZSTR_LEN(ns), ZSTR_VAL(name), ZSTR_LEN(name)); } else { return zend_string_copy(name); } } ``` 在zend_prefix_with_ns()方法中如果發現FC(current_namespace)不為空則將函數名加上FC(current_namespace)作為前綴,接下來向EG(function_table)注冊時就使用修改后的函數名作為key,類的情況與函數的處理方式相同,不再贅述。 __(2)編譯常量__ 常量的編譯過程與函數、類基本相同,也是在編譯過程獲取常量名時檢查FC(current_namespace)是否為空,如果不為空表示常量聲明在namespace下,則為常量名加上FC(current_namespace)前綴。 總結下命名空間的定義:編譯時如果發現定義了一個namespace,則將命名空間名稱保存到FC(current_namespace),編譯類、函數、常量時先判斷FC(current_namespace)是否為空,如果為空則按正常名稱編譯,如果不為空則將類名、函數名、常量名加上FC(current_namespace)作為前綴,然后再以修改后的名稱注冊。整個過程相當于PHP幫我們補全了類名、函數名、常量名。
                  <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>

                              哎呀哎呀视频在线观看