Home  >  Article  >  Backend Development  >  In-depth understanding of ini configuration in php (2), in-depth understanding of ini_PHP tutorial

In-depth understanding of ini configuration in php (2), in-depth understanding of ini_PHP tutorial

WBOY
WBOYOriginal
2016-07-13 10:17:191235browse

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:

<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>//</span><span> 去EG(ini_directives)中获取配置的值</span>
    old_value = zend_ini_string(varname, varname_len + <span>1</span>, <span>0</span><span>);

    </span><span>/*</span><span> copy to return here, because alter might free it! </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><span> 如果开启了安全模式,那么如下这些ini配置可能涉及文件操作,需要要辅助检查uid</span>
<span>#define</span> _CHECK_PATH(var, var_len, ini) php_ini_check_path(var, var_len, ini, sizeof(ini))
    <span>/*</span><span> safe_mode & basedir check </span><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><span> 在安全模式下,如下这些ini受到保护,不会被动态修改</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><span> 调用zend_alter_ini_entry_ex去动态修改ini配置</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>

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:

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> {{{ <span>*/<span>
{
    zend_ini_entry *<span>ini_entry;
    <span>char *<span>duplicate;
    zend_bool modifiable;
    zend_bool modified;

    <span>//<span> 找出EG(ini_directives)中对应的ini_entry
    <span>if (zend_hash_find(EG(ini_directives), name, name_length, (<span>void **) &ini_entry) ==<span> FAILURE) {
        <span>return<span> FAILURE;
    }

    <span>//<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> 是否强制修改
    <span>if (!<span>force_change) {
        <span>if (!(ini_entry->modifiable &<span> modify_type)) {
            <span>return<span> FAILURE;
        }
    }

    <span>//<span> EG(modified_ini_directives)用于存放被修改过的ini_entry<br />    // 主要用做恢复
    <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>//<span> 将ini_entry中的值,值的长度,可修改范围,保留到orig_xxx中去<br />    // 以便在请求结束的时候,可以对ini_entry做恢复<br /><span><span>    <span>if (!<span>modified) {
        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> 调用modify来更新XXX_G中对应的ini配置<span><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> 同上面,如果多次修改,则需要释放前一次修改的值
        <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>

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:

<span>//</span><span> 如果多次调用ini_set,则orig_value等始终保持最原始的值</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>

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:

<span>/*</span><span> 7. Shutdown scanner/executor/compiler and restore ini entries </span><span>*/</span><span>
zend_deactivate(TSRMLS_C);</span>

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.

<span>zend_try {
    zend_ini_deactivate(TSRMLS_C);
} zend_end_try();</span>

Let’s take a closer look at the implementation of zend_ini_deactivate:

ZEND_API <span>int</span> zend_ini_deactivate(TSRMLS_D) <span>/*</span><span> {{{ </span><span>*/</span><span>
{
    </span><span>if</span><span> (EG(modified_ini_directives)) {
        </span><span>//</span><span> 遍历EG(modified_ini_directives)中这张表
        </span><span>//</span><span> 对每一个ini_entry调用zend_restore_ini_entry_wrapper</span>
<span>        zend_hash_apply(EG(modified_ini_directives), (apply_func_t) zend_restore_ini_entry_wrapper TSRMLS_CC);
        
        </span><span>//</span><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>

From zend_hash_apply, the real task of restoring ini finally falls to the zend_restore_ini_entry_wrapper callback function.

<span>static</span> <span>int</span> zend_restore_ini_entry_wrapper(zend_ini_entry **<span>ini_entry TSRMLS_DC)
{
    </span><span>//</span><span> zend_restore_ini_entry_wrapper就是zend_restore_ini_entry_cb的封装</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><span> 只看修改过的ini项</span>
    <span>if</span> (ini_entry-><span>modified) {
        </span><span>if</span> (ini_entry-><span>on_modify) {
            </span><span>//</span><span> 使用orig_value,对XXX_G内的相关字段进行重新设置</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> runtime failure is OK </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>//</span><span> ini_entry本身恢复到最原始的值</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>

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模块配置的删除工作。

<span>void</span><span> php_module_shutdown(TSRMLS_D)
{
    ...
    
    </span><span>//</span><span> zend_shutdown会依次关闭除了Core之外的所有php模块
    </span><span>//</span><span> 关闭时会调用各个模块的PHP_MSHUTDOWN_FUNCTION</span>
<span>    zend_shutdown(TSRMLS_C);
    
    ...

    </span><span>//</span><span> 至此,EG(ini_directives)中只剩下了Core模块的配置
    </span><span>//</span><span> 这里手动清理一下</span>
<span>    UNREGISTER_INI_ENTRIES();
    
    </span><span>/</span><span>/ 回收configuration_hash</span><span>
    php_shutdown_config();
<br /></span>
<span>    <span>/<span>/ 回收EG(ini_directives)<br />    </span></span>zend_ini_shutdown(TSRMLS_C);<br />
    ...
}</span>

当手动调用UNREGISTER_INI_ENTRIES完成之后,EG(ini_directives)已经不包含任何的元素,理论上讲,此时的EG(ini_directives)是一张空的hash表。

2,configuration_hash的回收发生在EG(ini_directives)之后,上面贴出的代码中有关于php_shutdown_config的函数调用。php_shutdown_config主要负责回收configuration_hash。

<span>int</span> php_shutdown_config(<span>void</span><span>)
{
    </span><span>//</span><span> 回收configuration_hash</span>
    zend_hash_destroy(&<span>configuration_hash);
    
    ...
    
    </span><span>return</span><span> SUCCESS;
}</span>

注意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本身所占用的空间需要被释放。

ZEND_API <span>int</span><span> zend_ini_shutdown(TSRMLS_D)
{
    </span><span>//</span><span> EG(ini_directives)是动态分配出的空间,需要回收</span>
<span>    zend_hash_destroy(EG(ini_directives));
    free(EG(ini_directives));
    </span><span>return</span><span> SUCCESS;
}</span>

4,总结

用一张图大致描述一下和ini配置相关的流程:

 

 

php中ini的配置是怎做的

直接编辑php目录下的php.ini文件即可

有一个“register_globals = Off”值,这个值是用来打开全局变量的,比如表单送过来的值,如果这个值设为“Off”,就只能用“$_POST['变量名']、$_GET['变量名 ']”等来取得送过来的值,如果设为“On”,就可以直接使用“$变量名”来获取送过来的值,当然,设为“Off”就比较安全,不会让人轻易将网页间传送 的数据截取。这个值是否改成“On”就看自己感觉了,是安全重要还是方便重要?
要用mysql,就要把“;extension= php_mysql.dll”前的“;”去掉。所有的模块文件都放在php解压缩目录的“ext”之下,用什么就把前面的“;”去掉就行了。
 

php怎读取ini配置文件并按要显示

Do you think this meets your requirements?
d85604b2167e2723fdc8f5c62bb72d05';// 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 '1827e2ca8cfa0c24e1ea56456637fc2c'; echo 'b185a95813d68d257001b3c72cf5656e ',$key,'b90dd5946f0946207856a8a37f441edf'; echo 'b185a95813d68d257001b3c72cf5656e>

www.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...