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请求里的点号(.)和空格( )都转换为下划线(_).

忙碌的十一假期

待收的玉米地

十一假期,正好赶上家里秋收。
虽然很是辛苦,但是分担了父母的一些负担。
顺便,感谢老婆,辛苦了。

微云分享的链接会自动失效?

之前用微云分享了一个typecho的插件包,结果后来发现过期了。
百度一番才知道,貌似是多久没人下载就会自动失效。
好悲剧的说,以后用其他网盘分享吧。

让Sublime Text 2 支持代码注释

安装方法

  1. 首先安装一个基础的、必备的包管理:Package Control,用来以后安装插件用的。

    官方安装文档

  2. 通过快捷键 Ctrl+Shift+P, 打开Package Control来安装插件。在输入框里输入install,然后选择 Install Package。

    安装界面

  3. 在输入框里输入DocBlockr,然后回车就会自动安装。

使用方法

在函数前输入/**,然后按Tab,就会自动出现注释了。

官方示例

codeigniter框架开发中遇到的坑

  1. Controller子文件夹的问题

    目录结构:

     Controllers
         |----star
                |----manage.php
         |----star.php
    

    访问/star/manage的时候,提示对应方法不存在。

    看了下路由类,发现CI是会先找根目录对应的文件存不存在,存在的话直接返回根目录的文件。

        function _validate_request($segments)
        {
            if (count($segments) == 0)
            {
                return $segments;
            }

            // Does the requested controller exist in the root folder?
            if (file_exists(APPPATH.'controllers/'.$segments[0].'.php'))
            {
                return $segments;
            }

            // Is the controller in a sub-folder?
            if (is_dir(APPPATH.'controllers/'.$segments[0]))
            ...

  1. db返回的默认为对象而非数组

     $query = $this->db->get('test');
     $result = $query->result();
    

    上面的语句执行后,result是一个对象,而非数组。
    如果需要使用数组,请使用下面的代码。

     $query = $this->db->get($this->_table);
     $result = $query->result('array');