PHP从字符串到数值的类型转换

文章目录

环境

  • Windows 11 专业版
  • PHP 8.4.5

背景

前端(JavaScript)在向后端(PHP)发送请求时,包含了参数 teamid

javascript 复制代码
that.utils.sendRequest('api/enter/support', 'POST', {
    eventid: that.matchId,
    ......
    teamid:item.teamid
}

但实际上,在有些情况下, item 并没有 teamid 属性。此时, teamid 变量的值是 undefined 。当然,这算是代码的bug,实际上不应该出现这种情况。

后端(PHP)在处理该请求时,用 $teamid 变量接受 teamid 参数,并判断其值是否大于 0

php 复制代码
$teamid = $param['teamid'];

if ($teamid > 0) {
    ......
} else {
    ......
}

经实际测试,取到的参数值是字符串 "undefined"

问题来了:这里会走 if 流程,还是会走 else 流程?

换句话说,在PHP中,一个字符串(先考虑非空字符串,比如 "abc" )和 0 作比较,其结果是什么?

显式转换

显然,要把字符串和数值作比较,就得先把字符串转换成数值,再做比较。

我们来看看,把字符串显式转换成数值,结果是怎么样的,下面是一些例子:

php 复制代码
echo (int)"123abc"; // 123

echo (float)"123abc"; // 123

echo (int)"-4.5e2xyz"; // -450

echo (float)"-4.5e2xyz"; // -450

echo (int)" 007"; // 7

echo (float)" 007"; // 7

echo (int)"abc123"; // 0

echo (float)"abc123"; // 0

echo (int)""; // 0

echo (float)""; // 0

echo (int)"3.14.5"; // 3

echo (float)"3.14.5"; // 3.14

总结:字符串转换成数值,规则如下:

从字符串开头提取连续的字符转换,直到遇到不可转换的字符,然后把可转换的部分做转换。如果从一开始就不可转换,则转换成0。

因此,把字符串 "undefined" 显式转换成数值,其结果为 0。

隐式转换

注:接下来我会用 echo 语句来查看表达式的值,如果表达式是布尔类型的,则:

  • 如果其值是 true ,则会被隐式转换成字符串 "1"
  • 如果其值是 false ,则会被隐式转换成空字符串 ""

这有点难以理解,为什么不转换成 10 ,或者 truefalse 呢?

回到正题,在PHP中, "abc" > 0 的结果是什么?

关于这个问题,DeepSeek和豆包都说结果是 false ,原因是字符串会先转换成数值,然后再去做比较。而前面提到, "abc" 转换成数值是 0

然而,在我本机上,运行结果并不是这样的。

php 复制代码
$x = "abc";

echo $x > 0; // 1

可见,在我本机上, $x > 0 的值是 true

顺便提一下,在另外一台机器上(Linux,PHP 7.4.33),$x > 0 的值确实是 false 。这种不一致又是怎么回事?是因为PHP版本不同吗?难道不同版本之间不考虑兼容性吗?

回到我的本机,前面提到, "abc" 显式转换成数值是 0

php 复制代码
echo (int)"abc"; // 0

这一点是没有疑问的。那这么看来,显式转换和隐式转换还是有区别的。

事实上,在我本机上, "abc" 大于任何数值:

php 复制代码
echo "abc" >= PHP_INT_MAX; // 1

echo "abc" >= PHP_FLOAT_MAX; // 1

然而, "321abc" 的情况又有所不同:

php 复制代码
echo "321abc" >= PHP_INT_MAX; // ""

echo "321abc" >= PHP_FLOAT_MAX; // 1

综上所述,隐式转换的结果实在是有点难以理解。在代码中如果使用了隐式转换(像前面代码中,取到teamid后,直接和0作比较),是要出大乱子的。

总结

如果需要拿一个变量和数值作比较,不要直接比较,会出问题。

更好的方法是先做类型判断或者类型转换。

类型判断

判断一个变量是否是数值类型:

  • is_numeric() :是否是数值
  • is_int() :是否是整数
  • is_float() / is_double() :是否是浮点数

注:在PHP里, floatdouble 是相等的,一般用 float

不过这里需要注意的是,对于 is_numeric() ,如果参数确实是数值,结果固然是true,但如果参数是数值字符串(比如 "321" ),结果也是true。等等,这又是为啥?

下面是一些例子:

php 复制代码
echo is_numeric(321); // 1

echo is_numeric("321"); // 1

echo is_numeric("321xyz"); // ""

echo is_int(321); // 1

echo is_int("321"); // ""

echo is_int(321.0); // ""

echo is_float(321.0 ); // 1

echo is_float("321.0"); // ""

echo is_float(321); // ""

显式类型转换

如果不想判断类型,那也最好先显式转换类型,再做比较,因为显式转换的规则好歹是确定的。比如:

php 复制代码
if ((int)$x > 0) {
    ......
}

不要直接比较(隐式转换类型):

php 复制代码
if ($x > 0) {
    ......
}

其它

这是我不喜欢弱类型语言的一个原因,限制太少,太灵活,怎么写代码都不报错,写的时候很爽,等到出问题时(也不报错,就是预期结果不一致),想要调试就费劲了。最讨厌的是,对于绝大部分情况是OK的,只对于一些特殊情况不正确,这种潜在的问题最可怕。

相比而言,强类型语言,比如Java,就不会出现本文中的问题。

在Java里,如果拿字符串去和数值比较,直接就编译报错了。

java 复制代码
        String str = "abc";
        
        if (str > 0) { // 编译报错
            ......
        }

当然,我们也不会故意拿字符串去和数值比较,而一定会拿数值类型的变量去做比较。比如,在本例中,接收到参数(假设都是字符串)后,需要先转换为整数:

java 复制代码
int teamid = Integer.parseInt(str);

如果参数值是字符串,比如 "undefined" ,则在这一步就会抛出异常,一下就发现问题了。

总之,Java会帮助(强制)你做类型转换,确保代码意图,不会出现副作用。而像PHP、JavaScript那样,类型转换是可选的,不转换也能运行,也不报错,但结果却是不可预知的。

相关推荐
BingoGo2 天前
当你的 PHP 应用的 API 没有限流时会发生什么?
后端·php
JaguarJack2 天前
当你的 PHP 应用的 API 没有限流时会发生什么?
后端·php·服务端
BingoGo3 天前
OpenSwoole 26.2.0 发布:支持 PHP 8.5、io_uring 后端及协程调试改进
后端·php
JaguarJack3 天前
OpenSwoole 26.2.0 发布:支持 PHP 8.5、io_uring 后端及协程调试改进
后端·php·服务端
JaguarJack4 天前
推荐 PHP 属性(Attributes) 简洁读取 API 扩展包
后端·php·服务端
BingoGo4 天前
推荐 PHP 属性(Attributes) 简洁读取 API 扩展包
php
JaguarJack5 天前
告别 Laravel 缓慢的 Blade!Livewire Blaze 来了,为你的 Laravel 性能提速
后端·php·laravel
郑州光合科技余经理5 天前
代码展示:PHP搭建海外版外卖系统源码解析
java·开发语言·前端·后端·系统架构·uni-app·php
QQ5110082855 天前
python+springboot+django/flask的校园资料分享系统
spring boot·python·django·flask·node.js·php
WeiXin_DZbishe5 天前
基于django在线音乐数据采集的设计与实现-计算机毕设 附源码 22647
javascript·spring boot·mysql·django·node.js·php·html5