PHP从字符串到数值的类型转换
文章目录
- 环境
- 背景
- 显式转换
- 隐式转换
- 总结
- 类型判断
- 显式类型转换
- 其它
环境
- Windows 11 专业版
- PHP 8.4.5
背景
前端(JavaScript)在向后端(PHP)发送请求时,包含了参数 teamid
:
that.utils.sendRequest('api/enter/support', 'POST', {eventid: that.matchId,......teamid:item.teamid
}
但实际上,在有些情况下, item
并没有 teamid
属性。此时, teamid
变量的值是 undefined
。当然,这算是代码的bug,实际上不应该出现这种情况。
后端(PHP)在处理该请求时,用 $teamid
变量接受 teamid
参数,并判断其值是否大于 0
:
$teamid = $param['teamid'];if ($teamid > 0) {......
} else {......
}
经实际测试,取到的参数值是字符串 "undefined"
。
问题来了:这里会走 if
流程,还是会走 else
流程?
换句话说,在PHP中,一个字符串(先考虑非空字符串,比如 "abc"
)和 0
作比较,其结果是什么?
显式转换
显然,要把字符串和数值作比较,就得先把字符串转换成数值,再做比较。
我们来看看,把字符串显式转换成数值,结果是怎么样的,下面是一些例子:
echo (int)"123abc"; // 123echo (float)"123abc"; // 123echo (int)"-4.5e2xyz"; // -450echo (float)"-4.5e2xyz"; // -450echo (int)" 007"; // 7echo (float)" 007"; // 7echo (int)"abc123"; // 0echo (float)"abc123"; // 0echo (int)""; // 0echo (float)""; // 0echo (int)"3.14.5"; // 3echo (float)"3.14.5"; // 3.14
总结:字符串转换成数值,规则如下:
从字符串开头提取连续的字符转换,直到遇到不可转换的字符,然后把可转换的部分做转换。如果从一开始就不可转换,则转换成0。
因此,把字符串 "undefined"
显式转换成数值,其结果为 0。
隐式转换
注:接下来我会用 echo
语句来查看表达式的值,如果表达式是布尔类型的,则:
- 如果其值是
true
,则会被隐式转换成字符串"1"
- 如果其值是
false
,则会被隐式转换成空字符串""
这有点难以理解,为什么不转换成 1
和 0
,或者 true
和 false
呢?
回到正题,在PHP中, "abc" > 0
的结果是什么?
关于这个问题,DeepSeek和豆包都说结果是 false
,原因是字符串会先转换成数值,然后再去做比较。而前面提到, "abc"
转换成数值是 0
。
然而,在我本机上,运行结果并不是这样的。
$x = "abc";echo $x > 0; // 1
可见,在我本机上, $x > 0
的值是 true
。
顺便提一下,在另外一台机器上(Linux,PHP 7.4.33),$x > 0
的值确实是 false
。这种不一致又是怎么回事?是因为PHP版本不同吗?难道不同版本之间不考虑兼容性吗?
回到我的本机,前面提到, "abc"
显式转换成数值是 0
:
echo (int)"abc"; // 0
这一点是没有疑问的。那这么看来,显式转换和隐式转换还是有区别的。
事实上,在我本机上, "abc"
大于任何数值:
echo "abc" >= PHP_INT_MAX; // 1echo "abc" >= PHP_FLOAT_MAX; // 1
然而, "321abc"
的情况又有所不同:
echo "321abc" >= PHP_INT_MAX; // ""echo "321abc" >= PHP_FLOAT_MAX; // 1
综上所述,隐式转换的结果实在是有点难以理解。在代码中如果使用了隐式转换(像前面代码中,取到teamid后,直接和0作比较),是要出大乱子的。
总结
如果需要拿一个变量和数值作比较,不要直接比较,会出问题。
更好的方法是先做类型判断或者类型转换。
类型判断
判断一个变量是否是数值类型:
is_numeric()
:是否是数值is_int()
:是否是整数is_float()
/is_double()
:是否是浮点数
注:在PHP里, float
和 double
是相等的,一般用 float
。
不过这里需要注意的是,对于 is_numeric()
,如果参数确实是数值,结果固然是true,但如果参数是数值字符串(比如 "321"
),结果也是true。等等,这又是为啥?
下面是一些例子:
echo is_numeric(321); // 1echo is_numeric("321"); // 1echo is_numeric("321xyz"); // ""echo is_int(321); // 1echo is_int("321"); // ""echo is_int(321.0); // ""echo is_float(321.0 ); // 1echo is_float("321.0"); // ""echo is_float(321); // ""
显式类型转换
如果不想判断类型,那也最好先显式转换类型,再做比较,因为显式转换的规则好歹是确定的。比如:
if ((int)$x > 0) {......
}
不要直接比较(隐式转换类型):
if ($x > 0) {......
}
其它
这是我不喜欢弱类型语言的一个原因,限制太少,太灵活,怎么写代码都不报错,写的时候很爽,等到出问题时(也不报错,就是预期结果不一致),想要调试就费劲了。最讨厌的是,对于绝大部分情况是OK的,只对于一些特殊情况不正确,这种潜在的问题最可怕。
相比而言,强类型语言,比如Java,就不会出现本文中的问题。
在Java里,如果拿字符串去和数值比较,直接就编译报错了。
String str = "abc";if (str > 0) { // 编译报错......}
当然,我们也不会故意拿字符串去和数值比较,而一定会拿数值类型的变量去做比较。比如,在本例中,接收到参数(假设都是字符串)后,需要先转换为整数:
int teamid = Integer.parseInt(str);
如果参数值是字符串,比如 "undefined"
,则在这一步就会抛出异常,一下就发现问题了。
总之,Java会帮助(强制)你做类型转换,确保代码意图,不会出现副作用。而像PHP、JavaScript那样,类型转换是可选的,不转换也能运行,也不报错,但结果却是不可预知的。