|
This article will not be described in detail the use of a ini configuration items, which are already in the manual explain everything. I just think from a certain angle to dig implementation mechanism php, it will involve some of the core aspects of the knowledge of php.
Students know using php php.ini configuration will take effect throughout the life cycle of SAPI. In the process of implementation of a php script, if you manually modify the ini configuration will not start acting. At this point if you can not restart apache or nginx, etc., then it can only explicitly call ini_set interface php code. php ini_set function is to provide us with a dynamic configuration changes, you need to pay attention to is configured using the configuration and set ini file ini_set set in the time range of its entry into force is not the same. At the end php script execution, ini_set settings will be immediately invalidated.
Therefore, this article intends to two points, the first chapter describes the php.ini configuration principle, speak second in dynamically modify php configuration.
php.ini configuration generally involves three data, configuration_hash, EG (ini_directives) and PG, BG, PCRE_G, JSON_G, XXX_G like. If you do not know the meaning of these three data does not matter, it will be explained in detail below.
1, parsing INI configuration files
Because php.ini SAPI needs remain in effect in the process, then parse ini file and uses them to build php configuration work, must be the beginning of the occurrence of SAPI. In other words, it must have occurred during startup of php. php require any of the actual request prior to arrival, the interior has been generated good these configurations.
Reflected php kernel is php_module_startup function.
php_module_startup mainly responsible for php start, when usually it is called at the beginning of SAPI. btw, there is a common function is php_request_startup, which is responsible for each request initialize the time comes, php_module_startup and php_request_startup are two logos of action, but not within the scope of this article to explore them for analysis.
For example, when doing the following apache php attached to a module, so apache starts, it will activate all these module, including php module. Upon activation php module, it will call to php_module_startup. php_module_startup function completes the vast amount of work, once the end of the call php_module_startup means, OK, php has started, now accepts the request and respond.
In php_module_startup functions, the parsing ini files associated with this implementation 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;
}
We can see, in fact, called php_init_config function, to complete the ini file parse. parse work mainly lex & grammar analysis and ini file key, value key-value pairs extracted and saved. php.ini format is very simple, on the left side of the equal sign key, the right side value. Whenever one pair kv after being extracted, php will store them where? The answer is that the previously mentioned configuration_hash.
static HashTable configuration_hash;
configuration_hash php_ini.c statement, which is a HashTable type of data structure. As the name suggests, in fact, Zhang hash table. Digression, before php5.3 version is unable to get configuration_hash, because it is a static variable of php_ini.c file. Later php5.3 added php_ini_get_configuration_hash interface, which directly back & configuration_hash, so that each php extensions can easily glimpse the whole picture ... really configuration_hash Cape rejoicing Ben ...
Note four points:
First, php_init_config not make any addition to the lexical grammar check. That is, if we add a line hello = world in the ini file, as long as it is a properly formatted configuration items, then the final configuration_hash will contain a key to hello, and the value of world elements, reflecting the maximum configuration_hash ini file.
Second, ini file allows us to be configured as an array. For example ini file to write the following three lines:
drift.arr [] = 1
drift.arr [] = 2
drift.arr [] = 3
Then the final generated configuration_hash table, it will exist as a key element drift.arr, its value as an array containing three numbers 1,2,3. This is an extremely rare configuration.
Third, php allows us apart from the default php.ini file (to be exact php-% s.ini) outside, in addition to build some ini files. These ini files will be placed in an additional directory. The directory specified by the environment variable PHP_INI_SCAN_DIR, when php_init_config resolve over php.ini, this directory will be scanned again, and then find a directory of all the .ini files to analyze. kv these additional ini file generated key pair will be added to the configuration_hash go.
This is a useful feature occasionally, assuming our own development php extension, but do not want to configure mixed php.ini, you can also write an ini, and by PHP_INI_SCAN_DIR tell php where to go find it. Of course, the disadvantages are also obvious that you need to set additional environment variables for the support. A better solution is to extend the developers themselves call php_parse_user_ini_file or zend_parse_ini_file to resolve the corresponding ini file.
Fourth, configuration_hash the, key is a string, then what type of value is? The answer is a string (in addition to the very special array). Specifically, for example, the following configuration:
display_errors = On
log_errors = Off
log_errors_max_len = 1024
Then the last configuration_hash actually stored as key-value pairs:
key: "display_errors"
val: "1"
key: "log_errors"
val: ""
key: "log_errors_max_len"
val: "1024"
Note log_errors, store the value of even "0" is not, it is a literally empty string. Further, log_errors_max_len is not digital, but the string 1024.
Analysis of this point, basically parsing ini files relevant content say clear. Briefly summarize:
1, parsing ini occurred php_module_startup stage
2, in the analysis results stored in configuration_hash.
2, the configuration is applied to the module
Php general structure can be regarded as the lowest layer there is a zend engine, which is responsible for interacting with the OS, compile php code to provide memory-managed, etc., in the upper zend engine, lined with a lot of modules. On top of which a Core module, as well as others such as Standard, PCRE, Date, Session, etc ... These modules are another name, called php extension. We can simply understood as each module will provide a functional interface for developers to call, for example, commonly used such as explode, trim, array and other built-in functions, is provided by the Standard module.
Why talk about this because in addition to the php.ini for php itself, that is, for some configurations Core modules (such as safe_mode, display_errors, max_execution_time, etc.), as well as a considerable number of other configurations for different modules.
For example, date module, which provides a common date, time, strtotime function and so on. In php.ini, its configuration is as follows:
[Date]
; Date.timezone = 'Asia / Shanghai'
; Date.default_latitude = 31.7667
; Date.default_longitude = 35.2333
; Date.sunrise_zenith = 90.583333
; Date.sunset_zenith = 90.583333
In addition to these modules have an independent configurations, zend engine also can be equipped, only zend engine can be equipped with very few items, only error_reporting, zend.enable_gc and detect_unicode three.
In the previous section we have already talked about, php_module_startup calls php_init_config, its purpose is to parse ini file and generate configuration_hash. Then the next will be in php_module_startup in doing things? Obviously, that is, the configuration will configuration_hash role in Zend, Core, Standard, SPL different modules. Of course, this is not an overnight process, because php usually contains many modules, php start the process of these modules will be sequentially started. Then, configure the module A process is occurred during startup module A's.
Have extended experience in the development of the students will be explicitly stated, activate Module A is not in PHP_MINIT_FUNCTION (A) in it?
Yes, if you need to configure the module A, then PHP_MINIT_FUNCTION, you can call REGISTER_INI_ENTRIES () to complete. REGISTER_INI_ENTRIES name based on the current module configuration items that need to find configuration_hash configuration values set by the user, and updates to the module own global space.
2.1, the module global space
To understand how to configure the ini before from configuration_hash applied to each module, it is necessary to first understand what php modules global space. For different php module, you can open up one of their own storage space, and this space for this module, it is globally visible. In general, it will be used to store the ini configuration required for this module. That is, configuration_hash configuration items will eventually be stored in the global space. During the execution of the module, users only need to access this global space, you can get set up for the user of the module. Of course, it is also often used to record data in the intermediate module implementation process.
We bcmath module to illustrate, bcmath is a provider of mathematical computing interface php module, first let's look at what it 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 only one configuration item, we can configure php.ini with bcmath.scale bcmath module.
Then continue to see the global space defined bcmatch module. The following statement in php_bcmath.h in:
ZEND_BEGIN_MODULE_GLOBALS (bcmath)
bc_num _zero_;
bc_num _one_;
bc_num _two_;
long bc_precision;
ZEND_END_MODULE_GLOBALS (bcmath)
After macro expansion, namely:
typedef struct _zend_bcmath_globals {
bc_num _zero_;
bc_num _one_;
bc_num _two_;
long bc_precision;
} Zend_bcmath_globals;
In fact, zend_bcmath_globals bcmath module type is the type of global space. Here we are just declared zend_bcmath_globals structure, as well as specific examples of the definitions in bcmath.c:
// Expand after the zend_bcmath_globals bcmath_globals;
ZEND_DECLARE_MODULE_GLOBALS (bcmath)
As it can be seen, with ZEND_DECLARE_MODULE_GLOBALS complete the definition of variables bcmath_globals.
bcmath_globals is a true global space, which contains four fields. Its last field bc_precision, ini configuration corresponding to bcmath.scale. We set bcmath.scale values in php.ini, then start bcmath module when, bcmath.scale value is updated to bcmath_globals.bc_precision go.
The configuration_hash in value, updates to each module own xxx_globals variable defined, is called the ini configuration applied to the module. Once the module startup is complete, then the role of these configurations are also in place. Therefore, in the subsequent stages of implementation, php module without having to visit again configuration_hash, module only need to access your XXX_globals, you can get the configuration set by the user.
bcmath_globals, in addition to a configuration item field for the ini, why there are other three fields meaning? This is the second function module in the global space, which in addition to ini configuration, you can execute the process some of the data storage module.
As another example json module, php is a very commonly used modules:
ZEND_BEGIN_MODULE_GLOBALS (json)
int error_code;
ZEND_END_MODULE_GLOBALS (json)
We can see the json module does not need ini configuration that only a global space field error_code. error_code records the last execution error occurred json_decode or json_encode in. json_last_error function is to return to this error_code, to help users locate the error.
Very convenient to be able to access the module global variable space, php convention made some macros. For example, we want to access the json_globals error_code, of course, can be written as json_globals.error_code (multi-threaded environment does not work), but a more general wording is defined JSON_G macro:
#define JSON_G (v) (json_globals.v)
We use JSON_G (error_code) to access json_globals.error_code. When the beginning of this article, I mentioned PG, BG, JSON_G, PCRE_G, XXX_G etc. These macros php source code is also very common. Now we can easily understand them, PG macros can access the global variables Core module, BG accessing global variables Standard module, PCRE_G the access to the global variable PCRE module.
#define PG (v) (core_globals.v)
#define BG (v) (basic_globals.v)
2.2, how to determine what needs a module configuration?
What kind of module INI configuration, each module are in their definition. For example, the Core module, there are configuration items defined as follows:
PHP_INI_BEGIN ()
......
STD_PHP_INI_ENTRY_EX ( "display_errors", "1", PHP_INI_ALL, OnUpdateDisplayErrors, display_errors, php_core_globals, core_globals, display_errors_mode)
STD_PHP_INI_BOOLEAN ( "enable_dl", "1", PHP_INI_SYSTEM, OnUpdateBool, enable_dl, php_core_globals, core_globals)
STD_PHP_INI_BOOLEAN ( "expose_php", "1", PHP_INI_SYSTEM, OnUpdateBool, expose_php, php_core_globals, core_globals)
STD_PHP_INI_BOOLEAN ( "safe_mode", "0", PHP_INI_SYSTEM, OnUpdateBool, safe_mode, php_core_globals, core_globals)
......
PHP_INI_END ()
The code can be found in php-src main main.c file about 450+ lines. Wherein the macro more involved, there ZEND_INI_BEGIN, ZEND_INI_END, PHP_INI_ENTRY_EX, STD_PHP_INI_BOOLEAN and so on, this article does not enumerate, interested readers can own analysis.
The code obtained after macro expansion:
static const zend_ini_entry ini_entries [] = {
..
{0, PHP_INI_ALL, "display_errors", sizeof ( "display_errors"), OnUpdateDisplayErrors, (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_dl), (void *) & core_globals, NULL, "1", sizeof ( "1") - 1, NULL, 0, 0, 0, zend_ini_boolean_displayer_cb},
{0, PHP_INI_SYSTEM, "expose_php", sizeof ( "expose_php"), OnUpdateBool, (void *) XtOffsetOf (php_core_globals, 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 *) XtOffsetOf (php_core_globals, safe_mode), (void *) & core_globals, NULL, "0", sizeof ( "0") - 1, NULL, 0, 0, 0, zend_ini_boolean_displayer_cb},
...
{0, 0, NULL, 0, NULL, NULL, NULL, NULL, NULL, 0, NULL, 0, 0, 0, NULL}
};
We see that the definition of configuration items, which in essence is the definition of an array zend_ini_entry type. Field specific meaning zend_ini_entry structure is as follows:
struct _zend_ini_entry {
id // module; int module_number
int modifiable; // range may be modified, such as php.ini, ini_set
// Name of the configuration item; char * name
uint name_length;
ZEND_INI_MH ((* on_modify)); // callback function when register or modify configuration items will be called
void * mh_arg1; // usually configuration item field in the offset XXX_G
void * mh_arg2; // usually XXX_G
void * mh_arg3; // usually reserved field, rarely used
// Value of the configuration item; char * value
uint value_length;
char * orig_value; // original values of configuration items
uint orig_value_length;
int orig_modifiable; // original configuration item modifiable
int modified; // if there have been changes, if modified, then saved orig_value previous values
void (* displayer) (zend_ini_entry * ini_entry, int type);
};
2.3, the configuration is applied to the module --REGISTER_INI_ENTRIES
Often can be seen in different REGISTER_INI_ENTRIES extended PHP_MINIT_FUNCTION inside. REGISTER_INI_ENTRIES mainly responsible for the completion of two things. First, the global space XXX_G module filled synchronization configuration_hash the value to XXX_G go. Second, it generates EG (ini_directives).
REGISTER_INI_ENTRIES also a macro, after expansion but in reality for zend_register_ini_entries methods. Specifically, under zend_register_ini_entries implementation:
ZEND_API int zend_register_ini_entries (const zend_ini_entry * ini_entry, int module_number TSRMLS_DC) / * {{{* /
{
// Ini_entry to zend_ini_entry array type, p is a pointer to an array each
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 = registered_zend_ini_directives;
zend_bool config_directive_success = 0;
// Remember the last one ini_entry fixed {0, 0, NULL, ...} it
while (p-> name) {
config_directive_success = 0;
// P points zend_ini_entry will join EG (ini_directives)
if (zend_hash_add (directives, p-> name, p-> name_length, (void *) p, sizeof (zend_ini_entry), (void **) & hashed_ini_entry) == FAILURE) {
zend_unregister_ini_entries (module_number TSRMLS_CC);
return FAILURE;
}
hashed_ini_entry-> module_number = module_number;
// Name to configuration_hash query based on the results were out of place in default_value
// Note default_value value compared to the original, usually numbers, strings, arrays, etc., depending on the wording of the php.ini
if ((zend_get_configuration_directive (p-> name, p-> name_length, & default_value)) == SUCCESS) {
// Call on_modify updates to the module in the global space XXX_G
if (! hashed_ini_entry-> on_modify || hashed_ini_entry-> on_modify (hashed_ini_entry, Z_STRVAL (default_value), Z_STRLEN (default_value), hashed_ini_entry-> mh_arg1, hashed_ini_entry-> mh_arg2, hashed_ini_entry-> mh_arg3, ZEND_INI_STAGE_STARTUP TSRMLS_CC) == SUCCESS) {
hashed_ini_entry-> value = Z_STRVAL (default_value);
hashed_ini_entry-> value_length = Z_STRLEN (default_value);
config_directive_success = 1;
}
}
// If configuration_hash is not found, the default value
if (! config_directive_success && hashed_ini_entry-> on_modify) {
hashed_ini_entry-> on_modify (hashed_ini_entry, hashed_ini_entry-> value, hashed_ini_entry-> value_length, hashed_ini_entry-> mh_arg1, hashed_ini_entry-> mh_arg2, hashed_ini_entry-> mh_arg3, ZEND_INI_STAGE_STARTUP TSRMLS_CC);
}
p ++;
}
return SUCCESS;
}
In simple terms, the logic of the code above can be expressed as:
1, add ini CI module declaration to EG (ini_directives) in. Note that the value in the ini configuration items may subsequently be modified.
2, try to find the configuration_hash ini each module needed.
If you can find, indicating the user Xiao ini file to configure the value, use the user's configuration.
If none is found, OK, it does not matter, because the module in a statement ini time, will take the default value.
3, the value of ini sync to XX_G inside. After all, during the execution of php, the act or these XXX_globals. The specific procedure is to call each ini configuration corresponding on_modify Complete, on_modify by the modules specified in the declaration ini time.
Let's look specifically on_modify, it is actually a function pointer, look at two specific configuration statements Core module:
STD_PHP_INI_BOOLEAN ( "log_errors", "0", PHP_INI_ALL, OnUpdateBool, log_errors, php_core_globals, core_globals)
STD_PHP_INI_ENTRY ( "log_errors_max_len", "1024", PHP_INI_ALL, OnUpdateLong, log_errors_max_len, php_core_globals, core_globals)
For log_errors, it is set to on_modify OnUpdateBool, for log_errors_max_len, then on_modify is set to OnUpdateLong.
Suppose further that we configured in php.ini as follows:
log_errors = On
log_errors_max_len = 1024
Specifically, under OnUpdateBool function:
ZEND_API ZEND_INI_MH (OnUpdateBool)
{
zend_bool * p;
// Base address represents core_globals
char * base = (char *) mh_arg2;
// P represents core_globals address plus the offset field log_errors
// Get the address field is log_errors
p = (zend_bool *) (base + (size_t) mh_arg1);
if (new_value_length == 2 && strcasecmp ( "on", new_value) == 0) {
* P = (zend_bool) 1;
}
else if (new_value_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 {
// Configuration_hash value is stored in the string "1" instead of "On"
// So here with atoi into digital 1
* P = (zend_bool) atoi (new_value);
}
return SUCCESS;
}
The most puzzling is estimated that mh_arg1 mh_arg2 and, in fact, control the front zend_ini_entry definitions, mh_arg1, mh_arg2 still very easy to fathom. mh_arg1 represent the byte offset, mh_arg2 represents XXX_globals address. Therefore, (char *) mh_arg2 + mh_arg1 the result is a field XXX_globals address. Specific to this case, and that is core_globals in log_errors address calculation. Therefore, when the last execution to OnUpdateBool
* P = (zend_bool) atoi (new_value);
Its role is equivalent to
core_globals.log_errors = (zend_bool) atoi ( "1");
Analysis over OnUpdateBool, we look OnUpdateLong they feel a glance:
ZEND_API ZEND_INI_MH (OnUpdateLong)
{
long * p;
char * base = (char *) mh_arg2;
// Get log_errors_max_len address
p = (long *) (base + (size_t) mh_arg1);
// The "1024" is converted to long type, and assigned to core_globals.log_errors_max_len
* P = zend_atol (new_value, new_value_length);
return SUCCESS;
}
Finally, it should be noted that, zend_register_ini_entries function, if there is a configuration configuration_hash, then when the end of the call on_modify, hashed_ini_entry value_length and the value is updated. That is, if the user-configured in php.ini, the EG (ini_directives) is stored in the actual configuration values. If the user does not match, EG (ini_directives) is stored in the default value declared zend_ini_entry given.
zend_register_ini_entries in default_value variable naming relatively poor, very easily lead to misunderstanding. In fact default_value does not mean that the default values, but rather that the actual value of the user configuration.
3, summary
So far, three data configuration_hash, EG (ini_directives) and PG, BG, PCRE_G, JSON_G, XXX_G ... have all confessed clear.
in conclusion:
1, configuration_hash, storage php.ini configuration file, not to check, the value of the string.
2, EG (ini_directives), each module is stored in zend_ini_entry defined, if the user (configuration_hash exist) in the php.ini configuration too, the value is replaced configuration_hash the value, type is still string.
3, XXX_G, the macro is used to access the module's global space, this memory space available to store ini configuration and functions specified by on_modify update its data type is determined by XXX_G field declarations to decide. |
|
|
|