PHP三元运算符vs null合并运算符

bal*_*ing 285 php ternary-operator null-coalescing-operator php-7

有人可以解释PHP中三元运算符速记(?:)和空合并运算符(??)之间的差异吗?

他们什么时候表现不同,以同样的方式(如果这种情况发生)?

$a ?: $b
Run Code Online (Sandbox Code Playgroud)

VS.

$a ?? $b
Run Code Online (Sandbox Code Playgroud)

Mas*_*din 287

当你的第一个参数为null时,它们基本相同,只是E_NOTICE当你有一个未定义的变量时,null合并将不会输出.在PHP 7.0迁移的文档有这样一段话:

对于需要将三元组与isset()结合使用的常见情况,已添加空合并运算符(??)作为语法糖.它返回第一个操作数(如果存在且不为NULL); 否则返回第二个操作数.

这是一些示例代码来演示:

<?php

$a = null;

print $a ?? 'b'; // b
print "\n";

print $a ?: 'b'; // b
print "\n";

print $c ?? 'a'; // a
print "\n";

print $c ?: 'a'; // Notice: Undefined variable: c in /in/apAIb on line 14
print "\n";

$b = array('a' => null);

print $b['a'] ?? 'd'; // d
print "\n";

print $b['a'] ?: 'd'; // d
print "\n";

print $b['c'] ?? 'e'; // e
print "\n";

print $b['c'] ?: 'e'; // Notice: Undefined index: c in /in/apAIb on line 33
print "\n";
Run Code Online (Sandbox Code Playgroud)

它的输出:

$a = false ?? 'f'; // false
$b = false ?: 'g'; // 'g'
Run Code Online (Sandbox Code Playgroud)

有通知的行是我使用简写三元运算符而不是空合并运算符的行.但是,即使有通知,PHP也会给出相同的响应.

执行代码:https://3v4l.org/McavC

当然,这总是假设第一个参数是null.一旦它不再为null,那么你最终会产生差异,因为??操作符总是返回第一个参数,而?:速记只有在第一个参数是真的时才会发生,并且这依赖于PHP如何将事物类型转换为布尔值.

所以:

<?php

$a = null;

print $a ?? 'b'; // b
print "\n";

print $a ?: 'b'; // b
print "\n";

print $c ?? 'a'; // a
print "\n";

print $c ?: 'a'; // Notice: Undefined variable: c in /in/apAIb on line 14
print "\n";

$b = array('a' => null);

print $b['a'] ?? 'd'; // d
print "\n";

print $b['a'] ?: 'd'; // d
print "\n";

print $b['c'] ?? 'e'; // e
print "\n";

print $b['c'] ?: 'e'; // Notice: Undefined index: c in /in/apAIb on line 33
print "\n";
Run Code Online (Sandbox Code Playgroud)

然后$a将等于false$b等于'g'.

  • 这是正确的答案,但真实性检查是主要的差异,应该更加强调. (13认同)
  • 提示:如果你一直在使用?而不是?:但后来发现自己需要让你的代码与早于7的PHP版本兼容(对于ex的插件),那么你可能想换掉?? 与isset($ something)?$ something:$ something_else代码中的任何地方.使用find/replace工具,选择正则表达式选项并插入查找字段,可以使用Notepad ++或nedit(以及其他编辑器)轻松完成此操作:"\ s*(\ S +)\ s*\?\?" 并在替换字段中:"isset \($ 1 \)?$ 1:"没有引号(nedit使用\ 1而不是$ 1).然后全部替换. (6认同)
  • @MasterOdin对您的回答不满意。两者都不一样。有不同的结果。 (2认同)
  • 值得注意的是,您可以使用 ?? 与链接。例如:`$b = []; var_dump($b['a']['b']['c'] ?? 'default');` 或对象 `$b = new Foo; var_dump($b-&gt;a()-&gt;b()-&gt;c() ?? '默认');` (2认同)

And*_*rew 67

如果您使用这样的快捷方式三元运算符,如果$_GET['username']未设置则会引发通知:

$val = $_GET['username'] ?: 'default';
Run Code Online (Sandbox Code Playgroud)

所以你必须做这样的事情:

$val = isset($_GET['username']) ? $_GET['username'] : 'default';
Run Code Online (Sandbox Code Playgroud)

空合并运算符是等同于上面的语句,将返回"默认"如果$_GET['username']没有设置或者是null:

$val = $_GET['username'] ?? 'default';
Run Code Online (Sandbox Code Playgroud)

请注意,它不会检查真实性.它仅检查是否已设置且不为空.

您也可以这样做,并返回第一个定义的(set和not null)值:

$val = $input1 ?? $input2 ?? $input3 ?? 'default';
Run Code Online (Sandbox Code Playgroud)

现在这是一个合适的合并运算符.


a20*_*a20 43

在php交互模式下(php -a在终端上)进行下面的操作.每行的注释显示结果.

var_dump (false ?? 'value2');   # bool(false)
var_dump (true  ?? 'value2');   # bool(true)
var_dump (null  ?? 'value2');   # string(6) "value2"
var_dump (''    ?? 'value2');   # string(0) ""
var_dump (0     ?? 'value2');   # int(0)

var_dump (false ?: 'value2');   # string(6) "value2"
var_dump (true  ?: 'value2');   # bool(true)
var_dump (null  ?: 'value2');   # string(6) "value2"
var_dump (''    ?: 'value2');   # string(6) "value2"
var_dump (0     ?: 'value2');   # string(6) "value2"
Run Code Online (Sandbox Code Playgroud)

所以这是我的解释:

1. Null Coalescing运算符 - ??:

  • ??就像一个只允许NULL通过的"门".
  • 因此,它始终返回第一个参数,除非第一个参数恰好是NULL.
  • 这意味着??( !isset() || is_null() )

2.三元运算符 - ?:

  • ?:就像一扇anything falsy通过的大门- 包括NULL
  • 0,empty string,NULL,false,!isset(),empty()...任何气味falsy
  • 就像经典的三元运算符一样: echo ($x ? $x : false)
  • 注意:?:将抛出PHP NOTICE未定义(unset!isset())变量

3.所以,医生,做当我使用???:..

  • 我只是在开玩笑 - 我不是医生,这只是一种解释
  • 我会用?:
    • empty($x)检查
    • 经典的三元操作就像!empty($x) ? $x : $y可以缩短为$x ?: $y
    • if(!$x) { fn($x); } else { fn($y); } 可以缩短为 fn(($x ?: $y))
  • 我会用??
    • 我想做个!isset() || is_null()检查
    • 例如,检查对象是否存在 - $object = $object ?? new objClassName();

4.堆叠操作员......

  1. 三元运算符可堆叠 ...

    echo 0 ?: 1 ?: 2 ?: 3; //1
    echo 1 ?: 0 ?: 3 ?: 2; //1
    echo 2 ?: 1 ?: 0 ?: 3; //2
    echo 3 ?: 2 ?: 1 ?: 0; //3
    
    echo 0 ?: 1 ?: 2 ?: 3; //1
    echo 0 ?: 0 ?: 2 ?: 3; //2
    echo 0 ?: 0 ?: 0 ?: 3; //3
    
    Run Code Online (Sandbox Code Playgroud)

    此代码的来源和信誉

    这基本上是一系列:

    if( truthy ) {}
    else if(truthy ) {}
    else if(truthy ) {}
    ..
    else {}
    
    Run Code Online (Sandbox Code Playgroud)
  2. Null Coalese操作员可堆叠 ...

    $v = $x ?? $y ?? $z; 
    
    Run Code Online (Sandbox Code Playgroud)

    这是一系列:

    if(!isset($x) || is_null($x) ) {} 
    else if(!isset($y) || is_null($y) ) {}
    else {}
    
    Run Code Online (Sandbox Code Playgroud)
  3. 使用堆叠,我可以缩短这个:

    if(!isset($_GET['name'])){
       if(isset($user_name) && !empty($user_name)){
          $name = $user_name;
       }else {
          $name = 'anonymous';
       }
    } else { 
       $name = $_GET['name'];
    }
    
    Run Code Online (Sandbox Code Playgroud)

    对此:

    $name = $_GET['name'] ?? $user_name ?: 'anonymous';
    
    Run Code Online (Sandbox Code Playgroud)

    很酷,对吗?:-)

  • 这是了解这些运算符如何工作的一个非常好的答案,但我希望我永远不必对在一个语句中使用这两个运算符的生产代码进行故障排除! (8认同)
  • 高级,除了一个错误:缩短 `if(!$x) { echo $x; } else { 回显 $y; }` 到 `echo $x ?: $y`。一个不等于另一个。条件必须是“if($x)”,而不需要否定。它仍然让我了解了一些关于这个操作符的知识,它在它的简短版本中对我来说是新的,所以帖子收到了赞成票。 (2认同)

Dha*_*era 42

主要区别在于

  1. 三元运算表达式 expr1 ?: expr3返回expr1如果expr1计算结果为 TRUE,但是,从另一方面空合并运算符表达式 (expr1) ?? (expr2) 的计算结果为expr1,如果expr1 NULL

  2. expr1 ?: expr3如果左侧值(expr1) 不存在但是另一方面Null Coalescing Operator Null Coalescing Operator (expr1) ?? (expr2),则三元运算符会发出通知.如果左侧值不存在,则不发出通知(expr1) ,就像isset().

  3. TernaryOperator是左关联的

    ((true ? 'true' : false) ? 't' : 'f');
    
    Run Code Online (Sandbox Code Playgroud)

    Null Coalescing Operator是右关联的

    ($a ?? ($b ?? $c));
    
    Run Code Online (Sandbox Code Playgroud)

现在让我们解释一下示例之间的区别:

三元运算符 (?:)

$x='';
$value=($x)?:'default';
var_dump($value);

// The above is identical to this if/else statement
if($x){
  $value=$x;
}
else{
  $value='default';
}
var_dump($value);
Run Code Online (Sandbox Code Playgroud)

空融合运算符 (??)

$value=($x)??'default';
var_dump($value);

// The above is identical to this if/else statement
if(isset($x)){
  $value=$x;
}
else{
  $value='default';
}
var_dump($value);
Run Code Online (Sandbox Code Playgroud)

下面是解释之间的差异性和相似性表'??'?:

在此输入图像描述

特别注意:null合并运算符和三元运算符是一个表达式,它不会计算变量,而是表达式的结果.知道是否要通过引用返回变量很重要.声明返回$ foo ?? $酒吧; 并返回$ var == 42?$ a:$ b; 因此,在按引用返回功能中将不起作用并发出警告.


Cha*_*haz 14

在动态数据处理方面,它们的行为都不同.

如果变量为空(''),则空合并将把变量视为true,但速记三元运算符则不会.这是需要考虑的事情.

$a = NULL;
$c = '';

print $a ?? '1b';
print "\n";

print $a ?: '2b';
print "\n";

print $c ?? '1d';
print "\n";

print $c ?: '2d';
print "\n";

print $e ?? '1f';
print "\n";

print $e ?: '2f';
Run Code Online (Sandbox Code Playgroud)

并输出:

1b
2b

2d
1f

Notice: Undefined variable: e in /in/ZBAa1 on line 21
2f
Run Code Online (Sandbox Code Playgroud)

链接:https://3v4l.org/ZBAa1


St3*_*3an 10

实用简答:

尝试:

var_dump('' ?: 'ok');  // prints: ok
Run Code Online (Sandbox Code Playgroud)

var_dump('' ?? 'ok');  // prints empty string
Run Code Online (Sandbox Code Playgroud)

?:如果左侧值的计算结果为“真实”,则速记三元运算符 ( ) 将返回左侧的值;否则将返回右侧值。

然而

??如果左侧值已声明且不为空,则空合并运算符 ( ) 将返回左侧的值;否则将返回右侧值。

换句话说,?:测试真实性,并??充当 的简写isset()


*警告:如果你想用 测试一个变量?:你必须首先确保它被初始化/设置,否则 PHP 将引发一个E_NOTICE(反之则??不会)。


Dea*_* Or 9

两者都是较长表达的缩写.

?:是的缩写$a ? $a : $b.如果$ a的计算结果为TRUE,则此表达式将计算为$ a .

??是的缩写isset($a) ? $a : $b.如果设置了$ a,则此表达式将计算为$ a,而不是null.

当$ a未定义或为null时,它们的用例重叠.当$ a未定义??时,不会产生E_NOTICE,但结果是相同的.当$ a为null时,结果是相同的.


Scr*_*t47 5

向下滚动链接并查看该部分,它为您提供了一个比较示例,如下所示:

<?php
/** Fetches the value of $_GET['user'] and returns 'nobody' if it does not exist. **/
$username = $_GET['user'] ?? 'nobody';
/** This is equivalent to: **/
$username = isset($_GET['user']) ? $_GET['user'] : 'nobody';

/** Coalescing can be chained: this will return the first defined value out of $_GET['user'], $_POST['user'], and 'nobody'. **/
$username = $_GET['user'] ?? $_POST['user'] ?? 'nobody';
?>
Run Code Online (Sandbox Code Playgroud)

但是,不建议将运算符链接起来,因为它会使以后阅读代码时更难理解。

空合并运算符 (??) 已被添加为语法糖,用于需要将三元与 isset() 结合使用的常见情况。如果存在且不为 NULL,则返回其第一个操作数;否则它返回它的第二个操作数。

本质上,与三元运算符不同,使用合并运算符将使其自动检查 null。

  • @MarkBaker 链式三元很难理解,因为 PHP 破坏了三元关联。这不适用于合并运算符,恕我直言,链接合并是完全可以理解的。 (7认同)
  • 我不同意。链接空合并是一个很棒的功能,如果您了解运算符,它不会使阅读变得困难。它通常用于 javascript,一旦人们在 PHP 中习惯了它,就应该停止不使用链接的调用。链接三元组很难阅读,但空合并很容易。当您从左到右阅读时,它只列出接下来应该使用的值。 (7认同)
  • 请不要考虑链接......它和链接的三元组一样难以阅读/理解 (2认同)
  • 这看起来很像常见的`a || 乙|| JS 中的 c` 模式,除了 PHP 可以用于布尔值(JS 中的 `false || 2` 为 2;PHP 中的 `false ?? 2` 为假) (2认同)

Sup*_*eth 5

对于初学者:

空合并运算符(??)

null值和未定义(变量/数组索引/对象属性)外,其他所有内容均为真

例如:

$array = [];
$object = new stdClass();

var_export (false ?? 'second');                           # false
var_export (true  ?? 'second');                           # true
var_export (null  ?? 'second');                           # 'second'
var_export (''    ?? 'second');                           # ""
var_export ('some text'    ?? 'second');                  # "some text"
var_export (0     ?? 'second');                           # 0
var_export ($undefinedVarible ?? 'second');               # "second"
var_export ($array['undefined_index'] ?? 'second');       # "second"
var_export ($object->undefinedAttribute ?? 'second');     # "second"
Run Code Online (Sandbox Code Playgroud)

这基本上是检查变量(数组索引,对象属性等)是否存在null。类似于isset功能

三元运算符速记(?:)

每一个虚假的东西(falsenull0,空字符串)都是来作为假的,但如果它是一个不确定的,也都为假,但Notice会抛出

$array = [];
$object = new stdClass();

var_export (false ?: 'second');                           # "second"
var_export (true  ?: 'second');                           # true
var_export (null  ?: 'second');                           # "second"
var_export (''    ?: 'second');                           # "second"
var_export ('some text'    ?? 'second');                  # "some text"
var_export (0     ?: 'second');                           # "second"
var_export ($undefinedVarible ?: 'second');               # "second" Notice: Undefined variable: ..
var_export ($array['undefined_index'] ?: 'second');       # "second" Notice: Undefined index: ..
var_export ($object->undefinedAttribute ?: 'second');     # "Notice: Undefined index: ..
Run Code Online (Sandbox Code Playgroud)

希望这可以帮助