今天遇到一个关于PHP 嵌套使用条件运算符(ternary expressions)的问题

现象

先来看一段C语言代码(test.c):


#include<stdio.h>
int main() {
int x = 1;
int shit = x == 1 ? 100 :
x == 2 ? 200 : 300;
printf("shit的值:%d\n", shit);
return 0;
}

编译后运行一下


root$ gcc test.c -o test && ./test
shit的值:100

答案在意料之中,因为x==1,所以100被赋值给shit。

但是如果我们用PHP重写一下上文的代码(test.php):


<?php
$x = 1;
$shit = $x == 1 ? 100 :
$x == 2 ? 200 : 300;
echo "shit的值:$shit\n";

执行一下:


root$ php test.php
shit的值:200

我们发现返回的结果不一样了,这是为什么呢?

排查

首先怀疑可能是PHP中比较运算符(==)和条件运算符(?:)的优先级问题,我们查看一下PHP官方文档

==的优先级比?:更高(C语言也是这样),所以


$shit = $x == 1 ? 100 :
$x == 2 ? 200 : 300;

等效于


$shit = ($x == 1) ? 100 :
($x == 2) ? 200 : 300;

执行一遍也确实如此,可以排除掉是运算符优先级导致问题的可能性了。

但是官方文档里关于运算符结合方向的举例说明中出现了这么一句话:这跟上文描述的现象很相似,问题应该就在这了。一番查阅之后得到以下结论:

结论

  • C语言的条件运算符(?:)的结合方向是从右往左,每次求值都是从最右边的子表达式开始算起,所以

int x = 1; int shit = x == 1 ? 100 :
x == 2 ? 200 : 300;
//等效于
int shit = x == 1 ? 100 :
(x == 2 ? 200 : 300);
//等效于
int shit = x == 1 ? 100 :
(300);// 100
  • PHP的条件运算符(?:)的结合方向是从左往右,每次求值都是从最左边的子表达式开始算起,所以

$x = 1;
$shit = $x == 1 ? 100 :
$x == 2 ? 200 : 300;
//等效于
$shit = ($x == 1 ? 100 :
$x == 2) ? 200 : 300;
//等效于
$shit = (100) ? 200 : 300;// 200

介于PHP的条件运算符结合方向,我们无法像C/C++那样 通过嵌套条件运算符来达到if-elseif-elseif-else表达式的效果,除非我们在靠后的子表达式中加上括号,本例中就可以靠这种方式解决:


$shit = $x == 1 ? 100 :
($x == 2 ? 200 : 300);

但在条件分支较多的情况下,就会出现代码可读性问题(堆积括号):


$shit = $x == 1 ? 100 :
($x == 2 ? 200 :
($x== 3 ? 300 :
...
($x == 8 ? 800 : 900)))))));

由于PHP不堆积括号的写法与C/C++在执行结果上是不一致的,并且只能通过加括号改变默认的结合方向 以达到预期的结果,所以PHP文档里干脆不建议嵌套使用条件运算符:

Note:
It is recommended that you avoid "stacking" ternary expressions. PHP's
behaviour when using more than one ternary operator within a single statement is non-obvious

参考资料

PHP: Ternary Operator - Manual
PHP: Operator Precedence - Manual
php - Ternary operator left associativity - Stack Overflow
Understanding nested PHP ternary operator - Stack Overflow
C 运算符优先级- cppreference.com

原文地址:https://segmentfault.com/a/1190000015634596

PHP条件运算符的“坑”的更多相关文章

  1. 如何一步一步用DDD设计一个电商网站(九)—— 小心陷入值对象持久化的坑

    阅读目录 前言 场景1的思考 场景2的思考 避坑方式 实践 结语 一.前言 在上一篇中(如何一步一步用DDD设计一个电商网站(八)—— 会员价的集成),有一行注释的代码: public interfa ...

  2. 多线程爬坑之路-Thread和Runable源码解析之基本方法的运用实例

    前面的文章:多线程爬坑之路-学习多线程需要来了解哪些东西?(concurrent并发包的数据结构和线程池,Locks锁,Atomic原子类) 多线程爬坑之路-Thread和Runable源码解析 前面 ...

  3. Spark踩坑记——Spark Streaming+Kafka

    [TOC] 前言 在WeTest舆情项目中,需要对每天千万级的游戏评论信息进行词频统计,在生产者一端,我们将数据按照每天的拉取时间存入了Kafka当中,而在消费者一端,我们利用了spark strea ...

  4. 多线程爬坑之路-学习多线程需要来了解哪些东西?(concurrent并发包的数据结构和线程池,Locks锁,Atomic原子类)

    前言:刚学习了一段机器学习,最近需要重构一个java项目,又赶过来看java.大多是线程代码,没办法,那时候总觉得多线程是个很难的部分很少用到,所以一直没下决定去啃,那些年留下的坑,总是得自己跳进去填 ...

  5. 踩石行动:ViewPager无限轮播的坑

    2016-6-19 前言 View轮播效果在app中很常见,一想到左右滑动的效果就很容易想到使用ViewPager来实现.对于像我们常说的banner这样的效果,具备无限滑动的功能是可以用ViewPa ...

  6. 为C# as 类型转换及Assembly.LoadFrom埋坑!

    背景: 不久前,我发布了一个调试工具:发布:.NET开发人员必备的可视化调试工具(你值的拥有) 效果是这样的: 之后,有小部分用户反映,工具用不了(没反应或有异常)~~~ 然后,建议小部分用户换个电脑 ...

  7. 首个threejs项目-前端填坑指南

    第一次使用threejs到实际项目中,开始的时候心情有点小激动,毕竟是第一次嘛,然而做着做着就感受到这玩意水好深,满满的都是坑,填都填不过来.经过老板20天惨无人道的摧残,终于小有成就. 因为第一次搞 ...

  8. dll文件32位64位检测工具以及Windows文件夹SysWow64的坑

    自从操作系统升级到64位以后,就要不断的需要面对32位.64位的问题.相信有很多人并不是很清楚32位程序与64位程序的区别,以及Program Files (x86),Program Files的区别 ...

  9. 关于微软HttpClient使用,避免踩坑

    最近公司对于WebApi的场景使用也越来越加大了,随之而来就是Api的客户端工具我们使用哪个?我们最常用的估计就是HttpClient,在微软类库中命名空间地址:System.Net.Http,是一个 ...

随机推荐

  1. 【性能测试】服务器资源监测工具sar安装

    [root@yyy ~]# sar Cannot open /var/log/sa/sa19: No such file or directory 在Linux系统中,运行sar命令,发现无法执行: ...

  2. 洛谷 P4014 分配问题 【最小费用最大流+最大费用最大流】

    其实KM更快--但是这道题不卡,所以用了简单粗暴的费用流,建图非常简单,s向所有人连流量为1费用为0的边来限制流量,所有工作向t连流量为1费用为0的边,然后对应的人和工作连(i,j,1,cij),跑一 ...

  3. 洛谷P1552 [APIO2012]派遣(左偏树)

    传送门 做这题的时候现学了一波左偏树2333(好吧其实是当初打完板子就给忘了) 不难发现肯定是选子树里权值最小的点且选得越多越好 但如果在每一个点维护一个小根堆,我们得一直找知道权值大于m为止,时间会 ...

  4. 【插件开发】—— 12 GEF入门

    介绍 前面也简单的介绍了一下插件开发的相关知识,下面主要就介绍一下GEF的相关使用.由于最近使用到编辑器这部分的操作,因此就跳过其他的内容,先介绍下GEF的相关知识. 前文回顾 1 插件学习篇 2 简 ...

  5. Luogu P2327 [SCOI2005]扫雷【递推/数学】By cellur925

    题目传送门 推了好久啊.看来以后要多玩扫雷了qwq. 其实本题只有三种答案:0.1.2. 对于所有第一列,只要第一个数和第二个数确定后,其实整个数列就确定了,我们可以通过这个递推式得出 sec[i-] ...

  6. luogu P1309 瑞士轮【排序】

    题目背景 在双人对决的竞技性比赛,如乒乓球.羽毛球.国际象棋中,最常见的赛制是淘汰赛和循环赛.前者的特点是比赛场数少,每场都紧张刺激,但偶然性较高.后者的特点是较为公平,偶然性较低,但比赛过程往往十分 ...

  7. Stamps ans Envelope Sive UVA - 242

    ( ||{集合x}表示x中元素1||x中元素2||...||x的最后一个元素||(a,b)表示a||b) ans[i][j][k]表示考虑前i种邮票时取j个邮票能否得到面值kans[i][j][k]= ...

  8. Kay and Snowflake CodeForces - 686D

    Kay and Snowflake CodeForces - 686D 题意:给一棵有根树,有很多查询(100000级别的),查询是求以任意一点为根的子树的任意重心. 方法很多,但是我一个都不会 重心 ...

  9. mysql之流程控制

    目录 分支结构 循环结构 分支结构: 1.if condition then [statement] elseif condition then [statement] else [statement ...

  10. QT入门学习2

    QT获取窗口几何布局有2类函数: 1.包含框架:x().y().frameGemetry().pos().move()... 2.不包含框架:geometry().width().height().w ...