PHP 对输入变量名的自动转换的问题与源码分析

栏目: PHP · 发布时间: 4年前

内容简介:表单提交到PHP脚本时,底层的PHP会做一层转换。将一些符号转成下划线实际上这层转换中会发生很多意想不到的情况。

起步

表单提交到 PHP 脚本时,底层的PHP会做一层转换。将一些符号转成下划线 _

PHP 对输入变量名的自动转换的问题与源码分析

实际上这层转换中会发生很多意想不到的情况。

列举这些情况

PHP 对输入变量名的自动转换的问题与源码分析

一个简单的测试就出现了意外,一个是单个 [ 也会被替换,对于 array 的输入, key 不会做转换。于是我多多测了一下,得出如下列表:

<input name="a.b" />        转为: $_REQUEST["a_b"]
<input name="a b" />        转为: $_REQUEST["a_b"]
<input name="a[b" />        转为: $_REQUEST["a_b"]
<input name="a]b" />        转为: $_REQUEST["a]b"]
<input name="a-b" />        转为: $_REQUEST["a-b"]
<input name=" ab" />        转为: $_REQUEST["ab"]
<input name="ab " />        转为: $_REQUEST["ab "]
<input name="arr[a.b]" />   转为: $_REQUEST["arr"]["a.b"]
<input name="ar.r[a.b]" />  转为: $_REQUEST["ar_r"]["a.b"]
<input name="arr[a[b]]" />  转为: $_REQUEST["arr"]["a[b"]
<input name="arr[a[]x]" />  转为: $_REQUEST["arr"]["a["]
<input name="arr[]ab" />    转为: $_REQUEST["arr"][0]
<input name="arr[a]b" />    转为: $_REQUEST["arr"]["a"]
<input name="arr[a.b" />    转为: $_REQUEST["arr_a.b"]
<input name="arr[[a.b" />   转为: $_REQUEST["arr_[a.b"]

这个转换机制十分诡异是吧。查了一下,在 Bug#77172 convert error on receiving variables from external sources 中提出了 id[]_text 转换成 id[] 的问题,采取的结果是补全文档上的说明。

另外也有几个讨论是否关闭这层转换:

这三个 Request 都还是 open 状态,还没有结果,其中关于关闭转换的讨论早在06年就提出来了。我不清楚 PHP 为什么会做这个转换,目的是什么。据我所知的 java,Django 都不会做转换的。

PHP对于外部输入的变量都会转换的,这就涉及到了 $_POST, $_GET, $_FILES, $_COOKIE, $_REQUEST 这些变量了。

源码分析

虽然我没有阅读过php源码,在朋友的帮助下,关于这部分的转换代码在 main/php_variables.cphp_register_variable_ex 函数中 php_variables.c#L68 ,源码精简了下流程:

PHPAPI void php_register_variable_ex(char *var_name, zval *val, zval *track_vars_array)
{
    char *p = NULL;
    char *ip = NULL;        /* index pointer */
    char *index;
    char *var, *var_orig;

    /* ignore leading spaces in the variable name */
    while (*var_name==' ') { // 忽略前置空格
        var_name++;
    }

    for (p = var; *p; p++) {
        if (*p == ' ' || *p == '.') {   // 空格和点替换成下划线
            *p='_';
        } else if (*p == '[') {
            is_array = 1;     // 如果遇到 [ 则视为数组,is_array 设为1
            ip = p;
            *p = 0;
            break;
        }
    }
    ...
}

这里可以看出,忽略前置空格是最先做的动作;当遇到第一个 [ 时,php则认为数数组,不再进行转换,设置了 is_array = 1 就 break 了。

这个 is_array 有什么用呢,往下看:

if (is_array) {
    int nest_level = 0;
    while (1) {
        char *index_s;
        size_t new_idx_len = 0;
        ip++;    // [ 的下一个字符
        index_s = ip;
        if (*ip==']') { // 如果下一个字符就已经是],表示没有设置key
            index_s = NULL;
        } else {
            ip = strchr(ip, ']');  // 查找剩余字符串中的 ]
            if (!ip) {
                /* PHP variables cannot contain '[' in their names, so we replace the character with a '_' */
                *(index_s - 1) = '_'; // 如果没找到,则将 [ 替换成下划线

                index_len = 0;
                if (index) {
                    index_len = strlen(index);
                }
                goto plain_var;
                return;
            }
            *ip = 0;
            new_idx_len = strlen(index_s);  // key 的长度到第一个出现 ] 为止
        }
    }
    ...
}

到此,转化处理的过程就很清晰了,对于数组情况的变量名,分为两种:

  1. 没找到 ] 与其匹配,该变量名不是数组,将 [ 替换成下划线,后续字符串不做处理;
  2. ] 与其匹配,取到第一个出现 ] 的位置作为 key ,舍弃后面的字符。

对于情况1 就很奇怪了,如果输入是 arr[[a.b 那么就会转成成 arr_[a.b 了。

总结

鉴于当前的转换规则总结的规律如下:

  1. 在第一个 [ 之前的字符中,忽略前置的空格,将 .空格 替换成下划线 _
  2. 在第一个 [ 之后的字符,不再进行替换处理:
    • 若后续字符中 没有 ] 时,第一个 [ 替换成 _ ,后续字符串不做转换;
    • 若后续字符中 ] 时,取到第一次出现 ] 的位置作为 key,舍弃后续字符。

另外,谁能告诉我PHP的这层转换的设计初衷是什么啊。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Android编程权威指南(第3版)

Android编程权威指南(第3版)

比尔·菲利普斯 (Bill Phillips)、克里斯·斯图尔特 (Chris Stewart)、克莉丝汀·马西卡诺 (Kristin Marsicano) / 王明发 / 人民邮电出版社 / 2017-6 / 129.00元

Big Nerd Ranch 是美国一家专业的移动开发技术培训机构。本书主要以其Android 训练营教学课程为基础,融合了几位作者多年的心得体会,是一本完全面向实战的Android 编程权威指南。全书共36 章,详细介绍了8 个Android 应用的开发过程。通过这些精心设计的应用,读者可掌握很多重要的理论知识和开发技巧,获得宝贵的开发经验。 第3 版较之前版本增加了对数据绑定等新工具的介......一起来看看 《Android编程权威指南(第3版)》 这本书的介绍吧!

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具