Home php教程 php手册 Dynamically modify ini configuration in php_php basics

Dynamically modify ini configuration in php_php basics

May 16, 2016 am 08:59 AM
ini php Configuration

1. Change configuration during 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 this is only part of it, not all configurations can be modified dynamically. 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:

Copy code The code is 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;
    }

    // 去EG(ini_directives)中获取配置的值
    old_value = zend_ini_string(varname, varname_len 1, 0);

    /* copy to return here, 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 configurations may involve file operations and require auxiliary checking of 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, the following ini's 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 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:

Copy code The code is as follows:

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 *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;
}

// Whether it has been modified and whether it can be modified
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 modification
If (!force_change) {
If (!(ini_entry->modifiable & modify_type)) {
               return FAILURE;
         }
}

// EG (modified_ini_directives) is used to store 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);
}

//Reserve the value in ini_entry, the length of the value, and the modifiable range into orig_xxx
// So that ini_entry can be restored when the request ends
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) {
//Same as above, if modified multiple times, the previously modified value needs to be released
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 three pieces of logic 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:

Copy code The code is as follows:

// If ini_set is called multiple times, orig_value, etc. always maintain 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 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 module's global variables. 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, and 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 that of the php.ini file. Once the request execution ends, 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:

Copy code The code is as follows:

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

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

Copy code The code is as follows:

zend_try {
zend_ini_deactivate(TSRMLS_C);
} zend_end_try();

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

Copy code The code is as follows:

ZEND_API int zend_ini_deactivate(TSRMLS_D) /* {{{ */
{
If (EG(modified_ini_directives)) {
// Traverse this 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);
                                                                               
// Recycling operation
        zend_hash_destroy(EG(modified_ini_directives));
FREE_HASHTABLE(EG(modified_ini_directives));
         EG(modified_ini_directives) = NULL;
}
Return SUCCESS;
}

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

Copy code The code is as follows:

static int zend_restore_ini_entry_wrapper(zend_ini_entry **ini_entry TSRMLS_DC)
{
// zend_restore_ini_entry_wrapper is the encapsulation of 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;

// Only view modified ini items
If (ini_entry->modified) {
If (ini_entry->on_modify) {
//Use orig_value to reset the relevant fields in 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-phpc ngtphpcnmh_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 is restored to its 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;
}

The logic is quite clear, I believe readers can understand it. Summarize the recovery process of ini configuration:

Copy code The code is as follows:

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, for example, apache is shut down, the cli program is executed, 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.

The main thing UNREGISTER_INI_ENTRIES does is to delete the ini_entry configuration of a certain module from the EG (ini_directives) table. After deletion, the space of ini_entry itself will be recycled, but ini_entry->value may not be recycled.

After PHP_MSHUTDOWN_FUNCTION of all modules calls UNREGISTER_INI_ENTRIES once, only the ini configuration of the Core module is left in EG (ini_directives). At this time, you need to manually call UNREGISTER_INI_ENTRIES to complete the deletion of the Core module configuration.

Copy code The code is as follows:

void php_module_shutdown(TSRMLS_D)
{
...

// zend_shutdown will shut down all php modules except Core in sequence
// PHP_MSHUTDOWN_FUNCTION of each module will be called when closing
zend_shutdown(TSRMLS_C);

...

// At this point, only the configuration of the Core module is left in EG (ini_directives)
// Clean it manually here
UNREGISTER_INI_ENTRIES();

// Recycle configuration_hash
php_shutdown_config();

// Recycle EG(ini_directives)
zend_ini_shutdown(TSRMLS_C);

...
}

When the manual call to UNREGISTER_INI_ENTRIES is completed, EG (ini_directives) no longer contains any elements. Theoretically, EG (ini_directives) at this time is an empty hash table.

2. The recycling of configuration_hash occurs after EG (ini_directives). The code posted above contains the function call about php_shutdown_config. php_shutdown_config is mainly responsible for recycling configuration_hash.

Copy code The code is as follows:

int php_shutdown_config(void)
{
// Recycle configuration_hash
zend_hash_destroy(&configuration_hash);

...

Return SUCCESS;
}

Note that zend_hash_destroy does not release the space of configuration_hash itself. Like the global space of the module accessed by XXX_G, configuration_hash is also a global variable and does not need to be manually recycled.

3. When php_shutdown_config is completed, only the own space of EG (ini_directives) has not been released. So the last step calls zend_ini_shutdown. zend_ini_shutdown is used to release EG (ini_directives). As mentioned above, the EG (ini_directives) at this time is theoretically an empty hash table, so the space occupied by the HashTable itself needs to be released.

Copy code The code is as follows:

ZEND_API int zend_ini_shutdown(TSRMLS_D)
{
// EG (ini_directives) is dynamically allocated space and needs to be recycled
zend_hash_destroy(EG(ini_directives));
free(EG(ini_directives));
Return SUCCESS;
}

4. Summary
Use a picture to roughly describe the process related to ini configuration:

Dynamically modify ini configuration in php_php basics

Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

Video Face Swap

Video Face Swap

Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Tools

Notepad++7.3.1

Notepad++7.3.1

Easy-to-use and free code editor

SublimeText3 Chinese version

SublimeText3 Chinese version

Chinese version, very easy to use

Zend Studio 13.0.1

Zend Studio 13.0.1

Powerful PHP integrated development environment

Dreamweaver CS6

Dreamweaver CS6

Visual web development tools

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)

PHP 8.4 Installation and Upgrade guide for Ubuntu and Debian PHP 8.4 Installation and Upgrade guide for Ubuntu and Debian Dec 24, 2024 pm 04:42 PM

PHP 8.4 brings several new features, security improvements, and performance improvements with healthy amounts of feature deprecations and removals. This guide explains how to install PHP 8.4 or upgrade to PHP 8.4 on Ubuntu, Debian, or their derivati

7 PHP Functions I Regret I Didn't Know Before 7 PHP Functions I Regret I Didn't Know Before Nov 13, 2024 am 09:42 AM

If you are an experienced PHP developer, you might have the feeling that you’ve been there and done that already.You have developed a significant number of applications, debugged millions of lines of code, and tweaked a bunch of scripts to achieve op

How To Set Up Visual Studio Code (VS Code) for PHP Development How To Set Up Visual Studio Code (VS Code) for PHP Development Dec 20, 2024 am 11:31 AM

Visual Studio Code, also known as VS Code, is a free source code editor — or integrated development environment (IDE) — available for all major operating systems. With a large collection of extensions for many programming languages, VS Code can be c

Explain JSON Web Tokens (JWT) and their use case in PHP APIs. Explain JSON Web Tokens (JWT) and their use case in PHP APIs. Apr 05, 2025 am 12:04 AM

JWT is an open standard based on JSON, used to securely transmit information between parties, mainly for identity authentication and information exchange. 1. JWT consists of three parts: Header, Payload and Signature. 2. The working principle of JWT includes three steps: generating JWT, verifying JWT and parsing Payload. 3. When using JWT for authentication in PHP, JWT can be generated and verified, and user role and permission information can be included in advanced usage. 4. Common errors include signature verification failure, token expiration, and payload oversized. Debugging skills include using debugging tools and logging. 5. Performance optimization and best practices include using appropriate signature algorithms, setting validity periods reasonably,

How do you parse and process HTML/XML in PHP? How do you parse and process HTML/XML in PHP? Feb 07, 2025 am 11:57 AM

This tutorial demonstrates how to efficiently process XML documents using PHP. XML (eXtensible Markup Language) is a versatile text-based markup language designed for both human readability and machine parsing. It's commonly used for data storage an

PHP Program to Count Vowels in a String PHP Program to Count Vowels in a String Feb 07, 2025 pm 12:12 PM

A string is a sequence of characters, including letters, numbers, and symbols. This tutorial will learn how to calculate the number of vowels in a given string in PHP using different methods. The vowels in English are a, e, i, o, u, and they can be uppercase or lowercase. What is a vowel? Vowels are alphabetic characters that represent a specific pronunciation. There are five vowels in English, including uppercase and lowercase: a, e, i, o, u Example 1 Input: String = "Tutorialspoint" Output: 6 explain The vowels in the string "Tutorialspoint" are u, o, i, a, o, i. There are 6 yuan in total

Explain late static binding in PHP (static::). Explain late static binding in PHP (static::). Apr 03, 2025 am 12:04 AM

Static binding (static::) implements late static binding (LSB) in PHP, allowing calling classes to be referenced in static contexts rather than defining classes. 1) The parsing process is performed at runtime, 2) Look up the call class in the inheritance relationship, 3) It may bring performance overhead.

What are PHP magic methods (__construct, __destruct, __call, __get, __set, etc.) and provide use cases? What are PHP magic methods (__construct, __destruct, __call, __get, __set, etc.) and provide use cases? Apr 03, 2025 am 12:03 AM

What are the magic methods of PHP? PHP's magic methods include: 1.\_\_construct, used to initialize objects; 2.\_\_destruct, used to clean up resources; 3.\_\_call, handle non-existent method calls; 4.\_\_get, implement dynamic attribute access; 5.\_\_set, implement dynamic attribute settings. These methods are automatically called in certain situations, improving code flexibility and efficiency.

See all articles