In-depth understanding of ini configuration in php (1)

Source: Internet
Author: User
Tags register php sapi

In-depth understanding of ini configuration in php (1)
This article does not describe the purpose of an ini configuration item in detail. These are all covered in the manual. I just want to explore the implementation mechanism of php from a specific point of view, which involves some knowledge about the php kernel:-) All php users know php. the ini configuration takes effect throughout the SAPI lifecycle. During the execution of a php script, If you manually modify the ini configuration, it will not start. If you cannot restart apache or nginx, you can only explicitly call the ini_set interface in php code. Ini_set is a function provided by php to dynamically modify the configuration. It should be noted that the time range for ini_set to take effect is different from the configuration set in the INI file. After the php script is executed, the ini_set setting will expire immediately. Therefore, this article will be divided into two parts: the first part describes the principles of php. ini configuration, and the second part describes how to dynamically modify php configuration. The configuration of php. ini involves three pieces of data: configuration_hash, EG (ini_directives), PG, BG, PCRE_G, JSON_G, and XXX_G. If you do not know the meaning of the three types of data, the following will explain in detail. 1. parsing the INI configuration file because php. ini needs to take effect continuously in the SAPI process, parsing the INI file and constructing the php configuration accordingly must be the beginning of SAPI. In other words, it must occur in the php startup process. Php has generated these configurations before any actual request arrives. Reflected to the php kernel, which is the php_module_startup function. Php_module_startup is mainly responsible for starting php. It is usually called at the beginning of SAPI. Btw, another common function is php_request_startup, which initializes each request at the time of arrival. php_module_startup and php_request_startup are two identification actions, however, the analysis is not within the scope of this article. For example, when php is attached to an apache module, apache activates all these modules, including the php module. When activating the php module, php_module_startup is called. The php_module_startup function has done a lot of work. Once the call to php_module_startup is over, it means OK, php has been started, and now the request can be accepted and responded. In the php_module_startup function, the implementation related to parsing the INI file is:/* this will read in php. ini, set up the configuration parameters, load zend extensions and register php function extensions to be loaded later */if (php_init_config (TSRMLS_C) = FAILURE) {return FAILURE;} You can see, in fact, the php_init_config function is called to complete the parse of the INI file. Parse mainly analyzes lex & grammar and extracts and saves key and value pairs in the INI file. The format of php. ini is very simple. The key on the left side of the equals sign and value on the right side. Where does php store a pair of kv pairs after they are extracted? The answer is the previously mentioned configuration_hash. Static HashTable configuration_hash; The configuration_hash statement is a HashTable Data Structure in php_ini.c. As the name implies, it is actually a hash table. In other words, php5.3 and earlier versions cannot obtain configuration_hash, because it is a static variable in the php_ini.c file. Later, php5.3 added the php_ini_get_configuration_hash interface. This interface directly returns & configuration_hash, allowing various extensions of php to easily view the full view of configuration_hash... it's really amazing... note four points: First, php_init_config will not perform any verification except the lexical syntax. That is to say, if we add a line "hello = world" to the INI file, as long as this is a correct configuration item, the final configuration_hash will contain an element with the key "hello" and the value "world, configuration_hash reflects the INI file to the maximum extent. Second, the INI file allows us to configure it as an array. For example, write the following three lines in the INI file: drift. arr [] = 1drift. arr [] = 2drift. if arr [] = 3, a key is drift in the final generated configuration_hash table. the value of an arr element is an array containing numbers 1, 2, and 3. This is an extremely rare configuration method. Third, php also allows us to build some ini files in addition to the default php. ini file (php-% s. ini. These INI files are placed in an additional directory. This directory is specified by the Environment Variable PHP_INI_SCAN_DIR. After php_init_config has parsed php. ini, it will scan the directory again and find all. ini files in the directory for analysis. The kv key-value pairs generated in these extra INI files will also be added to configuration_hash. This is an occasional useful feature. If we develop php extensions by ourselves, we do not want to mix the configurations into php. ini, you can write another ini, and use PHP_INI_SCAN_DIR to tell php where to find it. Of course, its disadvantage is also obvious. It needs to set additional environment variables for support. A better solution is that the developer calls php_parse_user_ini_file or zend_parse_ini_file in the extension to parse the corresponding INI file. Fourth, in configuration_hash, if the key is a string, what is the value type? The answer is also a string (except for the above special arrays ). Specifically, for example, the following configuration: display_errors = Onlog_errors = Offlog_errors_max_len = 1024, then the key-value pairs actually stored in the final configuration_hash are: key: "display_errors" val: "1" key: "log_errors" val: "" key: "log_errors_max_len" val: "1024" NOTE: log_errors is a real, empty string that stores no value even "0. In addition, log_errors_max_len is not a number, but a string of 1024. At this point, the contents related to parsing the INI file are clearly explained. Summary: 1. parsing ini occurs in php_module_startup Phase 2, and the parsing result is stored in configuration_hash. 2. The general structure of the module php can be seen as a zend engine at the bottom layer. It is responsible for interacting with the OS, compiling php code, and providing memory hosting, on the upper layer of the zend engine, many modules are arranged. Among them, the Core is a Core module, and other modules, such as Standard, PCRE, Date, Session, etc., are also called php extensions. We can simply understand that each module provides a set of functional interfaces for developers to call. For example, common built-in functions such as explode, trim, and array are used, it is provided by the Standard module. Why do we need to talk about this. in ini, apart from php, some Core module configurations (such as safe_mode, display_errors, and max_execution_time), there are a lot of configurations for different modules. For example, the date module provides common functions such as date, time, and strtotime. In php. in ini, its related configuration is like: [Date]; date. timezone = 'Asia/Shanghai'; date. default_latitude = 31.7667; date. default_long1_= 35.2333; date. sunrise_zenith= 90.583333; date. sunset_zenith = 90.583333 apart from the independent configurations of these modules, the zend engine is also configurable, except that the zend engine has very few configurable items, only error_reporting and zend. enable_gc and detect_unicode. As we mentioned in the previous section, php_module_startup will call php_init_config to parse the INI file and generate configuration_hash. So what else will be done in php_module_startup? Obviously, the configuration in configuration_hash is applied to different modules such as Zend, Core, Standard, and SPL. Of course, this is not a one-stop process, because php usually contains many modules, and these modules will be started in sequence during php startup. Then, the configuration process of module A occurs in the startup process of module. Those who have experience in extended development directly pointed out that the startup of module A is not in PHP_MINIT_FUNCTION (? Yes. If module A needs to be configured, you can call REGISTER_INI_ENTRIES () in PHP_MINIT_FUNCTION. REGISTER_INI_ENTRIES searches for user-defined configuration values in configuration_hash Based on the configuration item names required by the current module, and updates them to the global space of the module. 2.1. Before you understand how to apply the ini configuration from configuration_hash to each module, you must first understand the global space of the php module. For different php modules, you can open up a storage space of your own, and this space is globally visible to this module. Generally, it is used to store the ini configurations required by the module. That is to say, the configuration items in configuration_hash will be stored in the global space. During the execution of the module, you only need to directly access this global space to get the settings you have set for this module. Of course, it is often used to record the intermediate data of a module during execution. We use the bcmath module as an example to illustrate that bcmath is a php module that provides mathematical interfaces. First, let's take a look at the ini configuration: PHP_INI_BEGIN () STD_PHP_INI_ENTRY ("bcmath. scale "," 0 ", PHP_INI_ALL, OnUpdateLongGEZero, bc_precision, zend_bcmath_globals, bcmath_globals) PHP_INI_END () bcmath has only one configuration item, which can be found in php. bcmath is used in ini. scale to configure the bcmath module. Next, let's take a look at the global space definition of the bcmatch module. In php_bcmath.h, the following declaration is made: Compute (bcmath) bc_num _ zero _; bc_num _ one _; bc_num _ two _; long bc_precision; ZEND_END_MODULE_GLOBALS (bcmath) macro after being expanded: typedef struct _ struct {bc_num _ zero _; bc_num _ one _; bc_num _ two _; long bc_precision;} zend_bcmath_globals; in fact, the zend_bcmath_globals type is the global space type in the bcmath module. Here, only the zend_bcmath_globals struct is declared. the specific instantiation definition in c is: // zend_bcmath_globals bcmath_globals after expansion; then (bcmath). We can see that ZEND_DECLARE_MODULE_GLOBALS is used to define the variable bcmath_globals. Bcmath_globals is a real global space. It contains four fields. The last field bc_precision corresponds to bcmath. scale in ini configuration. We set the value of bcmath. scale in php. ini. Then, when the bcmath module is started, the value of bcmath. scale is updated to bcmath_globals.bc_precision. Update the value in configuration_hash to the xxx_globals variable defined by each module. This is the so-called function of ini configuration to the module. Once the module is started, these configurations are also in place. Therefore, in the subsequent execution phase, the php module does not need to access configuration_hash again. The module only needs to access its own XXX_globals to obtain the User-Defined configuration. In bcmath_globals, in addition to one field being the ini configuration item, what do other three fields mean? This is the second role of the module's global space. In addition to the ini configuration, it can also store some data during the module's execution. For example, the json module is also a common module in php: ZEND_BEGIN_MODULE_GLOBALS (json) int error_code; ZEND_END_MODULE_GLOBALS (json). You can see that the json module does not require ini configuration, its global space has only one field error_code. Error_code records the errors that occurred in the last execution of json_decode or json_encode. The json_last_error function returns this error_code to help you locate the cause of the error. In order to easily access the global space variables of the module, some macros are proposed in php. For example, if you want to access error_code in json_globals, you can write json_globals.error_code directly (not in a multi-threaded Environment). However, a more common syntax is to define JSON_G macro: # define JSON_G (v) (JSON) we use JSON_G (error_code) to access json_globals.error_code. At the beginning of this article, we mentioned PG, BG, JSON_G, PCRE_G, and XXX_G. These macros are also common in php source code. Now we can easily understand them. PG macros can access the global variables of the Core module, BG can access the global variables of the Standard module, and PCRE_G can access the global variables of the PCRE module. # Define PG (v) (core_globals.v) # define BG (v) (basic_globals.v) 2.2. How do I determine the configurations required for a module? The INI configurations required by the module are defined in each module. For example, the Core module has the following configuration item definition: PHP_INI_BEGIN ()...... values ("display_errors", "1", values, values, display_errors, php_core_globals, core_globals, values) values ("enable_dl", "1", values, OnUpdateBool, enable_dl, values, core_globals) STD_PHP_INI_BOOLEAN ("expose_php", "1", PHP_INI_SYSTEM, OnUpdateBool, expose_php, php_core_glo Bals, core_globals) STD_PHP_INI_BOOLEAN ("safe_mode", "0", PHP_INI_SYSTEM, OnUpdateBool, safe_mode, php_core_globals, core_globals )...... PHP_INI_END () can be found in php-src \ main. the above code is found in more than 450 lines in the c file. Many macros are involved, including ZEND_INI_BEGIN, ZEND_INI_END, PHP_INI_ENTRY_EX, and STD_PHP_INI_BOOLEAN. This article will not describe them one by one, and interested readers can analyze them by themselves. After the above Code is macro expanded, We can get static const zend_ini_entry ini_entries [] = {.. {0, comment, "display_errors", sizeof ("display_errors"), comment, (void *) XtOffsetOf (php_core_globals, display_errors), (void *) & core_globals, NULL, "1", sizeof ("1")-1, NULL, 0, 0, 0, display_errors_mode}, {0, PHP_INI_SYSTEM, "enable_dl", sizeof ("enable_dl "), onUpdateBool, (void *) XtOffsetOf (php_core_globals, enable_d L), (void *) & core_globals, NULL, "1", sizeof ("1")-1, NULL, 0, 0, 0, zend_ini_boolean_displayer_cb}, {0, authorization, "expose_php", sizeof ("expose_php"), OnUpdateBool, (void *) XtOffsetOf (signature, expose_php), (void *) & core_globals, NULL, "1 ", sizeof ("1")-1, NULL, 0, 0, 0, zend_ini_boolean_displayer_cb}, {0, PHP_INI_SYSTEM, "safe_mode", sizeof ("safe_mode"), OnUpdateBool, (void *) XtOf FsetOf (php_core_globals, safe_mode), (void *) & core_globals, NULL, "0", sizeof ("0")-1, NULL, 0, 0, 0, 0, zend_ini_boolean_displayer_cb },... {0, 0, NULL, 0, NULL, 0, NULL, 0, 0, NULL}; we can see that the definition of the configuration item, essentially, it defines an array of the zend_ini_entry type. The field of the zend_ini_entry struct has the following meanings: struct _ zend_ini_entry {int module_number; // module id int modifiable; // modifiable range, such as php. ini, ini_set char * name; // configuration item name uint name_length; ZEND_INI_MH (* on_modify); // callback function, which calls void * mh_arg1 during configuration item registration or modification; // It is usually the offset void * mh_arg2 of the configuration item field in XXX_G; // It is usually XXX_G void * mh_arg3; // It is usually a reserved field and char * value is rarely used; // The value of the configuration item uint value_length; char * orig_value; // the original value of the configuration item uint orig_value_leng Th; int orig_modifiable; // The original modifiable int modified of the configuration item; // whether the modification has occurred. If any, orig_value will save the void (* displayer) value before the modification) (zend_ini_entry * ini_entry, int type) ;}; 2.3, which applies the configuration to the module -- REGISTER_INI_ENTRIES, which can often be seen in PHP_MINIT_FUNCTION of different extensions. REGISTER_INI_ENTRIES is mainly responsible for completing two tasks. First, fill in the global space XXX_G of the module and synchronize the value in configuration_hash to XXX_G. Second, it also generates EG (ini_directives ). REGISTER_INI_ENTRIES is also a macro. After expansion, it is actually the zend_register_ini_entries method. For more information, see the ZEND_API int partition (const zend_ini_entry * ini_entry, int module_number TSRMLS_DC)/* {*/{// ini_entry is an array of the zend_ini_entry type, p is the pointer const zend_ini_entry * p = ini_entry; zend_ini_entry * hashed_ini_entry; zval default_value; // EG (ini_directives) is registered_zend_ini_directives HashTable * directives = ingress; zend_bool Config_directive_success = 0; // remember that the last entry of ini_entry is fixed to {0, 0, NULL ,...} dowhile (p-> name) {config_directive_success = 0; // Add zend_ini_entry pointed by p to EG (ini_directives) if (zend_hash_add (directives, p-> name, p-> name_length, (void *) p, sizeof (zend_ini_entry), (void **) & hashed_ini_entry) = FAILURE) {detail (module_number TSRMLS_CC); return FAILURE ;} hashed_ini_entry-> module_number = Module_number; // query in configuration_hash Based on the name, and put the obtained result in default_value. // note that the value of default_value is relatively primitive, generally numbers, strings, arrays, etc, depends on php. in ini, if (zend_get_configuration_directive (p-> name, p-> name_length, & default_value) = SUCCESS) {// call on_modify to update to the global space XXX_G of the module if (! Response-> on_modify | response-> on_modify (response, Z_STRVAL (default_value), Z_STRLEN (default_value), response-> mh_arg1, response-> mh_arg2, response-> mh_arg3, required TSRMLS_CC) = SUCCESS) {hashed_ini_entry-> value = Z_STRVAL (default_value); hashed_ini_entry-> value_length = Z_STRLEN (default_value); Limit = 1 ;}}// If not found in configuration_hash, use the default value if (! Export & amp; # & gt; on_modify) {modify-& amp; on_modify (hashed_ini_entry, hashed_ini_entry-& gt; value, region-& gt; value_length, hashed_ini_entry, hashed_ini_entry-> mh_arg3, ZEND_INI_STAGE_STARTUP TSRMLS_CC);} p ++;} return SUCCESS;} the logic of the above Code can be expressed as: 1, add the ini configuration item declared by the module to EG (ini_directives. Note that the ini configuration item value may be modified later. 2. Try to find the ini required by each module in configuration_hash. If this parameter can be found, it indicates that the value is configured in the INI file, and the user configuration is used. If OK is not found, it does not matter because the module will bring the default value when declaring the ini. 3. Synchronize the ini value to XX_G. After all, in the php Execution Process, XXX_globals still works. The specific process is to call the on_modify method corresponding to each ini configuration. on_modify is specified when the module declares the ini. Let's take a look at on_modify, which is actually a function pointer. Let's look at the configuration declaration of two specific Core modules: STD_PHP_INI_BOOLEAN ("log_errors", "0", PHP_INI_ALL, OnUpdateBool, log_errors, php_core_globals, core_globals) values ("temperature", "1024", temperature, OnUpdateLong, distance, php_core_globals, core_globals) for log_errors, its on_modify is set to OnUpdateBool, for dimensions, on_modify is set to OnUpdateLong. Let's further assume that we are in php. the configuration in ini is: log_errors = Onlog_errors_max_len = 1024. For details, refer to the OnUpdateBool function: ZEND_API ZEND_INI_MH (OnUpdateBool) {zend_bool * p; // base indicates the address of core_globals char * base = (char *) mh_arg2; // p indicates the offset of the log_errors field added to the core_globals address. // The obtained address is the address p = (zend_bool *) (base + (size_t) mh_arg1) of the log_errors field ); if (new_value_length = 2 & strcasecmp ("on", new_value) = 0) {* p = (zend_bool) 1;} else if (new_val Ue_length = 3 & strcasecmp ("yes", new_value) = 0) {* p = (zend_bool) 1 ;} else if (new_value_length = 4 & strcasecmp ("true", new_value) = 0) {* p = (zend_bool) 1 ;} else {// The value stored in configuration_hash is the string "1" instead of "On" // Therefore, atoi is used to convert it to the number 1 * p = (zend_bool) atoi (new_value);} return SUCCESS;} the most confusing estimation is mh_arg1 and mh_arg2. In fact, compared with the definition of zend_ini_entry described earlier, mh_arg1 and mh_arg2 are easy to understand. Mh_arg1 indicates the byte offset, and mh_arg2 indicates the address of XXX_globals. Therefore, the result of (char *) mh_arg2 + mh_arg1 is the address of a field in XXX_globals. In this case, the log_errors address in core_globals is calculated. Therefore, when OnUpdateBool is finally executed to * p = (zend_bool) atoi (new_value); its function is equivalent to core_globals.log_errors = (zend_bool) atoi ("1"); OnUpdateBool is analyzed, let's take a look at OnUpdateLong and we can see at a glance: ZEND_API ZEND_INI_MH (OnUpdateLong) {long * p; char * base = (char *) mh_arg2; // obtain the address p = (long *) (base + (size_t) mh_arg1); // convert "1024" to the long type and assign it to core_globals.log_errors_max_len * p = zend_atol (new_value, new_value_length); retu Rn SUCCESS;} note that, in the zend_register_ini_entries function, if a configuration exists in configuration_hash, the value and value_length in hashed_ini_entry will be updated after on_modify is called. That is to say, if you have configured it in php. ini, the actual configuration value is stored in EG (ini_directives. If the user is not configured with zend_ini_entry, the default value is stored in EG (ini_directives. The default_value variable in zend_register_ini_entries has a bad name, which may cause misunderstanding. In fact, default_value does not represent the default value, but the value actually configured by the user. 3. So far, the three pieces of data configuration_hash, EG (ini_directives), PG, BG, PCRE_G, JSON_G, XXX_G... are all clear. To sum up, 1. configuration_hash stores the configuration in the php. ini file without verification. Its value is a string. 2. For example (ini_direves VES), The zend_ini_entry defined in each module is stored. if ini is configured (already exists in configuration_hash), the value is replaced with the value in configuration_hash, And the type is still a string. 3, XXX_G. The macro is used to access the global space of the module. This memory space can be used to store the ini configuration and updated using the function specified by on_modify, the data type is determined by the field declaration in XXX_G.

Related Article

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