给变量赋默认值的最佳方法(模拟Perl中的||、||=)

164
我喜欢在Perl中这样做:$foo = $bar || $baz,如果$bar为空或未定义,则将$baz赋值给$foo。你还可以使用$foo ||= $bletch,只有当$foo未被定义或为空时才将$bletch赋值给$foo
在PHP中,三元运算符在这种情况下是繁琐和乏味的。是否有一种简单优雅的方法可用?
或者唯一的答案是使用isset()的自定义函数吗?

1
相关问题请参考[Codereview.SE]: http://codereview.stackexchange.com/q/12722/31433 - Palec
1
顺便提一下,具有所需功能的 Perl 运算符是 ////=,它们存在于 Perl v5.10.0 中。原始的 ||||= 用于测试逻辑值,而不是定义性。 - Palec
1
@Palec,为什么一个有29个赞的4岁问题会被认定为一个有6个赞的1岁问题的重复(它本身被标记为另一个问题的重复?)我认为保留这个问题非常有价值,因为标题更通用(不引用答案,即:isset())。 - Tom Auger
你的变量名让我感到困惑。为什么不使用 "x"、"y"、"z" 等呢? - Black
@EdwardBlack 实际上,“foo”、“bar”、“baz”和(可以说)“bletch”在编程世界中非常常见 - 就像在给出示例变量/函数/方法名称的情况下“x”、“y”和“z”一样普遍。 - Tom Auger
显示剩余2条评论
8个回答

237

PHP 5.3 中有一个简略写法的 ?: 运算符:

$foo = $bar ?: $baz;

如果它不是一个空值,那么会将$bar赋值给它(我不知道这在PHP中与Perl有什么不同),否则赋值为$baz。在Perl和早期版本的PHP中,这与以下代码相同:

$foo = $bar ? $bar : $baz;

然而,PHP没有类似于Perl的||=的复合赋值运算符。

此外,如果不关闭提示,当$bar未设置时,PHP会发出警告。同时,isset()empty()之间存在语义差异。前者在变量不存在或被设置为NULL时返回false,后者在变量不存在或被设置为0''falseNULL时返回true。


2
+1,因为你向我介绍了5.3中另一个很棒的功能,而我在我的RHEL5/PHP5.1.2服务器上错过了它。 - Michael Berkowski
我猜你的倒数第二句话是指“第一个返回”,而不是“第二个”。 - Toto
24
请注意,如果你的变量未定义,PHP 将会抛出有关此变量的提示信息。然而,这并不能取代 $var = isset($var) ? $var : '默认值'; 这句话已经在答案中提到了,我只是再次强调一下,方便那些匆忙浏览的人。:-D - Brad
6
@$var ?: 'default value' 这样写有问题吗?如果有,为什么?考虑到唯一的“错误”可能是$var没有设置,并且我们不关心$var是否被设置... - Codemonkey

178

在PHP 7中,我们终于有了一种优雅的方法来做到这一点。它被称为空值合并运算符。您可以像这样使用它:

$name = $_GET['name'] ?? 'john doe';

这相当于

$name = isset($_GET['name']) ? $_GET['name']:'john doe';

15
我认为“飞船操作符”也很有优势。 - Mariano
4
我认为玛丽亚诺是在开我们的玩笑,但事实并非如此,这是一个真实的事情,并且相当准确! - HPWD
2
请注意,空值合并运算符与条件运算符的行为不同,因为它仅适用于 null 值。例如,如果 $_GET['name'] 被设置为空字符串,则第一行将返回一个空字符串,但我们可以使用 $_GET['name'] ? $_GET['name'] : 'john doe' 返回 "john doe"。 - VPhantom
@Mariano,是的,但宇宙飞船运算符在这里有什么相关性呢? - Xsmael
这个答案是误导性的/不正确的。请参考VPhantom的评论,了解为什么这个答案会损害研究者的体验。 - mickmackusa

10
在PHP 7.*之前,可以使用 ?: 来处理一个未定义的变量,并且在本地使用 @ 抑制错误。
$foo = @$bar ?: $baz;

2
在7.*版本之后会发生什么? - XCS
@XCS 看起来运行得很好 - Kimmax

8

感谢所有出色的答案!

对于其他可能寻求替代方案的人,这里提供一些函数,可以帮助简化这类事情。

function set_if_defined(&$var, $test){
    if (isset($test)){
        $var = $test;
        return true;
    } else {
        return false;
    }
}

function set_unless_defined(&$var, $default_var){
    if (! isset($var)){
        $var = $default_var;
        return true;
    } else {
        return false;
    }
}

function select_defined(){
    $l = func_num_args();
    $a = func_get_args();
    for ($i=0; $i<$l; $i++){
        if ($a[$i]) return $a[$i];
    }
}

示例:

// $foo ||= $bar;
set_unless_defined($foo, $bar);

//$foo = $baz || $bletch
$foo = select_defined($baz, $bletch);

我相信这些可以改进。


7

保持与旧版PHP兼容的常见习语是:

 $var = $bool   or   $var = "default";
 // If I use it, then only with excessive spaces for clarity.

这适用于可以在布尔上下文中进行评估的值。这里的优点是,如果变量未定义,它还会为您提供所述的debug e_notice。


如果 $bool 未定义,那么使用 $bool ? $bool : "default" 是否不会发出通知? - BoltClock
当然,没错。我以为 OP 指的是 isset($var) ? $var : DEFAULT。但听起来他无论如何都想避免它们。 - mario

0

一种可能的解决方案:defaultFor()

除非我们有一个工厂解决方案(这确实非常令人恼火),我建议使用以下小助手。它在大多数情况下都能完成任务:


    function defaultFor(&$x,$default=null) {
        if(!isset($x)) $x = $default;
    }

    //--------------------  Now you can do this: --------------

    defaultFor($a,"Jack");  // if $a is not set, it will be "Jack"
    defaultFor($x);         // no more notices for $x but keep it !isset


我希望这可以接近你想要实现的目标。如果你使用它与一个不存在的变量,它不会给你任何提示,而且非常方便。当然它也有一个缺点:默认值总是会预先计算,所以不要将其与一些重型的第二个参数(比如file_get_contents)一起使用。在这种情况下,最好使用isset来代替。

0

这是另一种适用于isset情况的好格式

isset($foo) || $foo= $bar;

另一种简单的方式,将为您提供更多控制权,因为您可以添加更多条件并同时分配给另一个变量

$foo = (isset($oData['foo']))?$bar['foo']:'默认值';


0

我认为一般做这样的事情:

$foo = $bar || $baz;

这是一个不好的想法,除非$bar$baz都是布尔值。如果它们不是:

$bar = 10;
$baz = 11;

然后问题变成了:什么决定某个东西是真还是假?大多数人可能希望零为假,其他所有值为真。但在一些语言中(例如Ruby),只有falsenil为假,这意味着01都为真。因为跨语言歧义的存在,我认为在这些情况下最好明确声明。
if ($bar !== 0) {
   $foo = $bar;
} else {
   $foo = $baz;
}

或者:

$foo = $bar !== 0 ? $bar : $baz;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接