PHP中的null合并运算符(??)对应C#中的哪个运算符?

52

在PHP中是否有类似C#中的??的三元运算符或类似的操作符?

??在C#中很简洁,但在PHP中你需要做像下面这样的事情:

// This is absolutely okay except that $_REQUEST['test'] is kind of redundant.
echo isset($_REQUEST['test'])? $_REQUEST['test'] : 'hi';

// This is perfect! Shorter and cleaner, but only in this situation.
echo null? : 'replacement if empty';

// This line gives error when $_REQUEST['test'] is NOT set.
echo $_REQUEST['test']?: 'hi';

“?:”非常接近于“??”。实际上,“?:”比“??”捕获更多的类似null的情况;“??”专门用于“null”和“!Nullabe<T>.HasValue”。您似乎正在寻找类似JavaScript的“||”运算符的内容。它就像“?:”,但JavaScript不会抱怨引用未定义的键/成员--尽管如果您尝试引用未定义/空值的键/成员,它会抛出错误,因此您只能进入一级。 - Zenexer
@dpp,你为什么说“someres”,然后又改成“test”了? - Pacerier
5
请查看第七版本。我们终于拥有了它。 - Marcelo Camargo
2
Php 7有这个功能。请查看https://wiki.php.net/rfc/isset_ternary - Mukesh
2
正如所述,这将使用PHP 7进行。在早期版本中,我认为这是错误抑制运算符的少数有效用例之一,例如:echo @$_REQUEST ['someres'] ?: 'hi'; ,其中抑制了错误。 - El Yobo
6个回答

70
PHP 7 添加了空合并运算符
// 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';

你还可以看一下PHP的三元运算符的简写方式?:(仅适用于PHP >=5.3)。
// Example usage for: Short Ternary Operator
$action = $_POST['action'] ?: 'default';

// The above is identical to
$action = $_POST['action'] ? $_POST['action'] : 'default';

而且你对C#的比较并不公平。“在PHP中你必须这样做” - 在C#中,如果你尝试访问一个不存在的数组/字典项,也会出现运行时错误。

7
真的吗?那么如果我访问一个不存在的数组元素?我也会得到错误,是的,这很有道理。 - kazinix
1
这是一个完全公平的陈述。System.Linq命名空间提供了扩展方法TSource Enumerable.ElementAtOrDefault<TSource>(this IEnumerable<TSource> source, int index) 用于数组等。 IDictionary<TKey, TValue>本身提供了此方法bool IDictionary<TKey, TValue>.TryGetValue(TKey key, out TValue value)。此方法返回两个值,因为C#更明确地区分null和未设置。如果您希望将null和未设置视为相同,则可以轻松编写扩展方法。 - Zenexer
@Zenexer:嗯,你认为我的答案是不正确的。我认为你的答案是错误的。有问题吗? - zerkms
让我们在聊天中继续这个讨论 - Zenexer
1
@zerkms 在 Web 开发中,我们经常处理用户输入。即使是 API 也可能生成具有“可变字段数量”的结构,我们仍然需要与它们集成。毕竟,isset 函数背后有一个原因。我的例子:empty($var) 不是很好,但我应该写类似于 empty($var['key']) 的东西(假设 $var 确实被定义,但我对 'key' 的存在存疑),但为了简洁起见省略了它。 - pilat
显示剩余14条评论

53

Null Coalesce运算符??)已被接受并实现在PHP 7中。它与短三目运算符 (?:) 的区别在于,??将抑制尝试访问不存在键的数组时会发生的E_NOTICE错误。RFC中的第一个示例为:

$username = $_GET['user'] ?? 'nobody';
// equivalent to: $username = isset($_GET['user']) ? $_GET['user'] : 'nobody';

注意,?? 运算符不需要手动应用 isset 来防止 E_NOTICE


2
它实际上可以在 isset() 能使用的任何地方工作,因此它也适用于变量和属性。 - Andrea
2
另外,一个很好的特性是你可以链式调用它,例如:$_GET['user'] ?? $_SESSION['user'] ?? 'bob'; - 并且由于它支持短路运算,你甚至可以在其中调用函数。此外,与 isset($foo) ? $foo : $bar 不同的是,如果它是一个函数调用(例如 foo()[0]),它不会被评估两次。 :) - Andrea
1
@AndreaFaulds 你应该说,与普通的三元运算符不同的是,你可以在没有创伤性脑损伤的情况下链式调用它。 - kojiro
你也可以使用短三元操作符 ?: 来完成这个操作,但它是一个不同的使用案例。 - Andrea

10

我使用函数。显然它不是运算符,但似乎比你的方法更清晰:

function isset_or(&$check, $alternate = NULL)
{
    return (isset($check)) ? $check : $alternate;
}

使用方法:

isset_or($_REQUEST['test'],'hi');

1
为什么要将 $check 变量作为引用传递? - Axel A. García
5
@AxelA.Grazx说:“我使用引用传递,因为如果不使用引用传递,$_REQUEST['test'] 的值将在函数调用之前计算并且传递到函数中。如果 $REQUEST 数组中没有这个元素,它会抛出 Undefined index: test 错误。我想避免这种情况。” - LukLed
哦...现在我明白了...不错的方法。 - Axel A. García
这个答案应该更高 - 我一直在使用相同的方法,非常整洁。 - Chris Middleton
4
这会导致一个负面影响,即将已检查的键设置为null:http://3v4l.org/9vmFR。这里有一个情况尤其棘手:http://3v4l.org/sMsKD。 - Zenexer

6
在 PHP 7 之前,是没有的。如果需要使用 isset,可以使用以下语法:isset($var) ? $var : null。没有包含 isset 特性的 ?: 运算符。

4
基本上这使得它对大多数情况无用。:/ - ThiefMaster
4
我仍然发现它有很多用途;基本上它是 PHP 版本的 Javascript 的 || 运算符,非常方便。 :) - deceze

4
在C#中,??是二元运算符,而不是三元运算符。在PHP 7之前的版本中,它没有相应的等价物。

1
在PHP 5.3中,相当于?:。例如,test ?: ifNull === test ? test : ifNull。换句话说,在PHP中,?:可以是二元的或三元的,因为中间操作数是可选的。 - devios1
实际上,C#中的??是三元运算符。它只是三元操作的语法糖,只是以二进制形式书写。 - Zenexer
3
@Zenexer:它是一个二元操作符,不仅仅是语法糖——func() ?? otherfunc() 只调用一次 func,而 func()!=null ? func() : otherfunc() 则调用两次。因此,如果 func 有副作用,则结果完全不同。 - leviathanbadger
很有趣;我不知道那个。 - Zenexer

1

截至PHP 5.6,不存在相同的运算符,但您可以创建一个类似的函数。

/**
 * Returns the first entry that passes an isset() test.
 *
 * Each entry can either be a single value: $value, or an array-key pair:
 * $array, $key.  If all entries fail isset(), or no entries are passed,
 * then first() will return null.
 *
 * $array must be an array that passes isset() on its own, or it will be
 * treated as a standalone $value.  $key must be a valid array key, or
 * both $array and $key will be treated as standalone $value entries. To
 * be considered a valid key, $key must pass:
 *
 *     is_null($key) || is_string($key) || is_int($key) || is_float($key)
 *         || is_bool($key)
 *
 * If $value is an array, it must be the last entry, the following entry
 * must be a valid array-key pair, or the following entry's $value must
 * not be a valid $key.  Otherwise, $value and the immediately following
 * $value will be treated as an array-key pair's $array and $key,
 * respectfully.  See above for $key validity tests.
 */
function first(/* [(array $array, $key) | $value]... */)
{
    $count = func_num_args();

    for ($i = 0; $i < $count - 1; $i++)
    {
        $arg = func_get_arg($i);

        if (!isset($arg))
        {
            continue;
        }

        if (is_array($arg))
        {
            $key = func_get_arg($i + 1);

            if (is_null($key) || is_string($key) || is_int($key) || is_float($key) || is_bool($key))
            {
                if (isset($arg[$key]))
                {
                    return $arg[$key];
                }

                $i++;
                continue;
            }
        }

        return $arg;
    }

    if ($i < $count)
    {
        return func_get_arg($i);
    }

    return null;
}

使用方法:

$option = first($option_override, $_REQUEST, 'option', $_SESSION, 'option', false);

这段代码会尝试每个变量,直到找到一个满足 isset() 的变量:
  1. $option_override
  2. $_REQUEST['option']
  3. $_SESSION['option']
  4. false
如果没有第4个变量,则默认为 null
注意:有一个更简单的实现使用引用,但它会导致一个副作用 如果被测试的项不存在,则将其设置为null。当数组的大小或真值很重要时,这可能会有问题(译者注:即可能会影响程序正常运行)

不回答OP的问题,即“是否存在这样的运算符”。此外,@LukLed的答案(在此之后几个月发布)展示了一种类似的方法,而无需解构关联数组索引。 - benrifkah
@benrifkah LukLed的回答有一个负面影响,即将指定的键设置为null:http://3v4l.org/9vmFR 如果您获取不存在的键的引用,则会创建该键。如果您正在使用不存在的数组,则尤其问题严重:http://3v4l.org/sMsKD - Zenexer
我从这个“first”函数中看到了奇怪的结果。愿意聊一聊吗?https://chat.stackoverflow.com/rooms/79985/chat-about-php-null-coalesce-function - benrifkah
@benrifkah 抱歉回复晚了,我现在就去那里。 - Zenexer

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