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


341

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

它们什么时候表现不同,什么时候以相同的方式表现(如果甚至发生)?

$a ?: $b

VS。

$a ?? $b

Answers:


343

当您的第一个参数为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";

注意的行是我使用速记三元运算符而不是空合并运算符的行。但是,即使有此通知,PHP也会给出相同的响应。

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

当然,这总是假设第一个参数是null。一旦它不再为null,则最终会出现以下差异:??运算符将始终返回第一个参数,而?:简写方式只有在第一个参数为true时才返回,并且这取决于PHP如何将内容类型转换为布尔值

所以:

$a = false ?? 'f'; // false
$b = false ?: 'g'; // 'g'

则将$a等于false$b等于'g'


8
提示:如果您一直在使用?? 而不是?:但是您发现自己需要使您的代码与7之前的PHP版本兼容(例如ex的插件),那么您可能希望换出??。与isset($ something)?$ something:$ something_else在代码中的任何位置。您可以使用“查找/替换”工具,通过Notepad ++或nedit(以及其他编辑器)轻松地执行此操作,选择正则表达式选项并插入查找字段中:“ \ s *(\ S +)\ s * \?\?” 并在替换字段中:“ isset($ 1)?$ 1:”,不带引号(nedit使用\ 1而不是$ 1)。然后全部替换。
达米安·格林

14
这是正确的答案,但是真实性检查是主要区别,应予以更多强调。
mancze

2
@MasterOdin对您的回答不满意。两者都不一样。有不同的结果。
好奇

1
值得注意的是,您可以使用?? 与链接。例如:$b = []; var_dump($b['a']['b']['c'] ?? 'default');或带有物体$b = new Foo; var_dump($b->a()->b()->c() ?? 'default');
Jack B

请注意,的行为也与有所不同$a = [];。请参阅:3v4l.org/iCCa0
Soullivaneuh

75

在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"

所以这是我的解释:

1.空合并运算符- ??

  • ?? 就像一个“门”,只允许NULL通过
  • 因此,它始终返回first参数,除非第一个参数碰巧是NULL
  • 这表示 ??( !isset() || is_null() )

2.三元运算符- ?:

  • ?: 就像一扇门, anything falsy通行 -包括NULL
  • 0empty stringNULLfalse!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

    此代码的来源和信誉

    这基本上是一个序列:

    if( truthy ) {}
    else if(truthy ) {}
    else if(truthy ) {}
    ..
    else {}
  2. 空煤算子可以堆叠 ...

    $v = $x ?? $y ?? $z; 

    这是一个序列:

    if(!isset($x) || is_null($x) ) {} 
    else if(!isset($y) || is_null($y) ) {}
    else {}
  3. 使用堆栈,我可以缩短此时间:

    if(!isset($_GET['name'])){
       if($user_name){
          $name = $user_name;
       }else {
          $name = 'anonymous';
       }
    } else { 
       $name = $_GET['name'];
    }

    对此:

    $name = $_GET['name'] ?? $user_name ?: 'anonymous';

    酷吧?:-)


3
到目前为止最好的答案
Faizan Anwer Ali Rupani

69

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

$val = $_GET['username'] ?: 'default';

因此,您必须执行以下操作:

$val = isset($_GET['username']) ? $_GET['username'] : 'default';

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

$val = $_GET['username'] ?? 'default';

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

您也可以执行此操作,并且将返回第一个定义的(set和not null)值:

$val = $input1 ?? $input2 ?? $input3 ?? 'default';

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


42

主要区别在于

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

  2. 三元操作员 expr1 ?: expr3发出通知,如果左侧值(expr1) 不存在,但是,从另一方面空合并运算符 (expr1) ?? (expr2)特别是,如果左侧值不发出通知(expr1) 不存在,就像isset()

  3. 三元运算符保持关联

    ((true ? 'true' : false) ? 't' : 'f');

    空合并运算符是正确的关联

    ($a ?? ($b ?? $c));

现在让我们通过示例来说明两者之间的区别:

三元运算符 (?:)

$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);

空合并运算符 (??)

$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);

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

在此处输入图片说明

特别说明:null合并运算符和三进制运算符是一个表达式,它不计算变量,而是计算表达式的结果。了解是否要通过引用返回变量很重要。语句返回$ foo?$ bar; 并返回$ var == 42?$ a:$ b; 因此,按引用返回功能中的“无效”将不起作用,并发出警告。


15

在动态数据处理方面,两者的行为都不同。

如果变量为空(''),则空合并将变量视为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';

并输出:

1b
2b

2d
1f

Notice: Undefined variable: e in /in/ZBAa1 on line 21
2f

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


对于PHP来说,这显然是违反直觉的,PHP通常将空字符串视为false。但是在文档中明确指出了??:It returns its first operand if it exists and is not NULL; otherwise it returns its second operand
西蒙

12

两者都是较长表达式的简写。

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

??是的缩写isset($a) ? $a : $b。如果设置了$ a且不为null,则该表达式的计算结果为$ a。

$ a未定义或为空时,它们的用例重叠。$ a未定义时??将不会产生E_NOTICE,但结果相同。$ a为空时,结果相同。


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"

这基本上是检查变量(数组索引,对象属性等)是否存在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: ..

希望这可以帮助


4

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

<?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';
?>

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

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

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


1
请不要考虑链接...与链接的三元链接一样难以阅读/理解
Mark Ba​​ker

7
@MarkBaker链式三元数很难理解,因为PHP破坏了三元性。这不适用于合并运算符,并且imho链式合并是完全可以理解的。
NikiC '16

7
我不同意。链接空合并是一个很棒的功能,如果您理解运算符,也不会很难阅读。它通常在javascript中使用,一旦人们对PHP满意,就应该停止不使用链接的调用。链接三元非常难读,但空合并很容易。当您从左到右阅读时,它仅列出了下一步应使用的值。
Earl3s

2
这看起来非常类似于a || b || cJS中的通用模式,除了PHP可以用于布尔值(false || 2在JS中为2;false ?? 2在PHP中为false)
fregante

1
我不同意您和其他人对不使用链接的看法。这就像说永远不要使用循环,因为可能不了解它们。开发人员/编码人员可以完全自由地使用他们了解的编码标准和实践,即使其他人不了解。我个人认为链式合并与switch语句非常相似。它返回找到(设置)的第一个值,如果找不到任何内容,则返回最后一个值。
kurdtpage

3

其他答案更深入,并给出了很好的解释。对于那些寻求快速解答的人,

$a ?: 'fallback'$a ? $a : 'fallback'

$a ?? 'fallback'$a = isset($a) ? $a : 'fallback'


主要区别在于左操作符为:

  • 一个falsy值不为空(0''false[],...)
  • 未定义的变量

$a =在的上述扩展中应该没有??$a ?? 'fallback' 不会设置或更改$ a的值。(它仅返回一个值)。
Doin

2

似乎使用??或都有优点和缺点?:。使用的优点?:是它对false和null以及“”的求值相同。缺点是,如果前面的参数为null,它将报告E_NOTICE。随着??亲是没有E_NOTICE,但con是,它不评估虚假和无效一样。以我的经验,我看到人们开始交替使用null和false,但后来他们最终诉诸修改代码以与使用null或false一致,但不能同时使用两者。一种替代方法是创建一个更复杂的三元条件:(isset($something) or !$something) ? $something : $something_else

以下是??使用null和false 来使用运算符的区别的示例:

$false = null;
$var = $false ?? "true";
echo $var . "---<br>";//returns: true---

$false = false;
$var = $false ?? "true";
echo $var . "---<br>"; //returns: ---

但是,通过详细说明三元运算符,我们可以使错误或空字符串“”表现为好像是空值,而不会引发e_notice:

$false = null;
$var = (isset($false) or !$false) ? $false : "true";
echo $var . "---<br>";//returns: ---

$false = false;
$var = (isset($false) or !$false) ? $false : "true";
echo $var . "---<br>";//returns: ---

$false = "";
$var = (isset($false) or !$false) ? $false : "true";
echo $var . "---<br>";//returns: ---

$false = true;
$var = (isset($false) or !$false) ? $false : "true";
echo $var . "---<br>";//returns: 1---

我个人认为,如果将来的PHP版本包括另一个新的运算符:?,那将是一件非常好的事情:它取代了上面的语法。即: // $var = $false :? "true";该语法将相等地评估null,false和“”,而不抛出E_NOTICE ...


3
您可以使用$ var = $ false?null?:“字符串为空/ false / null / undefined”;
RedSparr0w

哇... ?? null ?:事情太棒了,谢谢先生。聪明的家伙。
布莱恩·拉弗里尼尔

1
class a
{
    public $a = 'aaa';
}

$a = new a();

echo $a->a;  // Writes 'aaa'
echo $a->b;  // Notice: Undefined property: a::$b

echo $a->a ?? '$a->a does not exists';  // Writes 'aaa'

// Does not throw an error although $a->b does not exist.
echo $a->b ?? '$a->b does not exist.';  // Writes $a->b does not exist.

// Does not throw an error although $a->b and also $a->b->c does not exist.
echo $a->b->c ?? '$a->b->c does not exist.';  // Writes $a->b->c does not exist.

0

Null Coalescing operator仅执行两项任务:检查whether the variable is setwhether it is null。看下面的例子:

<?php
# case 1:
$greeting = 'Hola';
echo $greeting ?? 'Hi There'; # outputs: 'Hola'

# case 2:
$greeting = null;
echo $greeting ?? 'Hi There'; # outputs: 'Hi There'

# case 3:
unset($greeting);
echo $greeting ?? 'Hi There'; # outputs: 'Hi There'

上面的代码示例指出,Null Coalescing operator对待不存在的变量和以NULL相同方式设置为的变量。

Null Coalescing operator是对的改进ternary operator。看一下下面的代码片段,比较两者:

<?php /* example: checking for the $_POST field that goes by the name of 'fullname'*/
# in ternary operator
echo "Welcome ", (isset($_POST['fullname']) && !is_null($_POST['fullname']) ? $_POST['fullname'] : 'Mr. Whosoever.'); # outputs: Welcome Mr. Whosoever.
# in null coalecing operator
echo "Welcome ", ($_POST['fullname'] ?? 'Mr. Whosoever.'); # outputs: Welcome Mr. Whosoever.

因此,两者之间的区别在于,Null Coalescing operator运算符设计为比更好地处理未定义的变量ternary operator。鉴于,ternary operator是一个速记if-else

Null Coalescing operator并不是要替换ternary operator,而是在如上例所示的某些用例中,它使您可以编写麻烦的干净代码。

鸣谢:http : //dwellupper.io/post/6/php7-null-coalescing-operator-usage-and-examples


isset($_POST['fullname'])已经检查过NULL值-因此&& !is_null($_POST['fullname'])第一个示例中的还是多余的
Yaron

0

当使用$ _GET或$ _REQUEST之类的超全局变量时,应注意它们可能是一个空字符串。在这种情况下,这个例子

$username = $_GET['user'] ?? 'nobody';

将失败,因为$ username的值现在是一个空字符串。

因此,在使用$ _GET甚至$ _REQUEST时,应使用三元运算符,如下所示:

$username = (!empty($_GET['user'])?$_GET['user']:'nobody';

现在$ username的值是“ nobody”,符合预期。


接得好。另外,如果字符串为空,则合并运算符也会失败。
Choxx
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.