PHP中的合并函数是什么?

140
许多编程语言都有一个 coalesce 函数(返回第一个非 NULL 值,例如这里)。可悲的是,PHP在2009年还没有该函数。

在 PHP 自己获得 coalesce 函数之前,有什么好的方法来实现它呢?


12
相关:PHP 7 中的新的空合并运算符 ?? - kojiro
有关空值合并运算符的更多信息,请查看此处 - http://stackoverflow.com/questions/33666256/null-coalesce-operator-in-php - Peter
1
请注意,PHP7已经实现了这个函数。 - Grzegorz
@Grzegorz:运算符不是函数,或者你在哪里找到了PHP 7中的new函数 ;) - hakre
我所说的“function”并不是指函数,而是指特性。我表达得不够准确,谢谢 :) - Grzegorz
10个回答

203

在php 5.3中有一个新的运算符能够实现这个功能:?:

// A
echo 'A' ?: 'B';

// B
echo '' ?: 'B';

// B
echo false ?: 'B';

// B
echo null ?: 'B';

来源: http://www.php.net/ChangeLog-5.php#5.3.0


25
多个三元运算符的缩写怎样?像 "echo $a ?: $b ?: $c ?: $d;" 这样行吗? - ChrisR
5
针对数组,其行为与预期不符。例如尝试检查未定义的数组元素是否为falsey会导致错误。$input['properties']['range_low'] ?: '?' - Benbob
5
无论是否使用合并运算符,您都应该收到“未定义索引”通知。 - Kevin
2
多个假值参数返回最后一个参数,array() ?: null ?: false 返回 false。这个运算符确实很合理。 - Brad Koch
6
请记住,这个功能不仅接受非空值,而且像其他语言中的 coalesce 一样接受任何值,并将其隐式转换为布尔值。因此,请确保您对类型转换规则有所了解。类型转换规则链接 - DanMan
显示剩余4条评论

73

PHP 7引入了一个真正的合并运算符

echo $_GET['doesNotExist'] ?? 'fallback'; // prints 'fallback'

如果??之前的值不存在或为 null,则取??之后的值。

?:运算符的改进在于,??处理未定义变量时不会抛出E_NOTICE


1
终于不用在代码的各个地方使用isset()和empty()了! - George Kagan
9
@timeNomad,你仍然需要的是空的,它只检查 null 值。 - Nabeel Khan
获取安全的“falsy-coalesce”的唯一方法是同时使用两者:($_GET['doesNotExist'] ?? null) ?: 'fallback' - Nathan Baulch
1
?: 相对于 ?? 的优势在于它也可以合并空值,而 ?? 不能。类似于 JavaScript 中的逻辑 OR 运算符(即 $val || 'default')的行为,如果我们最终发现自己以相同的方式处理 null,那么我会认为 ?: 是一种更实用的 合并 形式。如果您想进一步强制执行此问题并忽略 E_NOTICE,您甚至可以这样争辩:echo @$val ?: 'default'; - Matt Borja
如果在仍支持 PHP 旧版本(如 5.5)的库中使用,它会产生致命错误吗? - Syed Waqas Bukhary

30

在谷歌上搜索"php coalesce",第一个命中的结果。

function coalesce() {
  $args = func_get_args();
  foreach ($args as $arg) {
    if (!empty($arg)) {
      return $arg;
    }
  }
  return NULL;
}

http://drupial.com/content/php-coalesce


9
不要将参数复制到数组中,以节省一点点的内存,只需使用foreach(func_get_args() as $arg) {}即可。 - TravisO
18
@[Alfred,Ciaran] - 你们的答案是不正确的。foreach()函数只会在最开始的时候对第一个参数进行一次求值,获取到数组后再进行迭代。 - gahooa
6
把func_get_args()放在foreach (here as $arg)里面,从性能角度来看不会有任何变化。 - Savageman
7
如果你想从你的应用程序中挤出这毫秒级别的性能或几个字节的内存,那么你可能正在关注错误的性能/内存瓶颈。 - ChrisR
5
具有讽刺意味的是,这现在成为了谷歌上“php coalesce”的第一个搜索结果。 - Will Shaver
显示剩余4条评论

18

我非常喜欢 ?: 运算符。不幸的是,它还没有在我的生产环境中实现。所以我使用了相当于这个运算符的东西:

function coalesce() {
  return array_shift(array_filter(func_get_args()));
}

1
这是一个“truthy”合并,使用array_filter来摆脱在传递的n个参数中评估为false(包括null)的任何内容。我猜使用shift而不是数组中的第一个元素在某种程度上更加健壮,但这部分我不清楚。参见:http://php.net/manual/en/language.types.boolean.php#language.types.boolean.casting - Adam Tolley
3
我喜欢它,但必须同意@hakre的观点——coalesce应该返回遇到的第一个非空参数,这将包括FALSE。然而,这个函数将舍弃FALSE,可能不是操作者想要的(至少不是我希望从coalesce函数中得到的)。 - Madbreaks
1
只有变量应该以引用方式传递。 - pronebird

10

值得注意的是,由于PHP对未初始化变量和数组索引的处理方式,任何类型的合并函数都有限制。我希望能够这样做:

$id = coalesce($_GET['id'], $_SESSION['id'], null);

但是在大多数情况下,这样做会导致PHP出现E_NOTICE错误。在使用变量之前,唯一安全的测试其存在的方法是直接在empty()或isset()中使用它。如果您知道合并运算符中的所有选项都已知,那么Kevin建议使用三元运算符是最佳选择。


在这种情况下,数组联合非常有效($getstuff = $_GET+$_SESSION+array('id'=>null);$id=$getstuff['id'];)。 - Brilliand
@Quill 这是什么意思?你有参考建议的解决方案吗? - pronebird
PHP 7引入了可爱的新isset ternary运算符??,使这个非常常见的操作更加简洁。 - botimer

6

确保您准确地确定了希望此函数与特定类型一起使用的方式。PHP具有多种类型检查或类似功能,因此请确保您知道它们的工作原理。以下是is_null()和empty()的比较示例。

$testData = array(
  'FALSE'   => FALSE
  ,'0'      => 0
  ,'"0"'    => "0"  
  ,'NULL'   => NULL
  ,'array()'=> array()
  ,'new stdClass()' => new stdClass()
  ,'$undef' => $undef
);

foreach ( $testData as $key => $var )
{
  echo "$key " . (( empty( $var ) ) ? 'is' : 'is not') . " empty<br>";
  echo "$key " . (( is_null( $var ) ) ? 'is' : 'is not')  . " null<br>";
  echo '<hr>';
}

正如您所看到的,empty() 对所有这些值都返回 true,但 is_null() 只对其中 2 个返回 true。


2

我想对Ethan Kent的回答进行扩展。由于array_filter内部机制的原因,该答案会丢弃那些被评估为假的非空参数,而这并不是一个coalesce函数通常所做的事情。例如:

echo 42 === coalesce(null, 0, 42) ? 'Oops' : 'Hooray';

哎呀

为了解决这个问题,需要第二个参数和函数定义。可调用的函数负责告诉 array_filter 是否将当前数组值添加到结果数组中:

// "callable"
function not_null($i){
    return !is_null($i);  // strictly non-null, 'isset' possibly not as much
}

function coalesce(){
    // pass callable to array_filter
    return array_shift(array_filter(func_get_args(), 'not_null'));
}

如果你可以直接将isset'isset'作为第二个参数传递给array_filter,那就太好了,但没有这样的运气。


0

我目前正在使用这个,但我想知道是否可以利用PHP 5的一些新功能来改进它。

function coalesce() {
  $args = func_get_args();
  foreach ($args as $arg) {
    if (!empty($arg)) {
    return $arg;
    }
  }
  return $args[0];
}

0

使用闭包的 PHP 5.3+:

function coalesce()
{
    return array_shift(array_filter(func_get_args(), function ($value) {
        return !is_null($value);
    }));
}
演示:https://eval.in/187365

只有变量应该按引用传递。 - pronebird
是的,我为了演示打破了严格的规则,只是为了让它更简单。 :) - Paulo Freitas

0
一个返回第一个非 NULL 值的函数:
  function coalesce(&...$args) { // ... as of PHP 5.6
    foreach ($args as $arg) {
      if (isset($arg)) return $arg;
    }
  }

在 PHP 7+ 中等同于 $var1 ?? $var2 ?? null

一个返回第一个非空值的函数:

  function coalesce(&...$args) {
    foreach ($args as $arg) {
      if (!empty($arg)) return $arg;
    }
  }

在 PHP 5.3+ 中,等同于 (isset($var1) ? $var1 : null) ?: (isset($var2) ? $var2 : null) ?: null

上述代码将把数值字符串 "0.00" 视为非空。通常来自 HTTP GET、HTTP POST、浏览器 cookie 或 MySQL 驱动程序将浮点数或十进制数作为数值字符串传递的情况。

以下是一个函数,用于返回第一个未定义、null、(bool)false、(int)0、(float)0.00、(string)""、(string)"0"、(string)"0.00"、(array)[] 的变量:

  function coalesce(&...$args) {
    foreach ($args as $arg) {
      if (!empty($arg) && (!is_numeric($arg) || (float)$arg!= 0)) return $arg;
    }
  }

用法如下:

$myvar = coalesce($var1, $var2, $var3, ...);

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