摘要:在這方面也沒(méi)看到有規(guī)定否則不會(huì)出現(xiàn)兩種語(yǔ)言解析不一致的情況了。源碼分析也就是說(shuō)后端在解析的時(shí)候的問(wèn)題。那只能從源碼里一探究竟看是如何解析數(shù)據(jù)的了。
安利
原文:我的個(gè)人博客 https://mengkang.net/1301.htmlbug 復(fù)現(xiàn)
工作了兩三年,技術(shù)停滯不前,迷茫沒(méi)有方向,安利一波我的直播 PHP 進(jìn)階之路
有個(gè)朋友跟我描述了一個(gè)bug,要我?guī)涂纯词鞘裁辞闆r。原本他有一個(gè)表單,如下。
但是有一個(gè)前端插件會(huì)動(dòng)態(tài)插入兩個(gè)input,最后ajax提交的時(shí)候是
后端當(dāng)我們用 php 來(lái)接收的時(shí)候
echo file_get_contents("php://input"); echo " "; var_export($_POST); echo " "; echo PHP_VERSION;
結(jié)果是
id%5B%5D=1&id%5B%5D_text=a&id%5B%5D=2&id%5B%5D_text=b array ( "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);
控制臺(tái)輸出的是
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" ] }小結(jié)
在接收外部變量時(shí),多個(gè)相同的外部變量,在nodejs中會(huì)被放在一個(gè)數(shù)組里面,而php中則是后者覆蓋前者,如果需要傳遞數(shù)組變量,則在變量名后面添加上[]。這個(gè)不兼容,ok,是語(yǔ)言的特性能接受。
但是在php中在解析id[]_text的數(shù)據(jù)的時(shí)候都轉(zhuǎn)換成id[]了,這點(diǎn)就有點(diǎn)坑了。rfc 在這方面也沒(méi)看到有規(guī)定否則不會(huì)出現(xiàn)兩種語(yǔ)言解析不一致的情況了。
源碼分析也就是說(shuō) php 后端在解析的時(shí)候的問(wèn)題。那只能從源碼里一探究竟看php是如何解析post數(shù)據(jù)的了。
我把子進(jìn)程數(shù)修改為1,然后根據(jù)pid來(lái)調(diào)試
gdb -p 22892 ... (gdb) b /data/soft/php-7.1.10/main/php_variables.c:php_register_variable_ex Breakpoint 1 at 0x812877: file /data/soft/php-7.1.10/main/php_variables.c, line 70. (gdb) i b Num Type Disp Enb Address What 1 breakpoint keep y 0x0000000000812877 in php_register_variable_ex at /data/soft/php-7.1.10/main/php_variables.c:70 (gdb) (gdb) c Continuing. 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:70 70 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怎么寫(xiě)的,源碼精簡(jiǎn)了下,如下
#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 %s ",var); printf("var_len %zu ",var_len); }
根據(jù)php_register_variable_ex里面的規(guī)則:
name里面的 和.都被替換成_
name里遇到[則認(rèn)為是數(shù)組,數(shù)組的key為[前面的字符串,后面的都被舍去。
上面我模擬了表單提交一個(gè)name為id 1.2[]_3時(shí),輸出結(jié)果就是
var id_1_2 var_len 6思考為什么
上面的替換規(guī)則在官方手冊(cè)中有說(shuō)明
http://php.net/manual/zh/lang...
Dots and spaces in variable names are converted to underscores.
但是沒(méi)有看到命名中關(guān)于不使用[]后連接字符串的說(shuō)明。
extract難道是因?yàn)?b>extract原因,如果數(shù)組key里面有[],則沒(méi)辦法正常執(zhí)行了。
$foo["id"] = 1; $foo["id[]_text"] = 2; var_export($foo); extract($foo); var_export(get_defined_vars());
試了以上代碼,也印證了我的想法id[]_text的值直接丟失了。
所以php在接受這樣命名的(foo[]boo)外部變量名是不符合規(guī)范的,手冊(cè)文檔需要補(bǔ)全;
php在接受這樣不符合命名規(guī)范的(foo[]boo)外部變量的時(shí)候是強(qiáng)制轉(zhuǎn)換成數(shù)組,還是直接丟棄呢?
后續(xù)處理方案我提交了 bug https://bugs.php.net/bug.php?...
官方修復(fù):在文檔上補(bǔ)全說(shuō)明 http://php.net/manual/zh/lang...
php 8 里面可能設(shè)置開(kāi)關(guān)來(lái)控制是否對(duì)外部變量進(jìn)行轉(zhuǎn)換 https://bugs.php.net/bug.php?...
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/29652.html
摘要:初次認(rèn)識(shí)是在剛學(xué)的時(shí)候最近終于有機(jī)會(huì)用上了故此說(shuō)說(shuō)使用上的一些感受個(gè)人是很喜歡這套框架的方式使用依賴(lài)注入讓代碼組織很靈活耦合也很低但是也許是框架東西太多了遇到幾個(gè)坑上一年以上的也還沒(méi)解決不過(guò)有一定開(kāi)發(fā)經(jīng)驗(yàn)的話(huà)還是可以自己修復(fù)的被這幾個(gè)坑浪費(fèi) 初次認(rèn)識(shí)phalcon是在剛學(xué)php的時(shí)候,最近終于有機(jī)會(huì)用上了.故此說(shuō)說(shuō)使用上的一些感受 個(gè)人是很喜歡phalcon這套框架的方式,使用...
摘要:遇到個(gè)和在獲取客戶(hù)端方式不同導(dǎo)致跨系統(tǒng)的問(wèn)題。編碼擴(kuò)展討論與的區(qū)別是什么手冊(cè)上的解釋是返回字符串,此字符串中除了之外的所有非字母數(shù)字字符都將被替換成百分號(hào)后跟兩位十六進(jìn)制數(shù),空格則編碼為加號(hào)。 遇到個(gè) Java 和 Php 在獲取客戶(hù)端 cookie 方式不同導(dǎo)致跨系統(tǒng)的問(wèn)題。所以寫(xiě)了這篇博客梳理下相關(guān)知識(shí)。 實(shí)驗(yàn) 下面通過(guò)兩個(gè)簡(jiǎn)單的實(shí)驗(yàn),來(lái)看Java和Php在獲取web請(qǐng)求中的coo...
摘要:語(yǔ)言中的宏,我認(rèn)為,可以理解為一種簡(jiǎn)單的封裝。通過(guò)宏定義,可以對(duì)開(kāi)發(fā)者隱去一些細(xì)節(jié),讓開(kāi)發(fā)者在使用簡(jiǎn)單的語(yǔ)法來(lái)完成重復(fù)的復(fù)雜的編碼。當(dāng)然,宏定義還有其它的用途,但是,我們?cè)谏婕暗降木褪沁@個(gè)作用。我們看到,在宏定義中,使用了另外的宏。 本人也只是個(gè)初入門(mén)的菜鳥(niǎo),因?qū)夹g(shù)有著向往,故在無(wú)趣的工作之余,盡自己所能提升自己。由于我的 C 語(yǔ)言功底也有限,故本文的深度也有限,如有幸得大牛閱讀,還...
摘要:我的博客運(yùn)行以下代碼互聯(lián)網(wǎng)產(chǎn)品我們可能以為會(huì)得到的結(jié)果是互聯(lián)網(wǎng)產(chǎn)品,實(shí)際結(jié)果是互聯(lián)網(wǎng)產(chǎn)。所以在執(zhí)行的時(shí)候,通過(guò)字節(jié)比對(duì),會(huì)將去掉,導(dǎo)致了最后出現(xiàn)了亂碼。 我的博客 https://mengkang.net/1039.html 運(yùn)行以下代碼: $tag = 互聯(lián)網(wǎng)產(chǎn)品、; $text = rtrim($tag, 、); print_r($text); 我們可能以為會(huì)得到的結(jié)果是互聯(lián)網(wǎng)產(chǎn)品...
閱讀 2871·2023-04-25 22:15
閱讀 1887·2021-11-19 09:40
閱讀 2233·2021-09-30 09:48
閱讀 3317·2021-09-03 10:36
閱讀 2120·2021-08-30 09:48
閱讀 1953·2021-08-24 10:00
閱讀 2793·2019-08-30 15:54
閱讀 770·2019-08-30 15:54