INI設(shè)置
和上一章你看到的超級全局變量以及持久化常量一樣, php.ini值必須在擴展的MINIT代碼塊中定義. 然而, 和其他特性不同的是, INI選項的定義僅僅由簡單的啟動/終止線組成.
PHP_MINIT_FUNCTION(sample4) { REGISTER_INI_ENTRIES(); return SUCCESS; } PHP_MSHUTDOWN_FUNCTION(sample4) { UNREGISTER_INI_ENTRIES(); return SUCCESS; }
定義并訪問INI設(shè)置
INI指令自身是在源碼文件中MINIT函數(shù)上面, 使用下面的宏完全獨立的定義的, 在這兩個宏之間可以定義一個或多個INI指令:
PHP_INI_BEIGN() PHP_INI_END()
立即學(xué)習(xí)“PHP免費學(xué)習(xí)筆記(深入)”;
這兩個宏函數(shù)和ZEND_BEGIN_MODULE_GLOBALS()/ZEND_END_MODULE_GLOBALS()異曲同工. 不過這里不是typdef一個結(jié)構(gòu)體, 而是對靜態(tài)數(shù)據(jù)實例定義的框架組織:
static zend_ini_entry ini_entries[] = { {0,0,NULL,0,NULL,NULL,NULL,NULL,NULL,0,NULL,0,0,NULL} };
立即學(xué)習(xí)“PHP免費學(xué)習(xí)筆記(深入)”;
如你所見, 它定義了一個zend_ini_entry值的向量, 以一條空的記錄結(jié)束. 這和你在前面看到的靜態(tài)向量function_entry的定義一致.
簡單的INI設(shè)置
現(xiàn)在, 你已經(jīng)有一個INI結(jié)構(gòu)體用于定義INI指令, 以及引擎注冊/卸載INI設(shè)置的機制, 因此我們可以真正的去為你的擴展定義一些INI指令了. 假設(shè)你的擴展暴露了一個打招呼的函數(shù), 就像第5章"你的第一個擴展"中一樣, 不過, 你想讓打招呼的話可以自定義:
PHP_FUNCTION(sample4_hello_world) { php_printf("Hello World!\n"); }
最簡單最直接的方式就是定義一個INI指令, 并給它一個默認(rèn)值"Hello world!":
#include "php_ini.h" PHP_INI_BEGIN() PHP_INI_ENTRY("sample4.greeting", "Hello World", PHP_INI_ALL, NULL) PHP_INI_END()
立即學(xué)習(xí)“PHP免費學(xué)習(xí)筆記(深入)”;
你可能已經(jīng)猜到了, 這個宏的前兩個參數(shù)表示INI指令的名字和它的默認(rèn)值. 第三個參數(shù)用來確定引擎是否允許這個INI指令被修改(這將涉及到本章后面要介紹的訪問級別問題). 最后一個參數(shù)是一個回調(diào)函數(shù), 它將在每次INI指令的值發(fā)生變化時被調(diào)用. 你將在修改事件一節(jié)看到這個參數(shù)的細(xì)節(jié).
譯注: 如果你和譯者一樣遇到結(jié)果和原著結(jié)果預(yù)期不一致時, 請在測試時, 在你的MINIT()函數(shù)中增加一句"REGISTER_INI_ENTRIES();"調(diào)用, 并確保該調(diào)用在你的MINIT中分配全局空間之后執(zhí)行.
現(xiàn)在你的INI設(shè)置已經(jīng)定義, 只需要在你的打招呼函數(shù)中使用就可以了.
PHP_FUNCTION(sample4_hello_world) { const char *greeting = INI_STR("sample4.greeting"); php_printf("%s\n", greeting); }
一定要注意, char *的值是引擎所有的, 一定不要修改. 正因為這樣, 所以將你本地用來臨時存儲INI設(shè)置值的變量定義為const修飾. 當(dāng)然, 并不是所有的INI值都是字符串; 還有其他的宏用來獲取整型, 浮點型以及布爾型的值:
long lval = INI_INT("sample4.intval"); double dval = INI_FLT("sample4.fltval"); zend_bool bval = INI_BOOL("sample4.boolval");
立即學(xué)習(xí)“PHP免費學(xué)習(xí)筆記(深入)”;
通常你想要知道的是INI設(shè)置的當(dāng)前值; 不過, 作為補充, 存在幾個宏可以用來讀取未經(jīng)修改的INI設(shè)置值:
const char *strval = INI_ORIG_STR("sample4.stringval"); long lval = INI_ORIG_INT("sample4.intval"); double dval = INI_ORIG_FLT("sample4.fltval"); zend_bool bval = INI_ORIG_BOOL("sample4.boolval");
立即學(xué)習(xí)“PHP免費學(xué)習(xí)筆記(深入)”;
這個例子中, INI指令的名字"sample4.greeting"增加了擴展名作為前綴, 這樣來保證不會和其他擴展暴露的INI指令名字沖突. 對于私有的擴展來說, 這個前綴不是必須的, 但是對于商業(yè)化或開源發(fā)布的公開擴展還是鼓勵這樣做的.
訪問級別
對于INI指令, 開始總是有一個默認(rèn)值. 多數(shù)情況下, 理想是保持默認(rèn)值不變; 然而, 對于某些特殊的環(huán)境或者腳本內(nèi)特定的動作, 這些值可能需要被修改. 如下表所示, INI指令的值可能在下面3個點被修改:
訪問級別 |
含義 |
SYSTEM |
位于php.ini中,或者apache的httpd.conf配置文件中 |
PERDIR |
位于Apache的httpd.conf配置文件中 |
USER |
一旦腳本開始執(zhí)行,就只能通過調(diào)用用戶空間函數(shù)ini_set()去修改INI設(shè)置了. |
某些設(shè)置如果可以在任何地方被修改就沒有多大意義了, 比如safe_mode, 如果可以在任何地方去修改, 那么惡意腳本的作者就可以很簡單的去禁用safe_mode, 接著去讀或修改本不允許操作的文件.
類似的, 某些非安全相關(guān)的指令比如register_globals或magic_quotes_gpc, 在腳本中不能被修改, 因為, 在腳本執(zhí)行時, 它所影響的事情已經(jīng)發(fā)生過了.
這些指令的訪問控制是通過PHP_INI_ENTRY()的第三個參數(shù)完成的. 在你前面例子中, 使用了PHP_INI_ALL, 它的定義是一個位域操作: PHP_INI_SYSTEM | PHP_INI_PERDIR | PHP_INI_USER.
對于register_globals和magic_quotes_gpc這樣的指令, 定義的訪問級別為PHP_INI_SYSTEM | PHP_INI_PERDIR. 排除了PHP_INI_USER將導(dǎo)致以這個名字調(diào)用ini_set()時最終會失敗.
現(xiàn)在, 你可能已經(jīng)猜到, safe_mode和open_basedir這樣的指令應(yīng)該僅被定義為PHP_INI_SYSTEM. 這樣的設(shè)置就確保了只有系統(tǒng)管理員可以修改這些值, 因為只有它們可以訪問修改php.ini或httpd.conf文件中的配置.
修改事件
當(dāng)INI指令被修改時, 無論是通過ini_set()函數(shù)還是某個perdir指令的處理, 引擎都會為其測試OnModify回調(diào). 修改處理器可以使用ZEND_INI_MH()宏定義, 并通過在OnModify參數(shù)上傳遞函數(shù)名附加到INI指令上:
ZEND_INI_MH(php_sample4_modify_greeting) { if (new_value_length == 0) { return FAILURE; } return SUCCESS; } PHP_INI_BEGIN() PHP_INI_ENTRY("sample4.greeting", "Hello World", PHP_INI_ALL, php_sample4_modify_greeting) PHP_INI_END()
通過在new_value_length為0時返回FAILURE, 這個修改處理器禁止將greeting設(shè)置為空字符串. ZEND_INI_MH()宏產(chǎn)生的整個原型如下:
int php_sample4_modify_greeting(zend_ini_entry *entry, char *new_value, uint new_value_length, void *mh_arg1, void *mh_arg2, void *mh_arg3, int stage TSRMLS_DC);
立即學(xué)習(xí)“PHP免費學(xué)習(xí)筆記(深入)”;
各個參數(shù)的含義見下表:
參數(shù)名 |
含義 |
entry |
指向引擎真實存儲的INI指令項.這個結(jié)構(gòu)體提供了當(dāng)前值,原始值,所屬模塊,以及其他一些下面代碼(zend_ini_entry結(jié)構(gòu)體結(jié)構(gòu))列出的信息 |
new_value |
要被設(shè)置的值.如果處理器返回SUCCESS,這個值將被設(shè)置到entry->value,同時如果entry->orig_value當(dāng)前沒有設(shè)置,則將當(dāng)前值設(shè)置到entry->orig_value中,并設(shè)置entry->modified標(biāo)記.這個字符串的長度通過new_value_length傳遞. |
mh_arg1, 2, 3 |
這3個指針對應(yīng)INI指令定義時給出的數(shù)據(jù)指針(zend_ini_entry結(jié)構(gòu)體中的3個同名成員).實際上,這幾個值是引擎內(nèi)部處理使用的,你不需要關(guān)心它們. |
stage |
ZEND_INI_STAGE_系列的5個值之一: STARTUP, SHUTDOWN, ACTIVATE, DEACTIVATE, RUNTIME. 這些常量對應(yīng)于MINIT, MSHUTDOWN, RINIT, RSHUTDOWN,以及活動腳本執(zhí)行. |
核心結(jié)構(gòu)體: zend_ini_entry
struct _zend_ini_entry { int module_number; int modifiable; char *name; uint name_length; ZEND_INI_MH((*on_modify)); void *mh_arg1; void *mh_arg2; void *mh_arg3; char *value; uint value_length; char *orig_value; uint orig_value_length; int modified; void ZEND_INI_DISP(*displayer); };
展示INI設(shè)置
在上一章, 你看到了MINFO函數(shù)以及相關(guān)的指令用于展示擴展的信息. 由于擴展暴露INI指令是很常見的, 因此引擎提供了一個公共的宏可以放置到PHP_MINFO_FUNCTION()中用于展示INI指令信息.
PHP_MINFO_FUNCTION(sample4) { DISPLAY_INI_ENTRIES(); }
立即學(xué)習(xí)“PHP免費學(xué)習(xí)筆記(深入)”;
這個宏將迭代PHP_INI_BEGIN()和PHP_INI_END()宏之間定義的INI指令集和, 在一個3列的表格中展示它們的INI指令名, 原始值(全局的), 以及當(dāng)前值(經(jīng)過PERDIR指令或ini_set()調(diào)用修改后)
默認(rèn)情況下, 所有的指令都直接以其字符串形式輸出. 對于某些指令, 比如布爾值以及用于語法高亮的顏色值, 則在展示處理時應(yīng)用了其他格式. 這些格式是通過每個INI設(shè)置的顯示處理器處理的, 它和你看到的OnModify一樣是一個動態(tài)的回調(diào)函數(shù)指針.
顯示處理器可以使用PHP_INI_ENTRY()宏的擴展版指定, 它接受一個額外的參數(shù). 如果設(shè)置為NULL, 則使用展示字符串值的處理器作為默認(rèn)處理器:
PHP_INI_ENTRY_EX("sample4.greeting", "Hello World", PHP_INI_ALL, php_sample4_modify_greeting, php_sample4_display_greeting)
立即學(xué)習(xí)“PHP免費學(xué)習(xí)筆記(深入)”;
顯然, 需要在INI設(shè)置定義之前聲明這個函數(shù). 和OnModify回調(diào)函數(shù)一樣, 這可以通過一個包裝宏以及少量編碼完成:
#include "SAPI.h" /* needed for sapi_module */ PHP_INI_DISP(php_sample4_display_greeting) { const char *value = ini_entry->value; /* 選擇合適的當(dāng)前值或原始值 */ if (type == ZEND_INI_DISPLAY_ORIG && ini_entry->modified) { value = ini_entry->orig_value; } /* 使得打招呼的字符串粗體顯示(當(dāng)以HTML方式輸出時) */ if (sapi_module.phpinfo_as_text) { php_printf("%s", value); } else { php_printf("<b>%s</b>", value); } }
立即學(xué)習(xí)“PHP免費學(xué)習(xí)筆記(深入)”;
綁定到擴展的全局空間
所有的INI指令都在Zend引擎內(nèi)有一塊存儲空間, 可以用以跟蹤腳本內(nèi)的變更并進行請求外部的全局設(shè)置維護. 在這塊存儲空間中, 所有的INI指令都以字符串值存儲. 你已經(jīng)知道了, 這些值可以使用INI_INT(), INI_FLT(), INI_BOOL()等宏函數(shù), 很簡單的翻譯成其他的標(biāo)量類型.
這個查找和轉(zhuǎn)換過程由于兩個原因非常低效: 首先, 每次一個INI的值在獲取時, 它必須通過名字在一個HashTable中進行定位. 這種查找方式對于僅在運行時編譯的用戶空間腳本而言是沒有問題的, 但是對于已編譯的機器代碼源, 運行時做這個工作就毫無意義.
每次請求標(biāo)量值的時候都需要將底層的字符串值轉(zhuǎn)換到標(biāo)量值是非常低效的. 因此我們使用你已經(jīng)學(xué)習(xí)過的線程安全全局空間作為存儲媒介, 每次INI指令值變更時更新它即可. 這樣, 所有訪問INI指令的代碼都只需要查找你的線程安全全局空間結(jié)構(gòu)體中的某個指針即可, 這樣就獲得了編譯期優(yōu)化的優(yōu)點.
在你的php_sample4.h文件MODULE_GLOBALS結(jié)構(gòu)體中增加const char *greeting; 接著更新sample4.c中的下面兩個方法:
ZEND_INI_MH(php_sample4_modify_greeting) { /* Disallow empty greetings */ if (new_value_length == 0) { return FAILURE; } SAMPLE4_G(greeting) = new_value; return SUCCESS; } PHP_FUNCTION(sample4_hello_world) { php_printf("%s\n", SAMPLE4_G(greeting)); }
由于這是對INI訪問的一種非常常見的優(yōu)化方式, 因此引擎暴露了一組專門處理INI指令到全局變量的綁定宏:
STD_PHP_INI_ENTRY_EX("sample4.greeting", "Hello World", PHP_INI_ALL, OnUpdateStringUnempty, greeting, zend_sample4_globals, sample4_globals, php_sample4_display_greeting)
這個宏執(zhí)行和上面你自己的php_sample4_modify_greeting相同的工作, 但它不需要OnModify回調(diào). 取而代之的是, 它使用了一個泛化的修改回調(diào)OnUpdateStringUnempty, 以及信息應(yīng)該存儲的空間. 如果要允許空的greeting指令值, 你可以直接指定OnUpdateString替代OnUpdateStringUnempty.
類似的, INI指令也可以綁定long, double, zend_bool的標(biāo)量值. 在你的php_sample4.h中MODULE_GLOBALS結(jié)構(gòu)體上增加幾個字段:
long mylong; double mydouble; zend_bool mybool;
現(xiàn)在在你的PHP_INI_BEGIN()/PHP_INI_END()代碼塊中使用STD_PHP_INI_ENTRY()宏創(chuàng)建新的INI指令, 它和對應(yīng)的_EX版本的宏的區(qū)別只是顯示處理器以及綁定到的值不同.
STD_PHP_INI_ENTRY("sample4.longval", "123", PHP_INI_ALL, OnUpdateLong, mylong, zend_sample4_globals, sample4_globals) STD_PHP_INI_ENTRY("sample4.doubleval", "123.456", PHP_INI_ALL, OnUpdateDouble, mydouble, zend_sample4_globals, sample4_globals) STD_PHP_INI_ENTRY("sample4.boolval", "1", PHP_INI_ALL, OnUpdateBool, mybool, zend_sample4_globals, sample4_globals)
立即學(xué)習(xí)“PHP免費學(xué)習(xí)筆記(深入)”;
這里要注意, 如果調(diào)用了DISPLAY_INI_ENTRIES(), 布爾類型的INI指令"sample4.boolval"將和其他設(shè)置一樣, 被顯示為它的字符串值; 然而, 首選的布爾值指令應(yīng)該被顯示為"on"或"off". 要使用這些更加表意的顯示, 你可以使用STD_PHP_INI_ENTRY_EX()宏并創(chuàng)建顯示處理器, 或者使用另外一個宏:
STD_PHP_INI_BOOLEAN("sample4.boolval", "1", PHP_INI_ALL, OnUpdateBool, mybool, zend_sample4_globals *, sample4_globals)
立即學(xué)習(xí)“PHP免費學(xué)習(xí)筆記(深入)”;
這個特定類型的宏是布爾類型特有的, 它提供的是將布爾值轉(zhuǎn)換為"on"/"off"值的顯示處理器.
小結(jié)
在本章, 你了解了php語言中最古老的特性之一的實現(xiàn), 它也是阻礙php可移植的罪魁. 對于每個新的INI設(shè)置, 都會使得編寫可移植代碼變得更加復(fù)雜. 使用這些特性要非常慎重, 因為擴展以后時鐘都要使用它了. 并且, 在使用時要注意不同系統(tǒng)間的行為一致性, 以免在維護時出現(xiàn)不可預(yù)期的狀況.
接下來的三張, 我們將深入到流API, 開始使用流的實現(xiàn)層和包裝操作, 上下文, 過濾器等.
以上就是[翻譯][php擴展開發(fā)和嵌入式]第13章-php的INI設(shè)置?的內(nèi)容,更多相關(guān)內(nèi)容請關(guān)注PHP中文網(wǎng)(www.miracleart.cn)!
PHP怎么學(xué)習(xí)?PHP怎么入門?PHP在哪學(xué)?PHP怎么學(xué)才快?不用擔(dān)心,這里為大家提供了PHP速學(xué)教程(入門到精通),有需要的小伙伴保存下載就能學(xué)習(xí)啦!
微信掃碼
關(guān)注PHP中文網(wǎng)服務(wù)號
QQ掃碼
加入技術(shù)交流群
Copyright 2014-2025 http://www.miracleart.cn/ All Rights Reserved | php.cn | 湘ICP備2023035733號