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

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                ## 7.5 運行時配置 ### 7.5.1 全局變量(資源) 使用C語言開發程序時經常會使用全局變量進行數據存儲,這就涉及前面已經介紹過的一個問題:線程安全,PHP設計了TSRM(即:線程安全資源管理器)用于解決這個問題,內核中頻繁使用到的EG、CG等都是根據是否開啟ZTS封裝的宏,同樣的,在擴展中也需要必須按照TSRM的規范定義全局變量,除非你的擴展不支持多線程的環境。 PHP為擴展的全局變量提供了一種存儲方式:每個擴展將自己所有的全局變量統一定義在一個結構體中,然后將這個結構體注冊到TSRM中,這樣擴展就可以像使用EG、CG那樣訪問這個結構體。 這個結構體的定義通過`ZEND_BEGIN_MODULE_GLOBALS(extension_name)`、`ZEND_END_MODULE_GLOBALS(extension_name)`兩個宏完成,這兩個宏必須成對出現,中間定義擴展需要的全局變量即可。 ```c ZEND_BEGIN_MODULE_GLOBALS(mytest) zend_long open_cache; HashTable class_table; ZEND_END_MODULE_GLOBALS(mytest) ``` 展開后實際就是個普通的struct: ```c typedef struct _zend_mytest_globals { zend_long open_cache; HashTable class_table; }zend_mytest_globals; ``` 接著創建一個此結構體的全局變量,這時候就會涉及ZTS了,如果未開啟線程安全直接創建普通的全局變量即可,如果開啟線程安全了則需要向TSRM注冊,得到一個唯一的資源id,這個操作也由專門的宏來完成:`ZEND_DECLARE_MODULE_GLOBALS(extension_name)`,展開后: ```c //ZTS:此時只是定義資源id,并沒有向TSRM注冊 ts_rsrc_id mytest_globals_id; //非ZTS zend_mytest_globals mytest_globals; ``` 最后需要定義一個像EG、CG那樣的宏用于訪問擴展的全局資源結構體,這一步將使用`ZEND_MODULE_GLOBALS_ACCESSOR()`宏完成: ```c #define MYTEST_G(v) ZEND_MODULE_GLOBALS_ACCESSOR(mytest, v) ``` 看起來是不是跟EG、CG的定義非常像?這個宏展開后: ```c //ZTS #define MYTEST_G(v) ZEND_TSRMG(mytest_globals_id, zend_mytest_globals *, v) //非ZTS #define MYTEST_G(v) (mytest_globals.v) ``` 接下來就可以在擴展中通過:MYTEST_G(opene_cache)、MYTEST_G(class_table)對結構體成員進行讀寫了。通常會把這個全局資源結構體及結構體的訪問宏定義在頭文件中,然后把全局變量的聲明放到源文件中: ```c //php_mytest.h #define MYTEST_G(v) ZEND_MODULE_GLOBALS_ACCESSOR(mytest, v) ZEND_BEGIN_MODULE_GLOBALS(mytest) zend_long open_cache; HashTable class_table; ZEND_END_MODULE_GLOBALS(mytest) //mytest.c ZEND_DECLARE_MODULE_GLOBALS(mytest) ``` > 在一個擴展中并不是只能定義一個全局變量結構,數目是不限制的。 ### 7.5.2 php.ini配置 php.ini是PHP主要的配置文件,解析時PHP將在這些地方依次查找該文件:當前工作目錄、環境變量PHPRC指定目錄、編譯時指定的路徑,在命令行模式下,php.ini的查找路徑可以用`-c`參數替代。 該文件的語法非常簡單:`配置標識符 = 值`。空白字符和用分號';'開始的行被忽略,[xxx]行也被忽略;配置標識符大寫敏感,通常會用'.'區分不同的節;值可以是數字、字符串、PHP常量、位運算表達式。 關于php.ini的解析過程本節不作介紹,只從應用的角度介紹如何在一個擴展中獲取一個配置項,通常會把php.ini的配置映射到一個變量,從而在使用時直接讀取那個變量,也就是把所有的配置轉化為了C語言中的變量,擴展中一般會把php.ini配置映射到上一節介紹的全局變量(資源),要想實現這個轉化需要在擴展中為每一項配置設置映射規則: ```c PHP_INI_BEGIN() //每一項配置規則 ... PHP_INI_END(); ``` 這兩個宏實際只是把各配置規則組成一個數組,配置規則通過`STD_PHP_INI_ENTRY()`設置: ```c STD_PHP_INI_ENTRY(name,default_value,modifiable,on_modify,property_name,struct_type,struct_ptr) ``` * __name:__ php.ini中的配置標識符 * __default_value:__ 默認值,注意不管轉化后是什么類型,這里必須設置為字符串 * __modifiable:__ 可修改等級,ZEND_INI_USER為可以在php腳本中修改,ZEND_INI_SYSTEM為可以在php.ini中修改,還有一個ZEND_INI_PERDIR,ZEND_INI_ALL表示三種都可以,通常情況下設置為ZEND_INI_ALL、ZEND_INI_SYSTEM即可 * __on_modify:__ 函數指針,用于指定發現這個配置后賦值處理的函數,默認提供了5個:OnUpdateBool、OnUpdateLong、OnUpdateLongGEZero、OnUpdateReal、OnUpdateString、OnUpdateStringUnempty,支持可以自定義 * __property_name:__ 要映射到的結構struct_type中的成員 * __struct_type:__ 映射結構的類型 * __struct_ptr:__ 映射結構的變量地址,發現配置后會 > 除了STD_PHP_INI_ENTRY()這個宏還有一個類似的宏`STD_PHP_INI_BOOLEAN()`,用法一致,差別在于后者會自動把配置添加到phpinfo()輸出中。 這個宏展開后生成一個`zend_ini_entry_def`結構: ```c typedef struct _zend_ini_entry_def { const char *name; int (*on_modify)(zend_ini_entry *entry, zend_string *new_value, void *mh_arg1, void *mh_arg2, void *mh_arg3, int stage); void *mh_arg1; //映射成員所在結構體的偏移:offsetof(type, member-designator)取到 void *mh_arg2; //要映射到結構的地址 void *mh_arg3; const char *value;//默認值 void (*displayer)(zend_ini_entry *ini_entry, int type); int modifiable; uint name_length; uint value_length; } zend_ini_entry_def; ``` 比如將php.ini中的`mytest.opene_cache`值映射到`MYTEST_G()`結構中的open_cache,類型為zend_long,默認值109,則可以這么定義: ```c PHP_INI_BEGIN() STD_PHP_INI_ENTRY("mytest.open_cache", "109", PHP_INI_ALL, OnUpdateLong, open_cache, zend_mytest_globals, mytest_globals) PHP_INI_END(); ``` property_name設置的是要映射到的結構成員`mytest_globals->open_cache`,zend_mytest_globals、mytest_globals都是宏展開后的實際值,前者是結構體類型,后者是具體分配的變量,上面的定義展開后: ```c static const zend_ini_entry_def ini_entries[] = { { "mytest.open_cache", OnUpdateLong, (void *) XtOffsetOf(zend_mytest_globals, open_cache), //獲取成員在結構體中的內存偏移 (void*)&mytest_globals, NULL, "109", NULL, PHP_INI_ALL, sizeof("mytest.open_cache")-1, sizeof("109")-1 }, { NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0} } ``` > `XtOffsetOf()`這個宏在linux環境下展開就是`offsetof()`,用來獲取一個結構體成員的offset,比如: > > #include <stdio.h> > #include <stddef.h> > > typedef struct{ > int id; > char *name; > }my_struct; > > int main(void) > { > printf("%d\n", (void*)offsetof(my_struct, name)); > return 0; > } > > 通過這個offset及結構體指針就可以讀取這個成員:`(char*)my_sutct + offset`,等價于`my_sutct->name`。 定義完上面的配置映射規則后就可以進行映射了,這一步通過`REGISTER_INI_ENTRIES()`完成,這個宏展開后:`zend_register_ini_entries(ini_entries, module_number)`,ini_entries是`PHP_INI_BEGIN/END()`兩個宏生成的配置映射規則數組,通常會把這個操作放到`PHP_MINIT_FUNCTION()`中,注意:此時php.ini已經解析到`configuration_hash`哈希表中,`zend_register_ini_entries()`將根據配置name查找這個哈希表,如果找到了表明用戶在php.ini中配置了該項,然后將調用此規則指定的on_modify函數進行賦值,比如上面的示例將調用`OnUpdateLong()`處理,整體的流程: ```c ZEND_API int zend_register_ini_entries(const zend_ini_entry_def *ini_entry, int module_number) { zend_ini_entry *p; zval *default_value; HashTable *directives = registered_zend_ini_directives; while (ini_entry->name) { //分配zend_ini_entry結構 p = pemalloc(sizeof(zend_ini_entry), 1); //zend_ini_entry初始化 ... //添加到registered_zend_ini_directives,EG(ini_directives)也是指向此HashTable if (zend_hash_add_ptr(directives, p->name, (void*)p) == NULL) { ... } //zend_get_configuration_directive()最終將調用cfg_get_entry() //從configuration_hash哈希表中查找配置,如果沒有找到將使用默認值 default_value = zend_get_configuration_directive(p->name) ... if (p->on_modify) { //調用定義的賦值handler處理 p->on_modify(p, p->value, p->mh_arg1, p->mh_arg2, p->mh_arg3, ZEND_INI_STAGE_STARTUP); } } } ``` `OnUpdateLong()`賦值處理: ```c ZEND_API ZEND_INI_MH(OnUpdateLong) { zend_long *p; #ifndef ZTS //存儲結構的指針 char *base = (char *) mh_arg2; #else char *base; //ZTS下需要向TSRM中獲取存儲結構的指針 base = (char *) ts_resource(*((int *) mh_arg2)); #endif //指向結構體成員的位置 p = (zend_long *) (base+(size_t) mh_arg1); //將值轉為zend_long *p = zend_atol(ZSTR_VAL(new_value), (int)ZSTR_LEN(new_value)); return SUCCESS; } ``` 如果PHP提供的幾個on_modify不能滿足需求可以自定義on_modify函數,舉個例子:將php.ini中的配置`mytest.class`插入MYTESY_G(class_table)哈希表,則可以在擴展中定義這樣一個on_modify:`ZEND_INI_MH(OnUpdateAddArray)`,將php.ini映射到全局變量的完整代碼: ```c //php_mytest.h #define MYTEST_G(v) ZEND_MODULE_GLOBALS_ACCESSOR(mytest, v) ZEND_BEGIN_MODULE_GLOBALS(mytest) zend_long open_cache; HashTable class_table; ZEND_END_MODULE_GLOBALS(mytest) //自定義on_modify函數 ZEND_API ZEND_INI_MH(OnUpdateAddArray); ``` ```c //mytest.c ZEND_DECLARE_MODULE_GLOBALS(mytest) PHP_INI_BEGIN() STD_PHP_INI_ENTRY("mytest.open_cache", "109", PHP_INI_ALL, OnUpdateLong, open_cache, zend_mytest_globals, mytest_globals) STD_PHP_INI_ENTRY("mytest.class", "stdClass", PHP_INI_ALL, OnUpdateAddArray, class_table, zend_mytest_globals, mytest_globals) PHP_INI_END(); ZEND_API ZEND_INI_MH(OnUpdateAddArray) { HashTable *ht; zval val; #ifndef ZTS char *base = (char *) mh_arg2; #else char *base; base = (char *) ts_resource(*((int *) mh_arg2)); #endif ht = (HashTable*)(base+(size_t) mh_arg1); ZVAL_NULL(&val); zend_hash_add(ht, new_value, &val); } PHP_MINIT_FUNCTION(mytest) { zend_hash_init(&MYTEST_G(class_table), 0, NULL, NULL, 1); //將php.ini解析到指定結構體 REGISTER_INI_ENTRIES(); printf("open_cache %d\n", MYTEST_G(open_cache)); } zend_module_entry mytest_module_entry = { STANDARD_MODULE_HEADER, "mytest", NULL,//mytest_functions, PHP_MINIT(mytest), NULL,//PHP_MSHUTDOWN(mytest), NULL,//PHP_RINIT(mytest), NULL,//PHP_RSHUTDOWN(mytest), NULL,//PHP_MINFO(mytest), "1.0.0", STANDARD_MODULE_PROPERTIES }; #ifdef COMPILE_DL_TIMEOUT #ifdef ZTS ZEND_TSRMLS_CACHE_DEFINE() #endif ZEND_GET_MODULE(mytest) #endif ``` 本節主要介紹了如何將php.ini配置項解析到C語言變量中,總結下主要分為兩步: * __定義解析規則:__ 通過PHP_INI_BEGIN()、PHP_INI_END()、STD_PHP_INI_ENTRY()配置 * __執行規則映射:__ 由REGISTER_INI_ENTRIES()來完成,這個操作之后解析目的變量就可以使用了
                  <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>

                              哎呀哎呀视频在线观看