php中动态修改初始化设置文件的后缀名配置
1,运行时改变配置在前一篇中曾经谈到,ini_set函数可以在服务器端编程语言(专业超文本预处理器的缩写)执行的过程中,动态修改服务器端编程语言(专业超文本预处理器的缩写)的部分配置。注意,仅仅是部分,并非所有的配置都可以动态修改。关于初始化设置文件的后缀名配置的可修改性,参见:http://PHP。net/手动/zh/配置。变化。模式。服务器端编程语言(Professional Hypertext Preprocessor的缩写)
我们直接进入ini_set的实现,函数虽然有点长,但是逻辑很清晰:
复制代码代码如下: PHP _ FUNCTION(ini _ set){ char * varname,* new _ valueint varname_len,new _ value _ lenchar * old _ value
if(ZEND _ parse _ parameters(ZEND _ NUM _ ARGS)(TSRMLS _ CC,' ss ',varname,varname_len,new_value,new _ value _ len)==FAILURE){ return;}
//去乙二醇(ini _指令)中获取配置的值old _ value=Zend _ ini _ string(varname,varname_len 1,0);
/*复制到这里返回,因为改变可能会释放它!*/if(old _ value){ RETVAL _ STRING(old _ value,1);} else { RETVAL _ FALSE}
//如果开启了安全模式,那么如下这些初始化设置文件的后缀名配置可能涉及文件操作,需要要辅助检查uid#define _CHECK_PATH(var,var_len,ini) php_ini_check_path(var,var_len,ini,sizeof(ini))/* safe _ mode basedir CHECK */if(PG(safe _ mode)| | PG(open _ basedir)){ if(_ CHECK _ PATH(varname,varname_len,' error _ log ')| _ CHECK _ PATH(varname,varname_len,' Java。上课。PATH ')| | _ CHECK _ PATH(varname,varname_len,' java.home ').php_checkuid(new_value,NULL,CHECKUID _ CHECK _ FILE _ AND _ DIR)){ zval _ dtor(return _ value);RETURN _ FALSE } if(PHP _ check _ open _ basedir(new _ value TSRMLS _ CC)){ zval _ dtor(RETURN _ value);RETURN _ FALSE} } }
//在安全模式下,如下这些初始化设置文件的后缀名受到保护,不会被动态修改if (PG(safe_mode)) { if(!strncmp('max_execution_time ',varname,sizeof(' max _ execution _ time ')| |!strncmp('memory_limit ',varname,sizeof(' memory _ limit ')| |!strncmp(' child _ terminal ',varname,sizeof(' child _ terminal ')){ zval _ dtor(return _ value);返回_假} }
//调用zend_alter_ini_entry_ex去动态修改初始化设置文件的后缀名配置if(Zend _ alter _ INI _ entry _ ex(varname,varname_len 1,new_value,new_value_len,PHP_INI_USER,PHP_INI_STAGE_RUNTIME,0 TSRMLS _ CC)==FAILURE){ zval _ dtor(return _ value);返回_假}}
可以看到,除了一些必要的验证工作,主要就是调用zend_alter_ini_entry_ex。
我们继续跟进到zend_alter_ini_entry_ex函数中:
复制代码代码如下: Zend _ API int Zend _ alter _ ini _ entry _ ex(char * name,uint name_length,char *new_value,uint new_value_length,int modify_type,int stage,int force _ change TSRMLS _ DC)/* { { { */{ Zend _ ini _ entry * ini _ entry;char *重复;zend_bool可修改;zend_bool已修改;
//找出乙二醇(ini _指令)中对应的ini _ entry if(Zend _ hash _ find(EG(ini _ instructions),name,name_length,(void * *)ini _ entry)==FAILURE){ return FAILURE;}
//是否被修改以及可修改性可修改=ini _ entry-可修改;modified=ini _ entry-modified;
if(STAGE==ZEND _ INI _ STAGE _ ACTIVATE _ modify _ type==ZEND _ INI _ SYSTEM){ INI _ entry-modified=ZEND _ INI _ SYSTEM;}
//是否强制修改if(!force_change) { if(!(ini _ entry-可修改的modify _ type)){返回FAILURE} }
//EG(已修改的_ ini _指令)用于存放被修改过的ini_entry //主要用做恢复if(!EG(modified _ ini _ insteads)){ ALLOC _ HASHTABLE(EG(modified _ ini _ insteads));Zend _ hash _ init(EG(modified _ ini _ instructions),8,NULL,NULL,0);} //将ini _条目中的值,值的长度,可修改范围,保留到orig_xxx中去//以便在请求结束的时候,可以对ini _条目做恢复if(!已修改){ ini _ entry-orig _ value=ini _ entry-value;ini _ entry-orig _ value _ length=ini _ entry-value _ length;ini _ entry-orig _ modified=可修改;ini _ entry-modified=1;Zend _ hash _ add(EG(modified _ ini _ instructions),name,name_length,ini_entry,sizeof(zend_ini_entry*),NULL);}
重复=estrndup(new_value,new _ value _ length);
//调用modify来更新相应的ini配置if(!ini _ entry-on _ modify | | ini _ entry-on _ modify(ini _ entry,重复,new_value_length,ini_entry-mh_arg1,ini_entry-mh_arg2,ini_entry-mh_arg3,Stage TSRMLS_CC)==SUCCESS) {//如上所述,如果多次修改,需要释放值if(修改后的ini _ entry-orig _ value!=ini _ entry-value){ efree(ini _ entry-value);} ini_entry-value=重复;ini _ entry-value _ length=new _ value _ length;} else { efree(重复);返回失败;}
返回SUCCESS}
有三个逻辑需要我们仔细理解:
1)1)ini _ entry中的修改字段用于指示配置是否已被动态修改。一旦ini配置被修改,修改将被设置为1。上述代码中有一个关键段落:
复制代码如下://如果多次调用ini_set,orig_value始终保持原值if(!已修改){ ini _ entry-orig _ value=ini _ entry-value;ini _ entry-orig _ value _ length=ini _ entry-value _ length;ini _ entry-orig _ modified=可修改;ini _ entry-modified=1;Zend _ hash _ add(EG(modified _ ini _ instructions),name,name_length,ini_entry,sizeof(zend_ini_entry*),NULL);}
这段代码表明,无论我们在php代码中调用ini_set多少次,这个逻辑只会在ini_set第一次的时候进入,orig_value才会被设置。自从第二次调用ini_set后,这个分支将不会再次执行,因为此时modified已经被设置为1。因此,ini_entry-orig_value总是在第一次修改之前保存配置值(即最原始的配置)。
2)为了使ini_set修改的配置立即生效,需要on_modify回调函数。
如前一篇文章所述,调用on_modify来更新模块的全局变量。再次回想一下,首先,模块全局变量中的配置不再是字符串类型,所以我们应该使用bool和int。其次,每个ini_entry都存储了模块全局变量的地址和对应的偏移量,这样on_modify就可以快速修改内存。另外,不要忘了在调用on_modify之后,需要进一步更新ini_entry-value,这样EG(ini _ instructions)中的配置值才是最新的。
3)有一个新的哈希表,EG(modified _ ini _ instructions)。
EG(modified _ ini _ instructions)仅用于存储动态修改的ini配置。如果一个ini配置被动态修改,它同时存在于EG(ini _指令)和EG(modified _ ini _指令)中。由于每个ini_entry都标记有一个修改过的字段,难道不能遍历EG(ini _ instructions)来获得所有修改过的配置吗?
答案是肯定的。个人认为这里的EG(modified _ ini _ instructions)主要是为了提高性能,酱料直接遍历EG(modified _ ini _ instructions)就够了。另外,将EG(modified _ ini _ instructions)的初始化延迟到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。关闭扫描器/执行器/编译器并恢复ini条目*/Zend _ deactivate(ts rmls _ c);
进入zend_deactivate,可以进一步看到zend_ini_deactivate被调用,zend_ini_deactivate负责恢复php配置。
复制代码如下: Zend _ try { Zend _ ini _ deactivate(ts rmls _ c);} Zend _ end _ try();
具体来看zend_ini_deactivate的实现:
复制代码代码如下: ZEND _ API int ZEND _ ini _ deactivate(TSRMLS _ D)/* { { { */{ if(EG(modified _ ini _指令)){ //遍历修改后的指令中这张表//对每一个ini _条目调用Zend _ restore _ ini _ entry _ wrapper Zend _ hash _ apply(EG(modified _ ini _ instructions),(apply _ func _ t)Zend _ restore _ ini _ entry _ wrapper TSRMLS _ CC);//回收操作Zend _ hash _ destroy(EG(modified _ ini _ instructions));FREE _ HASHTABLE(EG(modified _ ini _ instructions));EG(修改的_ ini _指令)=空;}返回成功}
从zend _哈希_应用来看,真正恢复初始化设置文件的后缀名的任务最终落地到了zend_restore_ini_entry_wrapper回调函数。
复制代码代码如下:静态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);返回1;}
静态int Zend _ restore _ ini _ entry _ CB(Zend _ ini _ entry * ini _ entry,int stage TSRMLS _ DC){ int result=FAILURE;
//只看修改过的初始化设置文件的后缀名项if(ini _ entry-modified){ if(ini _ entry-on _ modify){//使用原始值,对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();}如果(stage==ZEND_INI_STAGE_RUNTIME结果==FAILURE) { /*运行时失败为确定*/返回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-modified=ini _ entry-orig _ modified;ini _ entry-modified=0;ini _ entry-orig _ value=NULL;ini _ entry-orig _ value _ length=0;ini _ entry-orig _ modified=0;}返回0;}
逻辑都蛮清晰的,相信读者可以看明白。总结一下关于初始化设置文件的后缀名配置的恢复流程:
复制代码代码如下: PHP _ request _ down-Zend _ deactivate-Zend _ ini _ deactivate-Zend _ restore _ ini _ entry _ wrapper-Zend _ restore _ ini _ entry _ CB
3,配置的销毁在sapi生命周期结束的时候,比如街头流氓关闭,cli程序执行完毕等等。一旦进入到这个阶段,之前所说的配置散列,EG(ini _指令)等都需要被销毁,其用到的内存空间需要被释放。
1、php会依次结束所有的模块,在每个模块的PHP_MSHUTDOWN_FUNCTION中调用注销_INI_ENTRIES。注销_INI_ENTRIES和REGISTER_INI_ENTRIES对应,但是注销_INI_ENTRIES并不负责模块全局空间的释放,XXX_globals这块内存放在静态数据区上,无需人为回收。
注销_INI_ENTRIES主要做的事情,是将某个模块的ini _条目配置从乙二醇(ini _指令)表中删除。删除之后,ini_entry本身的空间会被回收,但是ini _ entry-值不一定会被回收。
当所有模块的PHP_MSHUTDOWN_FUNCTION都调用注销_INI_ENTRIES一遍之后,EG(ini _指令)中只剩下了核心模块的初始化设置文件的后缀名配置。此时,就需要手动调用UNREGISTER_INI_ENTRIES,来完成对核心模块配置的删除工作。
复制代码代码如下: void PHP _ module _ shut down(TSRMLS _ D){ 0.//Zend _关机会依次关闭除了核心之外的所有服务器端编程语言(专业超文本预处理器的缩写)模块//关闭时会调用各个模块的PHP _ MSHUTDOWN _ FUNCTION Zend _ shut down(TSRMLS _ C);
//至此,EG(ini _指令)中只剩下了核心模块的配置//这里手动清理一下UNREGISTER _ INI _ ENTRIES();//回收config _ hash PHP _ shut down _ config();
//回收EG(ini _ instructions)Zend _ ini _ shut down(TSRMLS _ C);
.}
当手动调用注销_INI_ENTRIES完成之后,EG(ini _指令)已经不包含任何的元素,理论上讲,此时的乙二醇(ini _指令)是一张空的混杂表。
2、配置_哈希的回收发生在乙二醇(ini _指令)之后,上面贴出的代码中有关于php _关机_配置的函数调用php _关机_配置主要负责回收配置_哈希。
复制代码如下: int PHP _ shut down _ config(void){//Recycle configuration _ hashZend _ hash _ destroy(configuration _ hash);返回SUCCESS}
请注意,zend_hash_destroy不会释放configuration_hash本身的空间。和XXX_G访问的模块全局空间一样,configuration_hash也是一个全局变量,不需要手动回收。
3.当php_shutdown_config完成时,只有EG(ini _ instructions)的空间没有被释放。因此,zend_ini_shutdown被称为最后一步。Zend_ini_shutdown用于释放EG(ini _指令)。如前所述,此时的EG(ini _ instructions)理论上是一个空的hashtable,因此需要释放HashTable本身占用的空间。
复制代码如下: Zend _ API int Zend _ ini _ shut down(tsrmls _ d){//eg(ini _指令)是动态分配的空间,Zend _ hash _ destroy (eg (ini _指令))需要回收;free(EG(ini _ instructions));返回SUCCESS}
4.用图片总结与ini配置相关的过程:
版权声明:php中动态修改初始化设置文件的后缀名配置是由宝哥软件园云端程序自动收集整理而来。如果本文侵犯了你的权益,请联系本站底部QQ或者邮箱删除。