EG(modified_ini_directives)只用于存放被动态修改过的ini配置,如果一个ini配置被动态修改过,那么它既存在于EG(ini_directives)中,又存在于EG(modified_ini_directives)中。既然每一个ini_entry都有modified字段做标记,那岂不是可以遍历EG(ini_directives)来获得所有被修改过的配置呢?
答案是肯定的。个人觉得,这里的EG(modified_ini_directives)主要还是为了提升性能,酱直接遍历EG(modified_ini_directives)就足够了。此外,把EG(modified_ini_directives)的初始化推迟到zend_alter_ini_entry_ex中,也可以看出php在细节上的性能优化点。
2,恢复配置
ini_set的作用时间和php.ini文件的作用时间是不一样的,一旦请求执行结束,则ini_set会失效。此外,当我们代码中调用了ini_restore函数,则之前通过ini_set设置的配置也会失效。
每一个php请求执行完毕之后,会触发php_request_shutdown,它和php_request_startup是两个相对应过程。如果php是挂接在apache/nginx下,则每处理完一个http请求,就会调用php_request_shutdown;如果php以CLI模式来运行,则脚本执行完毕之后,也会调用php_request_shutdown。
在php_request_shutdown中,我们可以看到针对ini的恢复处理:
复制代码 代码如下:
/* 7. Shutdown scanner/executor/compiler and restore ini entries */
zend_deactivate(TSRMLS_C);
进入zend_deactivate,可以进一步看到调用了zend_ini_deactivate函数,由zend_ini_deactivate来负责将php的配置进行恢复。
复制代码 代码如下:
zend_try {
zend_ini_deactivate(TSRMLS_C);
} zend_end_try();
具体来看看zend_ini_deactivate的实现:
复制代码 代码如下:
ZEND_API int zend_ini_deactivate(TSRMLS_D) /* {{{ */
{
if (EG(modified_ini_directives)) {
// 遍历EG(modified_ini_directives)中这张表
// 对每一个ini_entry调用zend_restore_ini_entry_wrapper
zend_hash_apply(EG(modified_ini_directives), (apply_func_t) zend_restore_ini_entry_wrapper TSRMLS_CC);
// 回收操作
zend_hash_destroy(EG(modified_ini_directives));
FREE_HASHTABLE(EG(modified_ini_directives));
EG(modified_ini_directives) = NULL;
}
return SUCCESS;
}
从zend_hash_apply来看,真正恢复ini的任务最终落地到了zend_restore_ini_entry_wrapper回调函数。
复制代码 代码如下:
static int zend_restore_ini_entry_wrapper(zend_ini_entry **ini_entry TSRMLS_DC)
{
// zend_restore_ini_entry_wrapper就是zend_restore_ini_entry_cb的封装
zend_restore_ini_entry_cb(*ini_entry, ZEND_INI_STAGE_DEACTIVATE TSRMLS_CC);
return 1;
}
static int zend_restore_ini_entry_cb(zend_ini_entry *ini_entry, int stage TSRMLS_DC)
{
int result = FAILURE;
// 只看修改过的ini项
if (ini_entry->modified) {
if (ini_entry->on_modify) {
// 使用orig_value,对XXX_G内的相关字段进行重新设置
zend_try {
result = 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->mh_arg3, stage TSRMLS_CC);
} zend_end_try();
}
if (stage == ZEND_INI_STAGE_RUNTIME && result == FAILURE) {
/* runtime failure is OK */
return 1;
}
if (ini_entry->value != ini_entry->orig_value) {
efree(ini_entry->value);
}
// ini_entry本身恢复到最原始的值
ini_entry->value = ini_entry->orig_value;
ini_entry->value_length = ini_entry->orig_value_length;
ini_entry->modifiable = ini_entry->orig_modifiable;
ini_entry->modified = 0;
ini_entry->orig_value = NULL;
ini_entry->orig_value_length = 0;
ini_entry->orig_modifiable = 0;
}
return 0;
}
逻辑都蛮清晰的,相信读者可以看明白。总结一下关于ini配置的恢复流程:
复制代码 代码如下:
php_request_shutdown--->zend_deactivate--->zend_ini_deactivate--->zend_restore_ini_entry_wrapper--->zend_restore_ini_entry_cb