140

Many programming languages have a coalesce function (returns the first non-NULL value, example). PHP, sadly in 2009, does not.

What would be a good way to implement one in PHP until PHP itself gets a coalesce function?

hakre
  • 193,403
  • 52
  • 435
  • 836
mikl
  • 23,749
  • 20
  • 68
  • 89

10 Answers10

203

There is a new operator in php 5.3 which does this: ?:

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

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

// B
echo false ?: 'B';

// B
echo null ?: 'B';

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

Kevin
  • 13,044
  • 11
  • 55
  • 76
  • 25
    What about multiple ternary shortcuts, would something like "echo $a ?: $b ?: $c ?: $d;" work? – ChrisR Mar 26 '10 at 13:55
  • 5
    Does not work as expected for arrays. For example when trying to check if an undefined array element is falsey will result in an error. `$input['properties']['range_low'] ?: '?'` – Benbob Jul 12 '11 at 23:28
  • 5
    You should get an Undefined Index notice irrespective of using the coalesce operator. – Kevin Jul 13 '11 at 17:16
  • [from the manual for the Ternary Operator](http://www.php.net/manual/en/language.operators.comparison.php#language.operators.comparison.ternary): Expression `expr1 ?: expr3` returns `expr1` if `expr1` evaluates to `TRUE`, and `expr3` otherwise – rymo Sep 04 '12 at 21:40
  • Too bad that `or` doesn't work like this in PHP. (It does the job in Python, for instance) – Kos Sep 24 '12 at 10:00
  • 2
    Multiple falsey arguments return the last argument, `array() ?: null ?: false` returns `false`. The operator is indeed sane. – Brad Koch Oct 07 '13 at 13:19
  • 6
    Keep in mind that this doesn't just accept non-null like coalesce in other languages, but any value, which will be implicitly converted to a boolean. So make sure you brush up on your [type casting rules](http://www.php.net/manual/en/types.comparisons.php) – DanMan May 24 '14 at 11:48
  • 1
    I just spent the last hour scratching my head with my code and finally realized it was my use of this coalescing operator, which as shown in this answer, takes the first NON-FALSY (!!!) value. This is different than the null coalescing operator I want. :-( – Tyler Collier Jul 11 '14 at 05:05
  • 2
    As DanMan said, this is not a "coalesce" as the title suggests. The answer from Will Shaver is actually coalesce. Coalesce returns the first non-null value. ($value ?: $other) is the same as ($value ? $value : $other). So, if $value === false then $other is returned. A true coalesce would return false because $value is non-null. – OneCleverMonkey Aug 21 '14 at 23:36
73

PHP 7 introduced a real coalesce operator:

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

If the value before the ?? does not exists or is null the value after the ?? is taken.

The improvement over the mentioned ?: operator is, that the ?? also handles undefined variables without throwing an E_NOTICE.

flori
  • 14,339
  • 4
  • 56
  • 63
  • 1
    Finally no more isset() and empty() all over the place! – George Kagan Apr 04 '16 at 07:06
  • 9
    @timeNomad you will still need is empty, it checks for null only – Nabeel Khan May 19 '16 at 00:35
  • The only way to get safe "falsy-coalesce" is to use a bit of both: `($_GET['doesNotExist'] ?? null) ?: 'fallback'` – Nathan Baulch Jul 18 '17 at 01:37
  • 1
    The advantage of `?:` over `??`, however, is that it coalesces empty values as well which `??` does not do. Similar to the behavior of the logical OR operator in JavaScript (i.e. `$val || 'default'`), I would find `?:` to be a more practical form of *coalescing* if in our practice we ultimately find ourselves handling both *empty* and *null* in the same way (i.e. `$val ?: 'default'`). And if you want to force the issue further and swallow `E_NOTICE`, you could argue this even: `echo @$val ?: 'default';` – Matt Borja Jan 07 '20 at 05:13
  • Will it give fatal error if used in libraries that sill support php older versions like 5.5? – Syed Waqas Bukhary Jan 16 '23 at 18:07
30

First hit for "php coalesce" on google.

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

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

Will Shaver
  • 12,471
  • 5
  • 49
  • 64
  • 9
    Save a tiny bit of ram and don't duplicate the args into an array, just do foreach(func_get_args() as $arg) {} – TravisO Jun 18 '09 at 20:38
  • @TravisO I believe that caching func_get_args() is faster because it will save data in variable instead of calculating for every loop – Alfred Jun 19 '09 at 02:31
  • @TravisO yeah like Alfred said, if you bring the call to func_get_args() into the loop condition it'll get called each time - PHP can't tell that the function result won't change each time. – Ciaran McNulty Jun 19 '09 at 08:11
  • 18
    @[Alfred,Ciaran] - you are incorrect. foreach() evaluates the first argument only once, to get an array, and then iterates over it. – gahooa Dec 12 '09 at 01:34
  • 6
    Putting func_get_args() inside the foreach (here as $arg) won't change anything from a performance point of view. – Savageman Dec 12 '09 at 01:47
  • 7
    @Savageman ... exactly ... if you are thinking of squeezing this millisecond of performance or few bytes of memory out of your application you're probably looking at the wrong performance/memory bottleneck – ChrisR Mar 26 '10 at 13:53
  • 5
    Ironically, this is now the first hit for "php coalesce" on Google. – Will Shaver Aug 23 '14 at 15:52
  • 1
    Shouldn't it be `if (!is_null($arg)) {`, since coalesce should return the first non-null value (even if it's 0, `false` or something else falsey). – caponica Oct 08 '16 at 21:33
  • This is not "real" coalesce function – Oto Shavadze Nov 23 '16 at 13:38
18

I really like the ?: operator. Unfortunately, it is not yet implemented on my production environment. So I use the equivalent of this:

function coalesce() {
  return array_shift(array_filter(func_get_args()));
}
Ethan Kent
  • 271
  • 3
  • 2
  • 1
    this is a 'truthy' coalesce, using array_filter to get rid of anything that evaluates to false (including null) in the n arguments passed in. My guess is using shift instead of the first element in the array is somehow more robust, but that part I don't know. see: http://php.net/manual/en/language.types.boolean.php#language.types.boolean.casting – Adam Tolley Jan 18 '13 at 21:19
  • 3
    I like it but have to agree with @hakre - `coalesce` is supposed to return the first *non-null* argument it encounters, which would include `FALSE`. This function will discard `FALSE` though, probably not what op has in mind (at least not what I'd want out of a `coalesce` function). – Madbreaks Feb 20 '13 at 22:11
  • 1
    Only variables should be passed by reference – pronebird Aug 16 '15 at 10:30
10

It is worth noting that due to PHP's treatment of uninitalised variables and array indices, any kind of coalesce function is of limited use. I would love to be able to do this:

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

But this will, in most cases, cause PHP to error with an E_NOTICE. The only safe way to test the existence of a variable before using it is to use it directly in empty() or isset(). The ternary operator suggested by Kevin is the best option if you know that all the options in your coalesce are known to be initialised.

Brad Koch
  • 19,267
  • 19
  • 110
  • 137
Andrew
  • 2,094
  • 19
  • 24
  • In this case, array unions work pretty nicely (`$getstuff = $_GET+$_SESSION+array('id'=>null);$id=$getstuff['id'];`). – Brilliand Dec 29 '14 at 22:58
  • @Quill what's that supposed to mean? Did you the suggested solution with reference? – pronebird Aug 16 '15 at 10:08
  • PHP 7 introduces the lovely new [isset ternary](https://wiki.php.net/rfc/isset_ternary) operator `??` to make this very common operation more concise. – botimer Sep 11 '15 at 20:28
6

Make sure you identify exactly how you want this function to work with certain types. PHP has a wide variety of type-checking or similar functions, so make sure you know how they work. This is an example comparison of is_null() and 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>';
}

As you can see, empty() returns true for all of these, but is_null() only does so for 2 of them.

Peter Bailey
  • 105,256
  • 31
  • 182
  • 206
2

I'm expanding on the answer posted by Ethan Kent. That answer will discard non-null arguments that evaluate to false due to the inner workings of array_filter, which isn't what a coalesce function typically does. For example:

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

Oops

To overcome this, a second argument and function definition are required. The callable function is responsible for telling array_filter whether or not to add the current array value to the result array:

// "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'));
}

It would be nice if you could simply pass isset or 'isset' as the 2nd argument to array_filter, but no such luck.

hakre
  • 193,403
  • 52
  • 435
  • 836
Madbreaks
  • 19,094
  • 7
  • 58
  • 72
0

I'm currently using this, but I wonder if it couldn't be improved with some of the new features in PHP 5.

function coalesce() {
  $args = func_get_args();
  foreach ($args as $arg) {
    if (!empty($arg)) {
    return $arg;
    }
  }
  return $args[0];
}
mikl
  • 23,749
  • 20
  • 68
  • 89
0

PHP 5.3+, with closures:

function coalesce()
{
    return array_shift(array_filter(func_get_args(), function ($value) {
        return !is_null($value);
    }));
}

Demo: https://eval.in/187365

Paulo Freitas
  • 13,194
  • 14
  • 74
  • 96
0

A function to return the first non-NULL value:

  function coalesce(&...$args) { // ... as of PHP 5.6
    foreach ($args as $arg) {
      if (isset($arg)) return $arg;
    }
  }

Equivalent to $var1 ?? $var2 ?? null in PHP 7+.

A function to return the first non-empty value:

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

Equivalent to (isset($var1) ? $var1 : null) ?: (isset($var2) ? $var2 : null) ?: null in PHP 5.3+.

The above will consider a numerical string of "0.00" as non-empty. Typically coming from a HTTP GET, HTTP POST, browser cookie or the MySQL driver passing a float or decimal as numerical string.

A function to return the first variable that is not undefined, 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;
    }
  }

To be used like:

$myvar = coalesce($var1, $var2, $var3, ...);
tim
  • 2,530
  • 3
  • 26
  • 45