php7扩展声明与获取ini配置
今天在开发PHP扩展中遇到了获取ini配置的需求,采用如下的方法获取发现得到的是空值:
1 2 3 4 5 6 7 8 9 |
static inline String ini_get(String varname) { char *value = zend_ini_string((char *) varname.c_str(), (uint) varname.length(), 0); if (!value) { return ""; } return value; } |
在ini文件中我是这样配置的:
1 2 |
[catx] catx.abc = "hello" |
经过谷歌搜索,发现了这篇博客介绍了INI的使用方式,发现ini是需要先声明才能获取的。
通常的用法,是先通过下面的宏定义一个INI数组:
1 2 3 |
ZEND_INI_BEGIN() ZEND_INI_ENTRY("catx.abc", "hi", PHP_INI_ALL, NULL) ZEND_INI_END() |
通过查看Zend源码,其对应展开如下:
1 2 3 4 5 6 7 8 9 10 11 12 |
#define ZEND_INI_BEGIN() static const zend_ini_entry_def ini_entries[] = { #define ZEND_INI_ENTRY(name, default_value, modifiable, on_modify) \ ZEND_INI_ENTRY_EX(name, default_value, modifiable, on_modify, NULL) #define ZEND_INI_ENTRY_EX(name, default_value, modifiable, on_modify, displayer) \ ZEND_INI_ENTRY3_EX(name, default_value, modifiable, on_modify, NULL, NULL, NULL, displayer) #define ZEND_INI_ENTRY3_EX(name, default_value, modifiable, on_modify, arg1, arg2, arg3, displayer) \ { name, on_modify, arg1, arg2, arg3, default_value, displayer, modifiable, sizeof(name)-1, sizeof(default_value)-1 }, #define ZEND_INI_END() { NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0} }; |
其实就是定义了一个zend_ini_entry_def的数组,其名字固定叫做ini_entries,里面每一项是要注册的INI配置。
上面的宏定义,无论你是在函数里还是全局作用域定义都是可以的,但是还需要主动注册到zend里去,通常用下面的2个宏来搞定:
1 2 |
#define REGISTER_INI_ENTRIES() zend_register_ini_entries(ini_entries, module_number) #define UNREGISTER_INI_ENTRIES() zend_unregister_ini_entries(module_number) |
上述宏默认就会找ini_entries变量传进去,第二个参数module_number你可以在扩展的MINIT和MSHUTDOWN两个函数的回调参数里得到,所以REGISTER_INI_ENTRIES要在MINIT里调用,后者在MSHUTDOWN里调用用于销毁注册的INI配置。
再就是关注一下zend_ini_entry_def的定义:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
typedef struct _zend_ini_entry_def { const char *name; ZEND_INI_MH((*on_modify)); void *mh_arg1; 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; |
name是ini配置的key,value是默认值(也就是ini里没配置时候的值),mh_arg1-3是用户可以指定的上下文参数(主要是用来displayer和on_modify回调时候提供上下文),modifiable是限制是否可以通过ini_set来修改ini配置,name_length是name的长度,value_length是value的长度。
on_modify和displayer没必要用,通过php代码ini_set修改变量可以直接反应到zend_ini_string的结果中,这些回调函数只是给你一个主动通知机制,一般是用不到的。
因为PHP-X项目是C++封装的Zend api,所以没法直接用上面的宏来搞定这些事情,因此需要绕过宏直接与Zend api交互:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
// INI struct IniEntry { std::string name; std::string default_value; int modifiable; }; // modifiable can be one of these:PHP_INI_SYSTEM/PHP_INI_PERDIR/PHP_INI_USER/PHP_INI_ALL void addIniEntry(const char* name, const char* default_value = "", int modifiable = PHP_INI_ALL) { IniEntry entry; entry.name = name; entry.default_value = default_value; entry.modifiable = modifiable; ini_entries.push_back(entry); } protected: std::vector<IniEntry> ini_entries; |
通过上述接口,允许用户添加若干ini配置,当MINIT回调的时候一股脑给它注册上去:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
void Extension::registerIniEntries(int module_number) { if (!ini_entries.size()) { return; } zend_ini_entry_def* entry_defs = new zend_ini_entry_def[ini_entries.size() + 1]; for (auto i = 0; i < ini_entries.size(); ++i) { IniEntry& entry = ini_entries[i]; zend_ini_entry_def def = { entry.name.c_str(), // name NULL, // on_modify NULL, // mh_arg1 NULL, // mh_arg2 NULL, // mh_arg3 entry.default_value.c_str(), // value NULL, // displayer entry.modifiable, // modifiable (uint)entry.name.size(), // name_length (uint)entry.default_value.size(), // value_length }; entry_defs[i] = def; } memset(entry_defs + ini_entries.size(), 0, sizeof(*entry_defs)); zend_register_ini_entries(entry_defs, module_number); delete []entry_defs; } |
当MSHUTDOWN的时候一键卸载:
1 2 3 4 5 |
void Extension::unregisterIniEntries(int module_number) { if (ini_entries.size()) { zend_unregister_ini_entries(module_number); } } |
这样就搞定ini了~
后续会陆续把PHP扩展开发中的一些知识点补充到博客上来。
另外教大家个办法,如果发现Zend api不熟悉、没有博客、没有文档说明的话,可以去github上搜api的名字(会罗列出所有使用该api的项目),或者去php源码的ext目录下看其他成熟扩展是怎么用的,屡试不爽。
如果文章帮助您解决了工作难题,您可以帮我点击屏幕上的任意广告,或者赞助少量费用来支持我的持续创作,谢谢~
