- URL: https://www.laruence.com/en/2009/12/26/1198.html
- Please include attribution when republishing.
As for where the data in PHP comes from, there are only two kinds:
1. From the code 2. From outside (GET/POST/DB)
For variables in the code (that is, literals), allocation/assignment happens at compile time, they are live during execution, and they are destroyed at request shutdown. For these variables, if you use APC for Opcode caching, the values of this part of the variables are cached.
For variables that come from outside, allocation/assignment happens after the compiler and before execution, and they are destroyed at request shutdown. For these variables, if you use APC for OpCode caching, they are not cached.
Today let's focus on one part of the external variables: the whole lifecycle of data coming from GET.
Suppose the following request arrives:
GET /index.php?name=laruence&career[]=yahoo&career[]=baidu
And, in index.php:
<?php $name = $_GET['name']; $career = $_GET['career']; //array
As we know, at the very end, during execution, the $_GET array must contain the following fragment:
$_GET = array( 'name' => 'laruence', 'career' => array( 'yahoo', 'baidu', ), )
So today let's focus on how the Query String is built into the $_GET array (for how GET variables are generated, please also read my earlier article: "The Generation Process of PHP's Large Variables such as GET/POST"):
When a request arrives, php_request_startup (defined in main.c) is called to set up the scene. This process includes setting timeout values and calling each module's request initialization function. And of course, it also includes the thing we care about: creating the variable environment.
php_hash_environment initializes each predefined large variable in turn according to variables_order in php.ini. So for $_GET:
...
case 'g':
case 'G':
if (!_gpc_flags[2]) {
sapi_module.treat_data(PARSE_GET, NULL, NULL TSRMLS_CC);
_gpc_flags[2] = 1;
if (PG(register_globals)) {
php_autoglobal_merge(&EG(symbol_table),
Z_ARRVAL_P(PG(http_globals)[TRACK_VARS_GET]) TSRMLS_CC);
}
}
break;
...
Roughly speaking, this logic first uses treat_data to generate the variable hash (PG(http_globals)[TRACK_VARS_GET]), and if auto_register_globals is enabled, it then adds the variables in the $_GET array into the symbol table.
treat_data is a member of sapi_module_struct:
Note: This article is based on the apache2handler SAPI, and this startup process differs slightly from the startup process in my earlier article on SAPI. php5 registers apache2's ap_hook_post_config hook and starts php when the apache server starts (php_apache_server_startup, defined in sapi/apache2hander/sapi_apache2.c); in that function it calls sapi_startup to start the SAPI, then calls php_apache2_startup to register the sapi module struct, then calls php_module_startup to initialize PHP, which also initializes the ZEND engine and fills in the treat_data member of zend_module_struct (via php_startup_sapi_content_types) with php_default_treat_data
Now let's go back and continue looking at treat_data (that is, php_default_treat_data):
....
if (arg == PARSE_GET) { /* GET data */
c_var = SG(request_info).query_string;
if (c_var && *c_var) {
res = (char *) estrdup(c_var);
free_buffer = 1;
} else {
free_buffer = 0;
}
} else if (arg == PARSE_COOKIE) { /* Cookie data */
....
In the logic above, res is assigned query_string. SG(request_info) is a struct representing the information of the current request, and its query_string is obtained in php_apache_request_ctor by copying args from apache's request_rec struct.
For the example in this article, res is now "name=laruence&career[]=yahoo&career[]=baidu".
Continuing in treat_data, the following logic is:
var = php_strtok_r(res, separator, &strtok_buf);
...
while (var) {
val = strchr(var, '=');
if (arg == PARSE_COOKIE) {
/* Remove leading spaces from cookie names,
needed for multi-cookie header where ; can be followed by a space */
while (isspace(*var)) {
var++;
}
if (var == val || *var == '