PHP的GET/POST参数里带有.号的问题
最近看其他项目的框架代码,发现在处理xxx.n这类参数时,使用的是$_GET['xxx_n']。
我的理解,这里不是应该是用$_GET['xxx.n']么?
为什么会变成\_了呢?
现象
原因分析
转载自鸟哥的《PHP的GET/POST等大变量生成过程》
首先明确一个问题,PHP的变量名中是不能包含点号的。 但是为了处理表单中的点号命名,PHP就会自动把点号(.)转换成下划线(_)。
要知道PHP是怎么处理的,首先我们要了解,$\_GET, $\_POST, $\_COOKIE等变量的构造过程。
在每个请求到来以后,apache处理到response阶段的时候, 会将控制权交给PHP模块, PHP模块会在处理请求之前首先间接调用php\_request_startup (具体调用序列是send\_php -> apache\_php\_module\_main -> php\_request\_startup, 关于这部门可以参看我前面的文章( PHP Life Cycle) , 在php\_request\_startup中:int php_request_startup(TSRMLS_D) { int retval = SUCCESS; #if PHP_SIGCHILD signal(SIGCHLD, sigchld_handler); #endif if (php_start_sapi() == FAILURE) { return FAILURE; } php_output_activate(TSRMLS_C); sapi_activate(TSRMLS_C); php_hash_environment(TSRMLS_C); zend_try { PG(during_request_startup) = 1; php_output_activate(TSRMLS_C); if (PG(expose_php)) { sapi_add_header(SAPI_PHP_VERSION_HEADER, sizeof(SAPI_PHP_VERSION_HEADER)-1, 1); } } zend_catch { retval = FAILURE; } zend_end_try(); return retval; }
注意其中的php\_hash\_environment(TSRMLS\_C) 函数调用 , 这个函数就是在请求处理前, 初始化请求相关的变量的函数。
这个函数定义在: main/php\_variables.c中 , 有兴趣的可以看看:int php_hash_environment(TSRMLS_D) { char *p; unsigned char _gpc_flags[5] = {0, 0, 0, 0, 0}; zend_bool jit_initialization = (PG(auto_globals_jit) && !PG(register_globals) && !PG(register_long_arrays)); struct auto_global_record { char *name; uint name_len; char *long_name; uint long_name_len; zend_bool jit_initialization; } auto_global_records[] = { { "_POST", sizeof("_POST"), "HTTP_POST_VARS", sizeof("HTTP_POST_VARS"), 0 }, { "_GET", sizeof("_GET"), "HTTP_GET_VARS", sizeof("HTTP_GET_VARS"), 0 }, { "_COOKIE", sizeof("_COOKIE"), "HTTP_COOKIE_VARS", sizeof("HTTP_COOKIE_VARS"), 0 }, { "_SERVER", sizeof("_SERVER"), "HTTP_SERVER_VARS", sizeof("HTTP_SERVER_VARS"), 1 }, { "_ENV", sizeof("_ENV"), "HTTP_ENV_VARS", sizeof("HTTP_ENV_VARS"), 1 }, { "_FILES", sizeof("_FILES"), "HTTP_POST_FILES", sizeof("HTTP_POST_FILES"), 0 }, }; size_t num_track_vars = sizeof(auto_global_records)/sizeof(struct auto_global_record); size_t i; /* jit_initialization = 0; */ for (i=0; i<num_track_vars; i++) { PG(http_globals)[i] = NULL; } for (p=PG(variables_order); p && *p; p++) { switch(*p) { case 'p': case 'P': if (!_gpc_flags[0] && !SG(headers_sent) && SG(request_info).request_method && !strcasecmp(SG(request_info).request_method, "POST")) { sapi_module.treat_data(PARSE_POST, NULL, NULL TSRMLS_CC); /* POST Data */ _gpc_flags[0] = 1; if (PG(register_globals)) { php_autoglobal_merge(&EG(symbol_table), Z_ARRVAL_P(PG(http_globals)[TRACK_VARS_POST]) TSRMLS_CC); } } break; ....以下省略: }}}
可以看到,离成功不远了,sapi\_module.treat_data 也就是php\_default\_treat\_data,
在php\_default\_treat\_data中,对于变量,都调用php\_register\_variable\_safe来注册变量, 而php\_register\_variable\_safe最终会调用php\_register\_variable\_ex:PHPAPI void php_register_variable_ex(char *var, zval *val, zval *track_vars_array TSRMLS_DC) { char *p = NULL; char *ip; /* index pointer */ char *index, *escaped_index = NULL; int var_len, index_len; zval *gpc_element, **gpc_element_p; zend_bool is_array = 0; HashTable *symtable1 = NULL; assert(var != NULL); if (track_vars_array) { symtable1 = Z_ARRVAL_P(track_vars_array); } else if (PG(register_globals)) { symtable1 = EG(active_symbol_table); } if (!symtable1) { /* Nothing to do */ zval_dtor(val); return; } /* * Prepare variable name */ /* ignore leading spaces in the variable name */ while (*var && *var==' ') { var++; } /* ensure that we don't have spaces or dots in the variable name (not binary safe) */ //特别注意以下这段。。。。 for (p = var; *p; p++) { if (*p == ' ' || *p == '.') { *p='_'; } else if (*p == '[') { is_array = 1; ip = p; *p = 0; break; } ....以下省略
呵呵,问题的原因找到了, 就是在php_register_variable的时候,会将(.)转换成(_).
结论
PHP会把http请求里的点号(.)和空格( )都转换为下划线(_).