'0xff'==255 对还是错? 再谈所谓的“2019阿里面试题”

背景

近日,php吧有吧友发布了一个所谓的2019阿里面试题。且不论真假,
里面有一道题很有意思,而且目前还没有看见一个人出来纠正这个问题。

题目如下:

$x = null;
if ('0xFF' == 255) {
    $x = (int)'0xFF'; 
}
//var_dump($x); 

问题:$x的值是多少?

他们给了这样一个“公认”的答案: $x=0而不是255。 然后后面一字一句的说得头头是道,差点让人信以为真。可静下心来,再读这段代码,仔细推敲后,发现了“完全不一样”的答案。

令人深思的十大问题之一

令人觉得可笑的是,这道号称令人值得深思的十大问题之一的问题似乎并没有多少人拿到这道题真正的去深思,甚至哪怕是动手运行一下,显然,“拿来主义”并不是一个良好的学习方式。

那么答案到底是什么呢?

答案是,此题条件不足,无解或存在特解

说人话就是,在不同的php版本下,这段代码有着不同答案

答案

在php7+环境下,$x 的值为null. 对,这是一个你最不可能相信的答案。

在php5.6-环境下,$x的值为0.

寻找根源

这是一个非常奇怪的答案,不应该会出现这种情况,除非是刻意而为之。
一个很容易想到的关键,应该是'0xFF' == 255这里出现问题。

字符串与数字进行比较,根据C语言的类型转换运算,低类型应该向高类型进行隐式转换。所以这里字符串要转为int类型。明显根据经验,php会使用is_numeric进行转换,看来,问题的根源就在于此了。

证明猜测

为了证明猜测的正确性,我们可以用以下语句来检验。

$x = null;
if ('0xff' == 255) {
    $x = (int)'0xFF'; 
}
var_dump('0xff'+0); 
var_dump(is_numeric('0xff'));
var_dump((int)'0xff');
var_dump('0xff' == 255); 
语句php7.xphp5.x对比
'0xff'+0int(0)int(255)出现差异
is_numeric('0xff')bool(false)bool(true)出现差异
(int)'0xff'int(0)int(0)正常
'0xff' == 255bool(false)bool(true)出现差异

语句解释:

'0xff'+0 : 强制类型转换且不改变数值,非常常见的一种数值化字符串方法。

is_numeric('0xff'):判别字符串形式下的十六进制,php5.x与7.x相差较大,见后文说明。

(int)'0xff':使用int强制转换,从左到右读取数字,遇到非数字结束。

'0xff' == 255):对比结果。

很明显,结果出来了。确实是is_numeric出现了问题。
事实上,官网手册上也验证了我的猜测。

PHP 7.0.0+ Strings in hexadecimal (e.g. 0xf4c3b00c) notation are no longer regarded as numeric strings, i.e. is_numeric() returns FALSE now.

如此,为何

为何要更改对字符串形式的16进制的解析呢?这个问题说来也简单。因为is_numeric并不是一个安全的函数,解析字符串形式的16进制很可能引起sql注入的安全问题,这里不过多讨论,想了解更多的请百度相关文章。所以在php7+,is_numeric不再解析字符串形式的16进制,是为了更加的安全。不论这道题是否是所谓的“2019阿里面试题”,希望所有还在这条路上摸索的人,都要去质疑这种网上流传已久的“神”题。我更加希望这不是阿里的面试题,因为如果HR不加思索的从网上“拿”这个题目出来让这些phper去做,甚至连技术负责人把关都没把到,那可能又是一番笑话了。所以,环境才是最重要的,代码离开了环境,谁能保证永远坚持不懈的动下去。共勉