今天遇到一个关于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. idea设置控制台不打印日志

    这样做的好处是当想打印数据到控制台查看就特别方便,这个在大数据spark sql使用的多.当然如果代码报错也会打印,这个不必担心. 方案Ⅰ 方法是将这个log日志文件放到idea的资源目录里即可 lo ...

  2. mybatis基础学习5-一对多和多对多(简写)

    1:建实体类 建mysql表

  3. Linux 常用命令八 find

    一.find命令 find命令用于查找文件. 按文件名字查找: wang@wang:~/workpalce/python$ sudo find / -name "create.txt&quo ...

  4. spring boot 项目发布运行

    1. maven install 发布jar包 2. java -jar webservice.jar 启动jar包

  5. 题解报告:hdu 1075 What Are You Talking About

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1075 Problem Description Ignatius is so lucky that he ...

  6. 利用autotools工具制作从源代码安装的软件 分类: linux 2014-06-02 23:27 340人阅读 评论(0) 收藏

    编写程序(helloworld.c)并将其放到一个单独目录. helloworld.c: #include<stdio.h> int main() { printf("hello ...

  7. 转-AFNetwork 作用和用法详解

    来自:http://www.maxiaoguo.com/clothes/269.html AFNetworking是一个轻量级的iOS网络通信类库.它建立在NSURLConnection和NSOper ...

  8. 434 Number of Segments in a String 字符串中的单词数

    统计字符串中的单词个数,这里的单词指的是连续的非空字符.请注意,你可以假定字符串里不包括任何不可打印的字符.示例:输入: "Hello, my name is John"输出: 5 ...

  9. Matrix Transformation codechef 数学题

    https://www.codechef.com/problems/MTRNSFRM 我只能说codechef的题好劲爆,这题居然是easy的题,太可怕了.而且还有一点就是codechef的题解很难看 ...

  10. Java_JDBC连接数据库_使用读取配置文件的方式

    package com.homewoek3_4.dao; import java.io.IOException; import java.io.InputStream; import java.sql ...