Dynamically modifying INI configuration in PHP _php base

Source: Internet
Author: User
Tags http request

1. Change configuration at runtime
As mentioned in the previous article, the Ini_set function can dynamically modify part of PHP configuration during PHP execution. Note that only partially, not all configurations can be dynamically modified. For the modifiable nature of the INI configuration, see: http://php.net/manual/zh/configuration.changes.modes.php

We go straight into the Ini_set, the function is a bit long, but the logic is clear:

Copy Code code as follows:

Php_function (Ini_set)
{
Char *varname, *new_value;
int Varname_len, New_value_len;
Char *old_value;

if (Zend_parse_parameters (Zend_num_args () TSRMLS_CC, "SS", &varname, &varname_len, &new_value, &new_ Value_len) = = failure) {
Return
}

Go to EG (ini_directives) to get the value of the configuration
Old_value = zend_ini_string (varname, Varname_len + 1, 0);

/* Copy to return this, because alter might free it! */
if (Old_value) {
Retval_string (Old_value, 1);
} else {
Retval_false;
}

If Safe mode is turned on, the following INI configuration may involve file operations and need to be assisted with a check 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.class.path") | |
_check_path (VarName, Varname_len, "Java.home") | |
_check_path (VarName, Varname_len, "Mail.log") | |
_check_path (VarName, Varname_len, "Java.library.path") | |
_check_path (VarName, Varname_len, "Vpopmail.directory")) {
if (PG (Safe_mode) && (!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;
}
}
}

In safe mode, these ini are protected and will not be dynamically modified
if (PG (Safe_mode)) {
if (!strncmp ("Max_execution_time", varname, sizeof ("Max_execution_time")) | |
!STRNCMP ("Memory_limit", varname, sizeof ("Memory_limit")) | |
!STRNCMP ("Child_terminate", varname, sizeof ("child_terminate"))
) {
Zval_dtor (Return_value);
Return_false;
}
}

Call ZEND_ALTER_INI_ENTRY_EX to dynamically modify the INI configuration
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);
Return_false;
}
}

As you can see, in addition to some of the necessary validation work, the main thing is to invoke ZEND_ALTER_INI_ENTRY_EX.

We continue to follow up to the ZEND_ALTER_INI_ENTRY_EX function:

Copy Code code as follows:

Zend_api int zend_alter_ini_entry_ex (char *name, uint name_length, char *new_value, uint new_value_length, int Modify_typ e, int stage, int force_change tsrmls_dc)/* {{* * *
{
Zend_ini_entry *ini_entry;
Char *duplicate;
Zend_bool modifiable;
Zend_bool modified;

Find the corresponding ini_entry in eg (ini_directives)
if (Zend_hash_find (EG (ini_directives), name, Name_length, (void *) &ini_entry) = = failure) {
return failure;
}

is modified and modifiable
modifiable = ini_entry->modifiable;
Modified = ini_entry->modified;

if (stage = = Zend_ini_stage_activate && Modify_type = = Zend_ini_system) {
ini_entry->modifiable = Zend_ini_system;
}

Whether to force modifications
if (!force_change) {
if (!) ( Ini_entry->modifiable & Modify_type)) {
return failure;
}
}

EG (modified_ini_directives) is used to store the modified Ini_entry
Mainly used for recovery
if (! EG (modified_ini_directives)) {
Alloc_hashtable (EG (modified_ini_directives));
Zend_hash_init (EG (modified_ini_directives), 8, NULL, NULL, 0);
}

The value in the Ini_entry, the length of the value, the range of modifications, can be retained into the ORIG_XXX
So that the ini_entry can be restored at the end of the request.
if (!modified) {
Ini_entry->orig_value = ini_entry->value;
Ini_entry->orig_value_length = ini_entry->value_length;
Ini_entry->orig_modifiable = modifiable;
ini_entry->modified = 1;
Zend_hash_add (EG (modified_ini_directives), name, Name_length, &ini_entry, sizeof (zend_ini_entry*), NULL);
}

    duplicate = Estrndup (New_value, new_value_length);

   //Call Modify to update the corresponding INI configuration in Xxx_g
    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) = = SUCCESS) {
       //With the above, if you modify it more than once, you need to release the previous modified value
& nbsp;       if (modified && ini_entry->orig_value!= ini_entry->value) {
            Efree (ini_entry->value);
       }
        ini_entry->value = duplicate;
        ini_entry->value_length = new_value_length;
   } else {
        efree (duplicate);
         return failure;
   }

return SUCCESS;
}

There are 3 of logics that require our careful understanding:

1) The Modified field in Ini_entry is used to indicate whether the configuration has been dynamically modified. Once the INI configuration has been modified, the modified will be set to 1. Some of the above code is critical:

Copy Code code as follows:

If you call ini_set more than once, Orig_value always keep the original value
if (!modified) {
Ini_entry->orig_value = ini_entry->value;
Ini_entry->orig_value_length = ini_entry->value_length;
Ini_entry->orig_modifiable = modifiable;
ini_entry->modified = 1;
Zend_hash_add (EG (modified_ini_directives), name, Name_length, &ini_entry, sizeof (zend_ini_entry*), NULL);
}

This code indicates that no matter how many times we call Ini_set in PHP code, only the first time Ini_set will enter this logic, set good orig_value. Starting with the second call Ini_set, the branch is not executed again because the modified is already set to 1. Therefore, Ini_entry->orig_value always saves the configuration value (that is, the most original configuration) before the first modification.

2 The on_modify callback function is required in order for the Ini_set modified configuration to take effect immediately.

As described in the previous article, the call to On_modify is to be able to update the module's global variables. Again, first of all, the configuration in the module global variable is not a string type, the bool with bool, and the int with int. Secondly, each Ini_entry store the address of the module global variable and the corresponding offset, so that on_modify can quickly make memory modification. Also, don't forget that after the on_modify call is over, you still need to update ini_entry->value so that the configuration value in eg (ini_directives) is up to date.

3 There is a new hash table, EG (modified_ini_directives).

eg (modified_ini_directives) is used only to store dynamically modified INI configurations, and if an INI configuration is dynamically modified, it exists both in eg (ini_directives) and in eg (modified_ini_ directives). Since every ini_entry has a modified field to mark, wouldn't it be possible to traverse eg (ini_directives) to get all the modified configurations?

The answer is yes. Personally, here's the EG (modified_ini_directives) is mainly to improve performance, the sauce directly traversing the eg (modified_ini_directives) is enough. In addition, the initialization of eg (modified_ini_directives) to the ZEND_ALTER_INI_ENTRY_EX can also be seen in the details of PHP performance optimization points.

2, restore Configuration
The action time of the Ini_set is not the same as that of the php.ini file, and Ini_set will fail once the request is completed. In addition, when the Ini_restore function is called in our code, the configuration previously set by Ini_set is invalidated.

After each PHP request executes, the php_request_shutdown is triggered, and the php_request_startup is a two relative process. If PHP is hooked under Apache/nginx, the Php_request_shutdown is invoked every HTTP request is processed, and if PHP is running in CLI mode, then the script will also call Php_request_ when it finishes execution. Shutdown

In Php_request_shutdown, we can see the recovery processing for the INI:

Copy Code code as follows:

/* 7. Shutdown scanner/executor/compiler and restore INI Entries * *
Zend_deactivate (Tsrmls_c);

Into the zend_deactivate, you can further see the call to the Zend_ini_deactivate function, the zend_ini_deactivate is responsible for the configuration of the PHP restore.

Copy Code code as follows:

Zend_try {
Zend_ini_deactivate (Tsrmls_c);
} zend_end_try ();

specifically to see the implementation of Zend_ini_deactivate:

Copy Code code as follows:

Zend_api int Zend_ini_deactivate (tsrmls_d)/* {{* * *
{
if (EG (modified_ini_directives)) {
Traverse the table in eg (modified_ini_directives)
Call Zend_restore_ini_entry_wrapper for each ini_entry
Zend_hash_apply (EG (modified_ini_directives), (apply_func_t) Zend_restore_ini_entry_wrapper TSRMLS_CC);

Recycle operations
Zend_hash_destroy (EG (modified_ini_directives));
Free_hashtable (EG (modified_ini_directives));
EG (modified_ini_directives) = NULL;
}
return SUCCESS;
}

From the zend_hash_apply point of view, the real restoration of the INI task finally landed to the Zend_restore_ini_entry_wrapper callback function.

Copy Code code as follows:

static int Zend_restore_ini_entry_wrapper (Zend_ini_entry **ini_entry tsrmls_dc)
{
Zend_restore_ini_entry_wrapper is the ZEND_RESTORE_INI_ENTRY_CB package.
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;

Look at the modified INI item only
if (ini_entry->modified) {
if (ini_entry->on_modify) {
Use Orig_value to reset related fields within 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 itself back to the original value
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;
}

Logic is quite clear, I believe that the reader can see clearly. Summarize the recovery process for the INI configuration:

Copy Code code as follows:

Php_request_shutdown--->zend_deactivate--->zend_ini_deactivate--->zend_restore_ini_entry_wrapper--- >zend_restore_ini_entry_cb

3, configuration of the destruction
At the end of the SAPI lifecycle, such as Apache shutdown, CLI program execution completed, and so on. Once entered into this phase, the previously mentioned Configuration_hash,eg (ini_directives) and so on need to be destroyed, the use of the memory space needs to be released.

1,php will end all modules in turn, and call Unregister_ini_entries in the php_mshutdown_function of each module. Unregister_ini_entries and Register_ini_entries correspond, but Unregister_ini_entries is not responsible for the release of the module global space, xxx_globals this block is stored in the static data area, No artificial recycling.

Unregister_ini_entries The main thing to do is to remove a module's ini_entry configuration from the eg (ini_directives) table. After deletion, the space of the ini_entry itself is reclaimed, but the ini_entry->value is not necessarily recycled.

After all the php_mshutdown_function of the modules have been called Unregister_ini_entries, only the INI configuration of the core module is left in EG (ini_directives). At this point, you need to manually invoke Unregister_ini_entries to complete the deletion of the core module configuration.

Copy Code code as follows:

void Php_module_shutdown (Tsrmls_d)
{
...

Zend_shutdown will turn off all PHP modules except core
The php_mshutdown_function of each module is invoked when closing
Zend_shutdown (Tsrmls_c);

...

So far, there is only the configuration of the core module in EG (ini_directives)
Here, clean it up manually.
Unregister_ini_entries ();

Recycling Configuration_hash
Php_shutdown_config ();

Recycling eg (ini_directives)
Zend_ini_shutdown (Tsrmls_c);

...
}

When the manual call Unregister_ini_entries is complete, eg (ini_directives) does not contain any elements, theoretically, at this point the eg (ini_directives) is an empty hash table.

The 2,configuration_hash recycle occurs after eg (ini_directives), and the code posted above has a function call about Php_shutdown_config. Php_shutdown_config is mainly responsible for recycling configuration_hash.

Copy Code code as follows:

int php_shutdown_config (void)
{
Recycling Configuration_hash
Zend_hash_destroy (&configuration_hash);

...

return SUCCESS;
}

Note that Zend_hash_destroy does not release the space of the Configuration_hash itself, as with the module global space accessed by Xxx_g, Configuration_hash is also a global variable without manual recycling.

3, when the Php_shutdown_config is complete, only eg (Ini_directives) 's own space has not been released. So the last step calls Zend_ini_shutdown. Zend_ini_shutdown is used to release eg (ini_directives). As already mentioned, the case of eg (ini_directives) is theoretically an empty hash table, so the space occupied by the hashtable itself needs to be released.

Copy Code code as follows:

Zend_api int Zend_ini_shutdown (tsrmls_d)
{
EG (ini_directives) is a dynamically allocated space that needs to be recycled
Zend_hash_destroy (EG (ini_directives));
Free (EG (ini_directives));
return SUCCESS;
}

4, summary
Outline the process associated with the INI configuration with a diagram:

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.