博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
PHP 源码探秘 - 在解析外部变量时的一个问题
阅读量:7103 次
发布时间:2019-06-28

本文共 5581 字,大约阅读时间需要 18 分钟。

安利

原文:我的个人博客 工作了两三年,技术停滞不前,迷茫没有方向,安利一波我的直播 [PHP 进阶之路][1]

bug 复现

有个朋友跟我描述了一个bug,要我帮看看是什么情况。原本他有一个表单,如下。

复制代码

但是有一个前端插件会动态插入两个input,最后ajax提交的时候是

复制代码

后端

当我们用 php 来接收的时候

echo file_get_contents('php://input');echo "\n";var_export($_POST);echo "\n";echo PHP_VERSION;复制代码

结果是

id%5B%5D=1&id%5B%5D_text=a&id%5B%5D=2&id%5B%5D_text=barray (  'id' =>   array (    0 => '1',    1 => 'a',    2 => '2',    3 => 'b',  ),)7.0.10复制代码

使用 nodejs 尝试

var http = require('http');var querystring = require('querystring');var postHTML = '
' + '
' + '
' + '
';http.createServer(function (req, res) { var body = ""; req.on('data', function (chunk) { body += chunk; console.log(body); body = querystring.parse(body); console.log(body); }); req.on('end', function () { res.writeHead(200, {
'Content-Type': 'text/html; charset=utf8'}); res.write(postHTML); res.end(); });}).listen(3000);复制代码

控制台输出的是

id%5B%5D=1&id%5B%5D_text=a&id%5B%5D=2&id%5B%5D_text=b{ 'id[]': [ '1', '2' ], 'id[]_text': [ 'a', 'b' ] }复制代码

小结

在接收外部变量时,多个相同的外部变量,在nodejs中会被放在一个数组里面,而php中则是后者覆盖前者,如果需要传递数组变量,则在变量名后面添加上[]这个不兼容,ok,是语言的特性能接受

但是在php中在解析id[]_text的数据的时候都转换成id[]了,这点就有点坑了。rfc 在这方面也没看到有规定否则不会出现两种语言解析不一致的情况了。

源码分析

也就是说 php 后端在解析的时候的问题。那只能从源码里一探究竟看php是如何解析post数据的了。 我把子进程数修改为1,然后根据pid来调试

gdb -p 22892...(gdb) b /data/soft/php-7.1.10/main/php_variables.c:php_register_variable_exBreakpoint 1 at 0x812877: file /data/soft/php-7.1.10/main/php_variables.c, line 70.(gdb) i bNum     Type           Disp Enb Address            What1       breakpoint     keep y   0x0000000000812877 in php_register_variable_ex at /data/soft/php-7.1.10/main/php_variables.c:70(gdb)(gdb) cContinuing.Breakpoint 1, php_register_variable_ex (var_name=0x7fb5b9056218 "id[]", val=0x7ffff23dacd0, track_vars_array=0xf114a0) at /data/soft/php-7.1.10/main/php_variables.c:7070		if (track_vars_array && Z_TYPE_P(track_vars_array) == IS_ARRAY) {(gdb) bt#0  php_register_variable_ex (var_name=0x7fb5b9056218 "id[]", val=0x7ffff23dacd0, track_vars_array=0xf114a0) at /data/soft/php-7.1.10/main/php_variables.c:70#1  0x00000000005af0d1 in php_sapi_filter (arg=
, var=0x7fb5b9056218 "id[]", val=0x7ffff23dad48, val_len=1, new_val_len=0x7ffff23dad40) at /data/soft/php-7.1.10/ext/filter/filter.c:465#2 0x00000000008135d0 in add_post_var (arr=0x7ffff23dce50, var=0x7ffff23dcda0, eof=
) at /data/soft/php-7.1.10/main/php_variables.c:308#3 0x0000000000813ce6 in add_post_vars (content_type_dup=
, arg=0x7ffff23dce50) at /data/soft/php-7.1.10/main/php_variables.c:324#4 php_std_post_handler (content_type_dup=
, arg=0x7ffff23dce50) at /data/soft/php-7.1.10/main/php_variables.c:361#5 0x000000000080cfe0 in sapi_handle_post (arg=
) at /data/soft/php-7.1.10/main/SAPI.c:174#6 0x00000000008133cf in php_default_treat_data (arg=0, str=0x0, destArray=
) at /data/soft/php-7.1.10/main/php_variables.c:423#7 0x000000000066c581 in mbstr_treat_data (arg=0, str=0x0, destArray=0x0) at /data/soft/php-7.1.10/ext/mbstring/mb_gpc.c:69#8 0x0000000000812463 in php_auto_globals_create_post (name=0x7fb5b1ddf768) at /data/soft/php-7.1.10/main/php_variables.c:720#9 0x000000000084125f in zend_activate_auto_globals () at /data/soft/php-7.1.10/Zend/zend_compile.c:1681#10 0x000000000081282e in php_hash_environment () at /data/soft/php-7.1.10/main/php_variables.c:690#11 0x0000000000804c11 in php_request_startup () at /data/soft/php-7.1.10/main/main.c:1672#12 0x0000000000918282 in main (argc=
, argv=
) at /data/soft/php-7.1.10/sapi/fpm/fpm/fpm_main.c:1904(gdb)复制代码

那么我们看php_register_variable_ex怎么写的,源码精简了下,如下

#include 
#include
#include
#include
void php_register_variable_ex(char *var_name);typedef unsigned char zend_bool;int main() { char *var_name = "id 1.2[]_3"; php_register_variable_ex(var_name); return 0;}void php_register_variable_ex(char *var_name){ char *p = NULL; char *ip = NULL; /* index pointer */ char *index; char *var, *var_orig; size_t var_len, index_len; zend_bool is_array = 0; assert(var_name != NULL); /* ignore leading spaces in the variable name */ while (*var_name==' ') { var_name++; } /* * Prepare variable name */ var_len = strlen(var_name); var = var_orig = malloc(var_len + 1); memcpy(var_orig, var_name, var_len + 1); /* 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; } } var_len = p - var; printf("var\t%s\n",var); printf("var_len\t%zu\n",var_len);}复制代码

根据php_register_variable_ex里面的规则:

  • name里面的.都被替换成_
  • name里遇到[则认为是数组,数组的key为[前面的字符串,后面的都被舍去。

上面我模拟了表单提交一个nameid 1.2[]_3时,输出结果就是

var	id_1_2var_len	6复制代码

思考为什么

上面的替换规则在官方手册中有说明

Dots and spaces in variable names are converted to underscores.

但是没有看到命名中关于不使用[]后连接字符串的说明。

extract

难道是因为extract原因,如果数组key里面有[],则没办法正常执行了。

$foo["id"] = 1;$foo["id[]_text"] = 2;var_export($foo);extract($foo);var_export(get_defined_vars());复制代码

试了以上代码,也印证了我的想法id[]_text的值直接丢失了。

所以

  1. php在接受这样命名的(foo[]boo)外部变量名是不符合规范的,手册文档需要补全
  2. php在接受这样不符合命名规范的(foo[]boo)外部变量的时候是强制转换成数组,还是直接丢弃呢?

转载于:https://juejin.im/post/5bf0f78ae51d451dca4766c5

你可能感兴趣的文章
perl pool ping
查看>>
weblogic10.3.6安装、卸载
查看>>
Struts2+JQuery+Json实例(2)
查看>>
grafana安装
查看>>
mysql“Access denied for user 'root'@'localhost'”问题的解决
查看>>
基于java的分布式爬虫
查看>>
React Native系列——Navigator组件的使用介绍
查看>>
bootstrap 代码
查看>>
jsp自定义标签
查看>>
我的技术博客-公告板!
查看>>
Websense:Android的安全性令人担忧
查看>>
Office 365 系列之四:添加自定义域
查看>>
Android应用程序启动过程——Launcher源码分析
查看>>
(实战)从关联表中取得JSON数据
查看>>
maven添加repository仓库
查看>>
vSAN 6.0设计与规模设定——vSAN设计概览
查看>>
zabbix邮件报警之完整总结
查看>>
我的友情链接
查看>>
Windows 10:现代化世界中安全与身份的守卫者
查看>>
查找相关数据结构和算法
查看>>