In-depth understanding of ini configuration in php (2), in-depth understanding of ini
Continue to write from the previous article.
1, change the configuration at runtime
As mentioned in the previous article, The ini_set function can dynamically modify some configurations of php during the execution of php. Note that not all configurations can be dynamically modified. Regarding the modifiable ini configuration, see: http://php.net/manual/zh/configuration.changes.modes.php
We go directly to the implementation of ini_set. Although the function is a bit long, the logic is very clear:
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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | <span>PHP_FUNCTION( ini_set )
{
</span><span>char</span> *varname, *<span>new_value;
</span><span>int</span><span> varname_len, new_value_len;
</span><span>char</span> *<span>old_value;
</span><span> if </span> (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, <span> "</span><span>ss</span><span>" </span>, &varname, &varname_len, &new_value, &new_value_len) ==<span> FAILURE) {
</span><span> return </span><span>;
}
</span><span>
old_value = zend_ini_string(varname, varname_len + <span>1</span>, <span>0</span><span>);
</span><span> </span>
<span> if </span><span> (old_value) {
RETVAL_STRING(old_value, </span><span>1</span><span>);
} </span><span> else </span><span> {
RETVAL_FALSE;
}
</span><span>
<span>#define</span> _CHECK_PATH( var , var_len, ini) php_ini_check_path( var , var_len, ini, sizeof(ini))
<span> </span>
<span> if </span> (PG(safe_mode) ||<span> PG(open_basedir)) {
</span><span> if </span> (_CHECK_PATH(varname, varname_len, <span> "</span><span>error_log</span><span>" </span>) ||<span>
_CHECK_PATH(varname, varname_len, </span><span> "</span><span>java.class.path</span><span>" </span>) ||<span>
_CHECK_PATH(varname, varname_len, </span><span> "</span><span>java.home</span><span>" </span>) ||<span>
_CHECK_PATH(varname, varname_len, </span><span> "</span><span>mail.log</span><span>" </span>) ||<span>
_CHECK_PATH(varname, varname_len, </span><span> "</span><span>java.library.path</span><span>" </span>) ||<span>
_CHECK_PATH(varname, varname_len, </span><span> "</span><span>vpopmail.directory</span><span>" </span><span>)) {
</span><span> if </span> (PG(safe_mode) && (!<span>php_checkuid(new_value, NULL, CHECKUID_CHECK_FILE_AND_DIR))) {
zval_dtor(return_value);
RETURN_FALSE;
}
</span><span> if </span><span> (php_check_open_basedir(new_value TSRMLS_CC)) {
zval_dtor(return_value);
RETURN_FALSE;
}
}
}
</span><span>
<span> if </span><span> (PG(safe_mode)) {
</span><span> if </span> (! strncmp (<span> "</span><span>max_execution_time</span><span>" </span>, varname, <span>sizeof</span>(<span> "</span><span>max_execution_time</span><span>" </span>)) ||
! strncmp (<span> "</span><span>memory_limit</span><span>" </span>, varname, <span>sizeof</span>(<span> "</span><span>memory_limit</span><span>" </span>)) ||
! strncmp (<span> "</span><span>child_terminate</span><span>" </span>, varname, <span>sizeof</span>(<span> "</span><span>child_terminate</span><span>" </span><span>))
) {
zval_dtor(return_value);
RETURN_FALSE;
}
}
</span><span>
<span> if </span> (zend_alter_ini_entry_ex(varname, varname_len + <span>1</span>, new_value, new_value_len, PHP_INI_USER, PHP_INI_STAGE_RUNTIME, <span>0</span> TSRMLS_CC) ==<span> FAILURE) {
zval_dtor(return_value);
RETURN_FALSE;
}
}</span>
|
Copy after login
As you can see, in addition to some necessary verification work, the main thing is to call zend_alter_ini_entry_ex.
We continue to follow up in the zend_alter_ini_entry_ex function:
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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | ZEND_API <span>int zend_alter_ini_entry_ex(<span>char *name, <span>uint name_length, <span>char *new_value, <span>uint new_value_length, <span>int modify_type, <span>int stage, <span>int force_change TSRMLS_DC) <span> <span>
{
zend_ini_entry *<span>ini_entry;
<span>char *<span>duplicate;
zend_bool modifiable;
zend_bool modified;
<span>
<span> if (zend_hash_find(EG(ini_directives), name, name_length, (<span>void **) &ini_entry) ==<span> FAILURE) {
<span> return <span> FAILURE;
}
<span>
modifiable = ini_entry-><span>modifiable;
modified = ini_entry-><span>modified;
<span> if (stage == ZEND_INI_STAGE_ACTIVATE && modify_type ==<span> ZEND_INI_SYSTEM) {
ini_entry->modifiable =<span> ZEND_INI_SYSTEM;
}
<span>
<span> if (!<span>force_change) {
<span> if (!(ini_entry->modifiable &<span> modify_type)) {
<span> return <span> FAILURE;
}
}
<span>
<span> if (!<span>EG(modified_ini_directives)) {
ALLOC_HASHTABLE(EG(modified_ini_directives));
zend_hash_init(EG(modified_ini_directives), <span>8, NULL, NULL, <span>0<span>);
}
<span>
ini_entry->orig_value = ini_entry-><span>value;
ini_entry->orig_value_length = ini_entry-><span>value_length;
ini_entry->orig_modifiable =<span> modifiable;
ini_entry->modified = <span>1<span>;
zend_hash_add(EG(modified_ini_directives), name, name_length, &ini_entry, <span>sizeof(zend_ini_entry*<span>), NULL);
}
duplicate =<span> estrndup(new_value, new_value_length);
<span>
<span> if (!ini_entry->on_modify || ini_entry->on_modify(ini_entry, duplicate, new_value_length, ini_entry->mh_arg1, ini_entry->mh_arg2, ini_entry->mh_arg3, stage TSRMLS_CC) ==<span> SUCCESS) {
<span>
<span> if (modified && ini_entry->orig_value != ini_entry->value) {<span>
efree(ini_entry-><span>value);
}
ini_entry->value =<span> duplicate;
ini_entry->value_length =<span> new_value_length;
} <span> else <span> {
efree(duplicate);
<span> return <span> FAILURE;
}
<span> return <span> SUCCESS;
}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
|
Copy after login
There are 3 logics that we need to understand carefully:
1) The modified field in ini_entry is used to indicate whether the configuration has been dynamically modified. Once the ini configuration is modified, modified will be set to 1. There is a crucial section in the above code:
1 2 3 4 5 6 7 8 | <span>
<span> if </span> (!<span>modified) {
ini_entry</span>->orig_value = ini_entry-><span>value;
ini_entry</span>->orig_value_length = ini_entry-><span>value_length;
ini_entry</span>->orig_modifiable =<span> modifiable;
ini_entry</span>->modified = <span>1</span><span>;
zend_hash_add(EG(modified_ini_directives), name, name_length, </span>&ini_entry, <span>sizeof</span>(zend_ini_entry*<span>), NULL);
}</span>
|
Copy after login
This code means that no matter how many times we call ini_set in the php code, only the first ini_set will enter this logic and set the orig_value. Starting from the second call to ini_set, this branch will not be executed again, because modified at this time has been set to 1. Therefore, ini_entry->orig_value always saves the configuration value before the first modification (that is, the most original configuration).
2) In order to make the configuration modified by ini_set take effect immediately, the on_modify callback function is required.
As mentioned in the previous article, on_modify is called to be able to update the global variables of the module. Recall again, first of all, the configuration in the module global variables is no longer of string type. Use bool when it should use bool, and int when it should use int. Secondly, each ini_entry stores the address of the module's global variable and the corresponding offset, so that on_modify can quickly modify the memory. In addition, don't forget that after on_modify is called, ini_entry->value still needs to be further updated so that the configuration value in EG (ini_directives) is the latest.
3) A new hash table appears here, EG (modified_ini_directives).
EG (modified_ini_directives) is only used to store dynamically modified ini configurations. If an ini configuration is dynamically modified, then it exists in both EG (ini_directives) and EG (modified_ini_directives). Since each ini_entry is marked with a modified field, isn't it possible to traverse EG (ini_directives) to obtain all modified configurations?
The answer is yes. Personally, I feel that the EG (modified_ini_directives) here is mainly to improve performance. It is enough to directly traverse the EG (modified_ini_directives). In addition, by deferring the initialization of EG (modified_ini_directives) to zend_alter_ini_entry_ex, you can also see the performance optimization points of PHP in details.
2, restore configuration
The action time of ini_set is different from the action time of php.ini file. Once the request execution is completed, ini_set will become invalid. In addition, when the ini_restore function is called in our code, the configuration previously set through ini_set will also become invalid.
After each php request is executed, php_request_shutdown will be triggered. It and php_request_startup are two corresponding processes. If php is hooked under apache/nginx, php_request_shutdown will be called every time an http request is processed; if php is run in CLI mode, php_request_shutdown will also be called after the script is executed.
In php_request_shutdown, we can see the recovery processing for ini:
1 2 | <span> </span><span>
zend_deactivate(TSRMLS_C);</span>
|
Copy after login
Enter zend_deactivate, you can further see that the zend_ini_deactivate function is called, and zend_ini_deactivate is responsible for restoring the php configuration.
1 2 3 | <span>zend_try {
zend_ini_deactivate(TSRMLS_C);
} zend_end_try();</span>
|
Copy after login
Let’s take a closer look at the implementation of zend_ini_deactivate:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | ZEND_API <span>int</span> zend_ini_deactivate(TSRMLS_D) <span> </span><span>
{
</span><span> if </span><span> (EG(modified_ini_directives)) {
</span><span>
</span><span>
<span> zend_hash_apply(EG(modified_ini_directives), (apply_func_t) zend_restore_ini_entry_wrapper TSRMLS_CC);
</span><span>
<span> zend_hash_destroy(EG(modified_ini_directives));
FREE_HASHTABLE(EG(modified_ini_directives));
EG(modified_ini_directives) </span>=<span> NULL;
}
</span><span> return </span><span> SUCCESS;
}</span>
|
Copy after login
From zend_hash_apply, the real task of restoring ini finally falls to the zend_restore_ini_entry_wrapper callback function.
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 29 30 31 32 33 34 35 36 37 38 | <span> static </span> <span>int</span> zend_restore_ini_entry_wrapper(zend_ini_entry **<span>ini_entry TSRMLS_DC)
{
</span><span>
zend_restore_ini_entry_cb(*<span>ini_entry, ZEND_INI_STAGE_DEACTIVATE TSRMLS_CC);
</span><span> return </span> <span>1</span><span>;
}
</span><span> static </span> <span>int</span> zend_restore_ini_entry_cb(zend_ini_entry *ini_entry, <span>int</span><span> stage TSRMLS_DC)
{
</span><span>int</span> result =<span> FAILURE;
</span><span>
<span> if </span> (ini_entry-><span>modified) {
</span><span> if </span> (ini_entry-><span>on_modify) {
</span><span>
<span> zend_try {
result </span>= ini_entry->on_modify(ini_entry, ini_entry->orig_value, ini_entry->orig_value_length, ini_entry->mh_arg1, ini_entry->mh_arg2, ini_entry-><span>mh_arg3, stage TSRMLS_CC);
} zend_end_try();
}
</span><span> if </span> (stage == ZEND_INI_STAGE_RUNTIME && result ==<span> FAILURE) {
</span><span> </span>
<span> return </span> <span>1</span><span>;
}
</span><span> if </span> (ini_entry->value != ini_entry-><span>orig_value) {
efree(ini_entry</span>-><span>value);
}
</span><span>
ini_entry->value = ini_entry-><span>orig_value;
ini_entry</span>->value_length = ini_entry-><span>orig_value_length;
ini_entry</span>->modifiable = ini_entry-><span>orig_modifiable;
ini_entry</span>->modified = <span>0</span><span>;
ini_entry</span>->orig_value =<span> NULL;
ini_entry</span>->orig_value_length = <span>0</span><span>;
ini_entry</span>->orig_modifiable = <span>0</span><span>;
}
</span><span> return </span> <span>0</span><span>;
}</span>
|
Copy after login
The logic is quite clear, I believe readers can understand it. To summarize the recovery process of ini configuration:
php_request_shutdown--->zend_deactivate--->zend_ini_deactivate--->zend_restore_ini_entry_wrapper--->zend_restore_ini_entry_cb
3. Destruction of configuration
At the end of the sapi life cycle, such as apache closing, cli program execution, etc. Once entering this stage, the previously mentioned configuration_hash, EG (ini_directives), etc. need to be destroyed, and the memory space used by them needs to be released.
1. PHP will end all modules in sequence and call UNREGISTER_INI_ENTRIES in PHP_MSHUTDOWN_FUNCTION of each module. UNREGISTER_INI_ENTRIES corresponds to REGISTER_INI_ENTRIES, but UNREGISTER_INI_ENTRIES is not responsible for releasing the global space of the module. The memory of XXX_globals is placed in the static data area and does not need to be manually recycled.
UNREGISTER_INI_ENTRIES主要做的事情,是将某个模块的ini_entry配置从EG(ini_directives)表中删除。删除之后,ini_entry本身的空间会被回收,但是ini_entry->value不一定会被回收。
当所有模块的PHP_MSHUTDOWN_FUNCTION都调用UNREGISTER_INI_ENTRIES一遍之后,EG(ini_directives)中只剩下了Core模块的ini配置。此时,就需要手动调用UNREGISTER_INI_ENTRIES,来完成对Core模块配置的删除工作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <span>void</span><span> php_module_shutdown(TSRMLS_D)
{
...
</span><span>
</span><span>
<span> zend_shutdown(TSRMLS_C);
...
</span><span>
</span><span>
<span> UNREGISTER_INI_ENTRIES();
</span><span>/</span><span>/ 回收configuration_hash</span><span>
php_shutdown_config();
<br /></span>
|
Copy after login
1 2 3 | <span> <span>/<span>/ 回收EG(ini_directives)<br /> </span></span>zend_ini_shutdown(TSRMLS_C);<br />
...
}</span>
|
Copy after login
当手动调用UNREGISTER_INI_ENTRIES完成之后,EG(ini_directives)已经不包含任何的元素,理论上讲,此时的EG(ini_directives)是一张空的hash表。
2,configuration_hash的回收发生在EG(ini_directives)之后,上面贴出的代码中有关于php_shutdown_config的函数调用。php_shutdown_config主要负责回收configuration_hash。
1 2 3 4 5 6 7 8 9 | <span>int</span> php_shutdown_config(<span>void</span><span>)
{
</span><span>
zend_hash_destroy(&<span>configuration_hash);
...
</span><span> return </span><span> SUCCESS;
}</span>
|
Copy after login
注意zend_hash_destroy并不会释放configuration_hash本身的空间,同XXX_G访问的模块全局空间一样,configuration_hash也是一个全局变量,无需手动回收。
3,当php_shutdown_config完成时,只剩下EG(ini_directives)的自身空间还没被释放。因此最后一步调用zend_ini_shutdown。zend_ini_shutdown用于释放EG(ini_directives)。在前文已经提到,此时的EG(ini_directives)理论上是一张空的hash表,因此该HashTable本身所占用的空间需要被释放。
1 2 3 4 5 6 7 | ZEND_API <span>int</span><span> zend_ini_shutdown(TSRMLS_D)
{
</span><span>
<span> zend_hash_destroy(EG(ini_directives));
free(EG(ini_directives));
</span><span> return </span><span> SUCCESS;
}</span>
|
Copy after login
4,总结
用一张图大致描述一下和ini配置相关的流程:

直接编辑php目录下的php.ini文件即可
有一个“register_globals = Off”值,这个值是用来打开全局变量的,比如表单送过来的值,如果这个值设为“Off”,就只能用“$_POST['变量名']、$_GET['变量名 ']”等来取得送过来的值,如果设为“On”,就可以直接使用“$变量名”来获取送过来的值,当然,设为“Off”就比较安全,不会让人轻易将网页间传送 的数据截取。这个值是否改成“On”就看自己感觉了,是安全重要还是方便重要?
要用mysql,就要把“;extension= php_mysql.dll”前的“;”去掉。所有的模块文件都放在php解压缩目录的“ext”之下,用什么就把前面的“;”去掉就行了。
Do you think this meets your requirements?
';// Traverse all keys foreach($keys[0] as $key) { // If the value is OK and does not exist in the second ini file, display ok if($values[0][$key]==='OK' AND !isset($values[1 ][$key])) { echo ''; echo ' ',$key,''; echo '>
http://www.bkjia.com/PHPjc/893204.htmlwww.bkjia.comtruehttp: //www.bkjia.com/PHPjc/893204.htmlTechArticleIn-depth understanding of ini configuration in php (2), in-depth understanding of ini, continue to write from the previous article. 1. Change the configuration during runtime. As mentioned in the previous article, the ini_set function can be used during the execution of PHP...