amway

After working on my personal blog mengkang.net/1301.html for two or three years, the technology stagnated and I was confused and directionless, amway started a wave of my live streams [PHP Advanced Path][1]

Bug emersion

A friend described a bug to me and asked me to check it out. Originally he had a form that looked like this.

<form method="post">
    <input type="text" name="id[]" value="1">
    <input type="text" name="id[]" value="2">
    <input type="submit">
</form>
Copy the code

However, there is a front-end plug-in that inserts two inputs dynamically, and the last time an Ajax submission is made

<form method="post">
    <input type="text" name="id[]" value="1">
    <input type="text" name="id[]_text" value="a">
    <input type="text" name="id[]" value="2">
    <input type="text" name="id[]_text" value="b">
    <input type="submit">
</form>
Copy the code

The back-end

When we receive it in PHP

echo file_get_contents('php://input');
echo "\n";
var_export($_POST);
echo "\n";
echo PHP_VERSION;
Copy the code

As a result,

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.010.
Copy the code

Try using NodeJS

var http = require('http');
var querystring = require('querystring');

var postHTML = '<form method="post">' +
    '<input type="text" name="id[]" value="1"><input type="text" name="id[]_text" value="a">' +
    '<input type="text" name="id[]" value="2"><input type="text" name="id[]_text" value="b">' +
    '<input type="submit"></form>';

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);
Copy the code

The console output is

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']}Copy the code

summary

When receiving external variables, multiple identical external variables are placed in an array in NodeJS, whereas in PHP the latter overwrites the former. If you need to pass array variables, add [] after the variable name. This incompatibility, ok, is a feature of the language acceptable.

Id []_text = id[]; The RFC doesn’t see any rules in this area either or there wouldn’t be parsing inconsistency between the two languages.

Source code analysis

In other words, the PHP back end has problems parsing. You can only look at the source code to see how PHP parses post data. I change the number of child processes to 1, and then debug according to the PID

gdb -p 22892 ... (GDB) b /data/soft/ phP-7.1.10 /main/php_variables. C :php_register_variable_ex Breakpoint 1 at 0x812877: The 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 0x0000000000812877inPhp_register_variable_ex at /data/soft/php-7.1.10/main/php_variables. C :70 (GDB) (GDB) c php_register_variable_ex (var_name=0x7fb5b9056218"id[]", val=0x7ffff23dacd0, track_VARS_array = 0xf114A0) at /data/soft/ phP-7.1.10 /main/ php_variable. c:70 70if (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 = 0 xf114a0) 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)
      The at - 7.1.10 / data/soft/PHP/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_variable.c :308
      
#3 0x0000000000813ce6 in add_post_vars (content_type_dup=
      
       , Arg = 0 x7ffff23dce50) at/data/soft/PHP - 7.1.10 / main/php_variables. C: 324
      
#4 php_std_post_handler (content_type_dup=
      
       , Arg = 0 x7ffff23dce50) 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_variable.c :423
      
#7 0x000000000066c581 in mbstr_treat_data (arg=0, str=0x0, DestArray = 0 x0) - 7.1.10 at/data/soft/PHP/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
#11 0x0000000000804c11 in php_request_startup () at /data/soft/php-7.1.10/main/main.c:1672 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)
Copy the code

Php_register_variable_ex php_register_variable_ex php_register_variable_ex

#include <stdio.h>
#include <assert.h>
#include <memory.h>
#include <stdlib.h>

void php_register_variable_ex(char *var_name);

typedef unsigned char zend_bool;

int main(a) {
    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);

}
Copy the code

Php_register_variable_ex:

  • nameThe inside of theand.I’m going to replace it with_
  • namemeet[Is considered an array whose key is[The preceding string and the following string are omitted.

Above I simulated submitting a form whose name is ID 1.2[]_3 and the output is

var	id_1_2
var_len	6
Copy the code

Think about why

The above substitution rules are explained in the official manual

Php.net/manual/zh/l… Dots and spaces in variable names are converted to underscores.

But I don’t see anything in the name about not using the concatenation string after [].

extract

[] : insert into array key [] : insert into array key []

$foo["id"] = 1;
$foo["id[]_text"] = 2;

var_export($foo);

extract($foo);

var_export(get_defined_vars());
Copy the code

Id []_text is lost.

so

  1. phpIn receiving so named (foo[]booExternal variable names are not conforming to the specification,Manual documents need to be completed;
  2. phpBefore accepting such nonconforming naming conventions (foo[]booIs an external variable cast to an array or thrown away?