Press "Enter" to skip to content

PHP类型转换相关的一个Bug

PHP为了避免数字索引和数字字符串索引(注1)的混乱, 引入了zend_symtable_*系列函数, 并应用于数组中.
这样一来, 数字字符串索引也就会被当作数字索引, 然而总是有一些情况, 是PHP的维护者没有想到的...
比如, 类型转换时刻:

鉴于很多朋友好心的提示, 使用json_deocde的第二个参数就可以直接得到数组.
我说明下, 如下的代码是我有意而为之, 并不是为了json_decode, 而是为了构造一个"有问题"的数组.

在PHP5.2.*下(json version 1.2.1):

$data = array(
    123 => 'laruence',
);
$value = json_encode($data);
$obj   = json_decode($value);
$arr   = (array)$obj;
var_dump($arr);

神仙提供了如下的更简单的构造方法:

$obj=new stdClass;
$obj->{'123'} = "laruence";
$arr = (array)$obj;
var_dump($arr);

此时, 问题就出现了, 上面得到的输出是:

array(1) {
  ["123"]=>
  string(8) "laruence"
}

现在,你郁闷吧, 因为数组键是字符串, 而通过正常渠道访问的时候, PHP都会自动把数字字符串转换成数字, 所以:

print_r($arr[123]);
//PHP Notice:  Undefined offset:  123 in ***
print_r($arr["123"]);
//PHP Notice:  Undefined offset:  123 in ***
var_dump(array_key_exists("123", $arr));
//bool(false)

我已经报了Bug, 不过PHP本身也不保证类型转换的一致性, 所以PHP维护者最后认为是不是Bug都无所谓了, 大家平时注意即可:http://bugs.php.net/bug.php?id=51915
注1
本文中所说的字符串数字和之前的文章PHP字符串比较中所说的numeric string有一点不同, 在zend_symtable_*系列函数中, 只会吧/^-?[^0][0-9]*$/这样的字符串认为是数字字符串. 相关核心逻辑如下:

#define HANDLE_NUMERIC(key, length, func) {
    register char *tmp=key;
    if (*tmp=='-') {
        tmp++;
    }
    if ((*tmp>='0' && *tmp<='9')) do {
        char *end=key+length-1;
        long idx;
        if (*tmp++=='0' && length>2) {
            break;
        }
        while (tmp<end) {
            if (!(*tmp>='0' && *tmp<='9')) {
                break;
            }
            tmp++;
        }
        if (tmp==end && *tmp=='0') {
            if (*key=='-') {
                idx = strtol(key, NULL, 10);
                if (idx!=LONG_MIN) {
                    return func;
                }
            } else {
                idx = strtol(key, NULL, 10);
                if (idx!=LONG_MAX) {
                    return func;
                }
            }
        }
    } while (0);
}

PS:我这里只有5.2.8, 5.2.11俩个版本, 各位读者如果有其他版本的PHP, 帮忙测试下是否在你的版本下也存在这个问题. 谢谢
另: 谢谢远豪提供这个问题, 原问题是和Memcached相关的.

26 Comments

  1. Melly Gracia
    Melly Gracia July 17, 2019

    Very impressive. Thanks a lot for your efforts. Will read more of your articles in the while.

  2. CVV shop
    CVV shop March 17, 2018

    Hi all! It’s very useful info! Just want to asy thank you sir!

  3. 泡泡
    泡泡 September 9, 2015

    PHP本身就是弱类型,数字字符串会当成数字去使用,更何况数组的key肯定当成索引使用了。

  4. aquaponics
    aquaponics January 20, 2014

    An Aquaponics garden may be set up indoors or outdoors inside backyard.
    Nutrient wastage is eliminated because the river is cycled through different tanks continuously.
    The sky really is the limit with the type of fish you are able to grow
    (provided there are no bans on this).

  5. colin
    colin May 17, 2012

    $re = ‘/^-?[^0][0-9]*$/’;
    $str = ‘n0’;
    var_dump(preg_match($re, $str));// int 1

  6. 何林丹
    何林丹 November 2, 2011

    我的是5.2.10,也出现同样的bug.我又测试了下object转换成array,也是同样的问题。
    希望有时间能给大家讲些php强制转换的原理
    ‘laruence’,
    );
    $obj = (object)$data;
    var_dump($obj);
    var_dump($obj->{123});
    //NULL
    ?>

  7. k
    k March 11, 2011

    json_encode的实现里面,判断一个数组是索引数组还是关联数组,认为索引数组必须从0开始,所以非0开始的数组都当做是关联数组,关联数组的键就当做字符串处理了。

  8. BILLY
    BILLY June 26, 2010

    PillSpot.org. Canadian Health&Care.Special Internet Prices.No prescription online pharmacy.Pillspot.org. .
    Categories: Stop SmokingMental HealthVitamins/Herbal Supplements.Anxiety/Sleep Aid.Antidiabetic.Eye Care.Antiviral.Antibiotics.Stomach.Blood Pressure/Heart.Antidepressants.Pain Relief.Womens Health.Anti-allergic/Asthma.Skin Care.Mens Health.Weight…

  9. 雪候鸟
    雪候鸟 June 7, 2010

    @神仙 赞, 我还真没想到能这么做. 😉

  10. 神仙
    神仙 June 7, 2010

    有个更简单的构造办法
    $obj=new stdClass;
    $obj->{‘123’} = 1;
    $arr = (array)$obj;
    var_dump($arr);

  11. noknow
    noknow May 26, 2010

    多看手册没坏处的!

  12. ....
    .... May 26, 2010

    $data = array(
    123 => ‘laruence’,
    );
    var_dump($data);
    这时候就打印不出来了,还需要转换么?

  13. 雪候鸟
    雪候鸟 May 26, 2010

    @jessica 我是处心积虑的特意这么做的,为了构造这么个数组, 呵呵

  14. Jessica
    Jessica May 26, 2010

    鸟哥 decode的时候多价格参数 不要返回object 直接返回成数组..
    $arr = json_decode($value, true)

Leave a Reply

Your email address will not be published. Required fields are marked *