[Translation][php extension development and embedded] Chapter 13-php INI settings

黄舟
Release: 2023-03-05 16:30:02
Original
981 people have browsed it


INI setting

and the previous one Like the superglobal variables and persistent constants you saw in Chapter 1, php.ini values must be defined in the extended MINIT block. However, unlike other features, the definition of INI options consists only of simple start/stop lines. .

PHP_MINIT_FUNCTION(sample4) { REGISTER_INI_ENTRIES(); return SUCCESS; } PHP_MSHUTDOWN_FUNCTION(sample4) { UNREGISTER_INI_ENTRIES(); return SUCCESS; }
Copy after login

Define and access INI settings

The INI instruction itself is above the MINIT function in the source code file, use The following macros are defined completely independently. One or more INI instructions can be defined between these two macros:

PHP_INI_BEIGN() PHP_INI_END()
Copy after login

These two macro functions are similar to ZEND_BEGIN_MODULE_GLOBALS()/ZEND_END_MODULE_GLOBALS(). However, this is not a typdef structure, but a framework organization for the definition of static data instances:

static zend_ini_entry ini_entries[] = { {0,0,NULL,0,NULL,NULL,NULL,NULL,NULL,0,NULL,0,0,NULL} };
Copy after login

As you can see, it defines a vector of zend_ini_entry values, ending with an empty record. This is the same as the static you saw earlier The definition of vector function_entry is consistent.

##Simple INI settings

Now, you already have an INI structure for It is used to define INI instructions and the mechanism for engine registration/uninstallation of INI settings, so we can actually define some INI instructions for your extension. Suppose your extension exposes a greeting function, just like Chapter 5 "Your The same as in the first extension", but you can customize it if you want to say hello:

PHP_FUNCTION(sample4_hello_world) { php_printf("Hello World!\n"); }
Copy after login

The simplest and most direct way is to define an INI command and give it a default value "Hello world!":

##

#include "php_ini.h" PHP_INI_BEGIN() PHP_INI_ENTRY("sample4.greeting", "Hello World", PHP_INI_ALL, NULL) PHP_INI_END()
Copy after login

As you may have guessed, the first two parameters of this macro Indicates the name of the INI instruction and its default value. The third parameter is used to determine whether the engine allows this INI instruction to be modified (this will involve the access level issue introduced later in this chapter). The last parameter is a callback function, which Will be called every time the value of the INI directive changes. You will see the details of this parameter in the Modification Events section.

Translation Note: If you and the translator When you encounter results that are inconsistent with the expected results of the original work, please add a "REGISTER_INI_ENTRIES();" call to your MINIT() function during testing, and make sure that the call is executed after allocating global space in your MINIT.

Now that your INI settings have been defined, you just need to use them in your greeting function.

PHP_FUNCTION(sample4_hello_world) { const char *greeting = INI_STR("sample4.greeting"); php_printf("%s\n", greeting); }
Copy after login

Be sure Note that the value of char * is owned by the engine and must not be modified. Because of this, define your local variable used to temporarily store INI setting values as const. Of course, not all INI values are strings; There are other macros for getting integer, floating point and boolean values:

long lval = INI_INT("sample4.intval"); double dval = INI_FLT("sample4.fltval"); zend_bool bval = INI_BOOL("sample4.boolval");
Copy after login

Usually what you want to know is the current value of the INI setting; however, as a supplement, there are several macros that can be used to read the unmodified INI setting value:

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");
Copy after login

In this example, the name of the INI instruction "sample4.greeting" adds the extension name as a prefix to ensure that it will not conflict with the INI instruction names exposed by other extensions. For private extensions , this prefix is not required, but is encouraged for public extensions for commercial or open source releases.

Access Level

For INI instructions, there is always a default value to start with. In most cases, the ideal is to leave the default value unchanged; however, for some special circumstances or specific actions within the script, these values may Needs to be modified. As shown in the following table, the value of the INI instruction may be modified at the following three points:



某些设置如果可以在任何地方被修改就没有多大意义了, 比如safe_mode, 如果可以在任何地方去修改, 那么恶意脚本的作者就可以很简单的去禁用safe_mode, 接着去读或修改本不允许操作的文件.

类似的, 某些非安全相关的指令比如register_globals或magic_quotes_gpc, 在脚本中不能被修改, 因为, 在脚本执行时, 它所影响的事情已经发生过了.

这些指令的访问控制是通过PHP_INI_ENTRY()的第三个参数完成的. 在你前面例子中, 使用了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将导致以这个名字调用ini_set()时最终会失败.

现在, 你可能已经猜到, safe_mode和open_basedir这样的指令应该仅被定义为PHP_INI_SYSTEM. 这样的设置就确保了只有系统管理员可以修改这些值, 因为只有它们可以访问修改php.ini或httpd.conf文件中的配置.

修改事件

当INI指令被修改时, 无论是通过ini_set()函数还是某个perdir指令的处理, 引擎都会为其测试OnModify回调. 修改处理器可以使用ZEND_INI_MH()宏定义, 并通过在OnModify参数上传递函数名附加到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()
Copy after login

通过在new_value_length为0时返回FAILURE, 这个修改处理器禁止将greeting设置为空字符串. ZEND_INI_MH()宏产生的整个原型如下:

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);
Copy after login

各个参数的含义见下表:


Access Level

Meaning

SYSTEM

is located inphp.ini#,orin thehttpd.confconfiguration file ofapache and External directive,affects the startup phase of the engine,can be considered asINI#"global"Value

.

##PERDIR

###

is located in thehttpd.confconfiguration file ofApache and in the directive,or the directory or virtual directory where the request script is located The.htaccessfile under the host and otherapacheINI## set elsewhere before processing the request#Command.

##USER

Once the script starts executing

,can only be modified by calling the user space functionini_set()INIset.


核心结构体: 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); };
Copy after login

展示INI设置

在上一章, 你看到了MINFO函数以及相关的指令用于展示扩展的信息. 由于扩展暴露INI指令是很常见的, 因此引擎提供了一个公共的宏可以放置到PHP_MINFO_FUNCTION()中用于展示INI指令信息.

PHP_MINFO_FUNCTION(sample4) { DISPLAY_INI_ENTRIES(); }
Copy after login

这个宏将迭代PHP_INI_BEGIN()和PHP_INI_END()宏之间定义的INI指令集和, 在一个3列的表格中展示它们的INI指令名, 原始值(全局的), 以及当前值(经过PERDIR指令或ini_set()调用修改后)

默认情况下, 所有的指令都直接以其字符串形式输出. 对于某些指令, 比如布尔值以及用于语法高亮的颜色值, 则在展示处理时应用了其他格式. 这些格式是通过每个INI设置的显示处理器处理的, 它和你看到的OnModify一样是一个动态的回调函数指针.

显示处理器可以使用PHP_INI_ENTRY()宏的扩展版指定, 它接受一个额外的参数. 如果设置为NULL, 则使用展示字符串值的处理器作为默认处理器:

PHP_INI_ENTRY_EX("sample4.greeting", "Hello World", PHP_INI_ALL, php_sample4_modify_greeting, php_sample4_display_greeting)
Copy after login

显然, 需要在INI设置定义之前声明这个函数. 和OnModify回调函数一样, 这可以通过一个包装宏以及少量编码完成:

#include "SAPI.h" /* needed for sapi_module */ PHP_INI_DISP(php_sample4_display_greeting) { const char *value = ini_entry->value; /* 选择合适的当前值或原始值 */ if (type == ZEND_INI_DISPLAY_ORIG && ini_entry->modified) { value = ini_entry->orig_value; } /* 使得打招呼的字符串粗体显示(当以HTML方式输出时) */ if (sapi_module.phpinfo_as_text) { php_printf("%s", value); } else { php_printf("%s", value); } }
Copy after login

绑定到扩展的全局空间

所有的INI指令都在Zend引擎内有一块存储空间, 可以用以跟踪脚本内的变更并进行请求外部的全局设置维护. 在这块存储空间中, 所有的INI指令都以字符串值存储. 你已经知道了, 这些值可以使用INI_INT(), INI_FLT(), INI_BOOL()等宏函数, 很简单的翻译成其他的标量类型.

这个查找和转换过程由于两个原因非常低效: 首先, 每次一个INI的值在获取时, 它必须通过名字在一个HashTable中进行定位. 这种查找方式对于仅在运行时编译的用户空间脚本而言是没有问题的, 但是对于已编译的机器代码源, 运行时做这个工作就毫无意义.

每次请求标量值的时候都需要将底层的字符串值转换到标量值是非常低效的. 因此我们使用你已经学习过的线程安全全局空间作为存储媒介, 每次INI指令值变更时更新它即可. 这样, 所有访问INI指令的代码都只需要查找你的线程安全全局空间结构体中的某个指针即可, 这样就获得了编译期优化的优点.

在你的php_sample4.h文件MODULE_GLOBALS结构体中增加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)); }
Copy after login

由于这是对INI访问的一种非常常见的优化方式, 因此引擎暴露了一组专门处理INI指令到全局变量的绑定宏:

STD_PHP_INI_ENTRY_EX("sample4.greeting", "Hello World", PHP_INI_ALL, OnUpdateStringUnempty, greeting, zend_sample4_globals, sample4_globals, php_sample4_display_greeting)
Copy after login


这个宏执行和上面你自己的php_sample4_modify_greeting相同的工作, 但它不需要OnModify回调. 取而代之的是, 它使用了一个泛化的修改回调OnUpdateStringUnempty, 以及信息应该存储的空间. 如果要允许空的greeting指令值, 你可以直接指定OnUpdateString替代OnUpdateStringUnempty.

类似的, INI指令也可以绑定long, double, zend_bool的标量值. 在你的php_sample4.h中MODULE_GLOBALS结构体上增加几个字段:

long mylong; double mydouble; zend_bool mybool;
Copy after login

现在在你的PHP_INI_BEGIN()/PHP_INI_END()代码块中使用STD_PHP_INI_ENTRY()宏创建新的INI指令, 它和对应的_EX版本的宏的区别只是显示处理器以及绑定到的值不同.

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)
Copy after login

这里要注意, 如果调用了DISPLAY_INI_ENTRIES(), 布尔类型的INI指令"sample4.boolval"将和其他设置一样, 被显示为它的字符串值; 然而, 首选的布尔值指令应该被显示为"on"或"off". 要使用这些更加表意的显示, 你可以使用STD_PHP_INI_ENTRY_EX()宏并创建显示处理器, 或者使用另外一个宏:

STD_PHP_INI_BOOLEAN("sample4.boolval", "1", PHP_INI_ALL, OnUpdateBool, mybool, zend_sample4_globals *, sample4_globals)
Copy after login

这个特定类型的宏是布尔类型特有的, 它提供的是将布尔值转换为"on"/"off"值的显示处理器.

小结

在本章, 你了解了php语言中最古老的特性之一的实现, 它也是阻碍php可移植的罪魁. 对于每个新的INI设置, 都会使得编写可移植代码变得更加复杂. 使用这些特性要非常慎重, 因为扩展以后时钟都要使用它了. 并且, 在使用时要注意不同系统间的行为一致性, 以免在维护时出现不可预期的状况.

接下来的三张, 我们将深入到流API, 开始使用流的实现层和包装操作, 上下文, 过滤器等.

以上就是[翻译][php扩展开发和嵌入式]第13章-php的INI设置 的内容,更多相关内容请关注PHP中文网(m.sbmmt.com)!


Related labels:
php
source:php.cn
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template
About us Disclaimer Sitemap
php.cn:Public welfare online PHP training,Help PHP learners grow quickly!

参数名

含义

entry

Points to the actual storage of the engineINIInstruction item.This structure provides Current value,original value,belonging module,and some other codes below(zend_ini_entrystructure structure)Listed information

new_value

The value to be set.If the processor returnsSUCCESS,This value will be set toentry->value,At the same time ifentry->orig_valueis currently not set,will set the current value toentry->orig_value,and setentry->modifiedmarker.The length of this string is passednew_value_lengthPass.

##mh_arg1, 2, 3

These3pointers correspond to the data pointers given when theINIinstruction is defined(zend_ini_entryThe3members with the same name).In fact,These values are used by the engine for internal processing,You don’t need to care about them.

##stage

##ZEND_INI_STAGE_

series One of5values: STARTUP, SHUTDOWN, ACTIVATE, DEACTIVATE, RUNTIME.These constants correspond toMINIT, MSHUTDOWN, RINIT, RSHUTDOWN,and active script execution.