74

In Python one can do:

foo = {}
assert foo.get('bar', 'baz') == 'baz'

In PHP one can go for a trinary operator as in:

$foo = array();
assert( (isset($foo['bar'])) ? $foo['bar'] : 'baz' == 'baz' );

I am looking for a golf version. Can I do it shorter/better in PHP?

UPDATE [March 2020]:

assert($foo['bar'] ?? 'baz' == 'baz');

It seems that Null coalescing operator ?? is worth checking out today.

found in the comments below (+1)

5
  • I am qualifying ==$_=& for a hack answer. Though true answer is no - there is no shortcut for this. Looks like a nice feature request, smth like array_get($foo, 'bar', 'baz') function. In fact, there is one pending request with patch for PHP 6 bugs.php.net/bug.php?id=40792 Commented Jul 19, 2011 at 23:02
  • 3
    I don't understand why this isn't built in into php. It's so simple and so useful, should have been there for ages. How many versions of PHP do they have to put out before fixing this? Do the people that create PHP actually use it? What's the point of writing an expression twice? Commented Mar 8, 2013 at 16:55
  • 1
    FYI Yauhen: a better (and shorter) Python idiom is to simply use the default no-match-value which is None, and assert directly that we didn't get None: assert foo.get('bar') is not None which is equivalent to assert foo.get('bar', None) is not None Commented Sep 8, 2015 at 19:25
  • Also you might like to add the tag language-design Commented Sep 8, 2015 at 19:28
  • 2
    The updated answer is use the null coalescing operating "??" for PHP 7: stackoverflow.com/a/41246606/1151229 Commented Mar 10, 2020 at 20:26

8 Answers 8

81

Time passes and PHP is evolving. PHP 7 now supports the null coalescing operator, ??:

// 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';
Sign up to request clarification or add additional context in comments.

4 Comments

I find this answer misleading, as it is not checking for the array existence. If the key and the array variable don't exist this allows it leniently. I find answer stackoverflow.com/a/22389397/330624 more accurate.
@msemelman The question was about getting default key from array - nothing about checking whether array exists.
@IvanYarych isset($foo['bar']) in the question is a longstanding idiom for checking if the array key exists. Not a good one for sure, but that was a very common usage.
Still not quite the same as a default value if the key is not set , in cases when the value is NULL . $a = array('b' => NULL); print($a['b'] ?? 'default'); // prints 'default'
54

I just came up with this little helper function:

function get(&$var, $default=null) {
    return isset($var) ? $var : $default;
}

Not only does this work for dictionaries, but for all kind of variables:

$test = array('foo'=>'bar');
get($test['foo'],'nope'); // bar
get($test['baz'],'nope'); // nope
get($test['spam']['eggs'],'nope'); // nope
get($undefined,'nope'); // nope

Passing a previously undefined variable per reference doesn't cause a NOTICE error. Instead, passing $var by reference will define it and set it to null. The default value will also be returned if the passed variable is null. Also note the implicitly generated array in the spam/eggs example:

json_encode($test); // {"foo":"bar","baz":null,"spam":{"eggs":null}}
$undefined===null; // true (got defined by passing it to get)
isset($undefined) // false
get($undefined,'nope'); // nope

Note that even though $var is passed by reference, the result of get($var) will be a copy of $var, not a reference. I hope this helps!

5 Comments

Is there any way to delete/unset $ar[0][1][1] created by get($ar[0][1][1]), 'def'); ?
I wrote a blog post to further investigate the problem and explain my solution.
Better use !empty instead of isset, it verifies if the variable actually has a value.
It will throw a warning when you use it the following way: get($test['foo']['bar']);
24

Use the error control operator @ with the PHP 5.3 shortcut version of the ternary operator:

$bar = @$foo['bar'] ?: 'defaultvalue';

6 Comments

Interesting. What else can go wrong except for syntax errors in such a small portion of code (which is maybe not that dramatic)?
I just noticed this pattern in our codebase. It feels very wrong somehow, but it works.
Note that all this does is temporarily set error_reporting(0) while evaluating $foo['bar']. It will still invoke the error handler, and it is the responsibility of the error handler to ignore the error if error_reporting() === 0.
To the top with you!
Doesn't work for values like 0.$foo = ['bar' => 0]; $bar = @$foo['bar'] ?: 'defaultvalue'; gives "defaultvalue".
|
12

I find it useful to create a function like so:

function array_value($array, $key, $default_value = null) {
    return is_array($array) && array_key_exists($key, $array) ? $array[$key] : $default_value;
}

And use it like this:

$params = array('code' => 7777, 'name' => "Cloud Strife"); 

$code    = array_value($params, 'code');
$name    = array_value($params, 'name');
$weapon  = array_value($params, 'weapon', "Buster Sword");
$materia = array_value($params, 'materia');

echo "{ code: $code, name: $name, weapon: $weapon, materia: $materia }";

The default value in this case is null, but you may set it to whatever you need.

I hope it is useful.

4 Comments

Yup, array_key_exists would be better instead of isset.
I'd recommend switching the order of $array, $key since the built-in functions have it like that e.g. array_key_exists($key, $array)
small bug, if it is not an array it returns false instead of default_value
@malhal. Interesting. Must be order-of-precedence of operators. To be sure is executed as expected, add parentheses: return (is_array($array) && array_key_exists($key, $array)) ? ....
7

PHP 5.3 has a shortcut version of the ternary operator:

$x = $foo ?: 'defaultvaluehere';

which is basically

if (isset($foo)) {
   $x = $foo;
else {
   $x = 'defaultvaluehere';
}

Otherwise, no, there's no shorter method.

7 Comments

The short ternary syntax is equivalent to $x = isset($foo) ? isset($foo) : 'defaultvaluehere';, not $x = isset($foo) ? $foo : 'defaultvaluehere';
@Marc B But that doesn't work if false or the empty string are valid values in the dictionary, does it?
@phihag: yeah. the joys of PHP's typecast. In that case, you'd need to use the long-form if() statement with strict equality checking (===/!==)
This DOES NOT WORK. The short ternary syntax still generates a NOTICE error when the variable is undefined, unlike the isset function.
yeah, this won't work. Ternary operators don't give implicit isset() calls
|
6

A "slightly" hacky way to do it:

<?php
    $foo = array();
    var_dump('baz' == $tmp = &$foo['bar']);
    $foo['bar'] = 'baz';
    var_dump('baz' == $tmp = &$foo['bar']);

http://codepad.viper-7.com/flXHCH

Obviously this isn't really the nice way to do it. But it is handy in other situations. E.g. I often declare shortcuts to GET and POST variables like that:

<?php
    $name =& $_GET['name'];
    // instead of
    $name = isset($_GET['name']) ? $_GET['name'] : null;

PS: One could call this the "built-in ==$_=& special comparison operator":

<?php
    var_dump('baz' ==$_=& $foo['bar']);

PPS: Well, you could obviously just use

<?php
    var_dump('baz' == @$foo['bar']);

but that's even worse than the ==$_=& operator. People don't like the error suppression operator much, you know.

1 Comment

Why does =& suppress the notice for an undefined index? I get that & makes it a reference, but I don't get why that helps here.
2

If you enumerate the default values by key in an array, it can be done this way:

$foo = array('a' => 1, 'b' => 2);
$defaults = array('b' => 55, 'c' => 44);

$foo = array_merge($defaults, $foo);

print_r($foo);

Which results in:

Array
(
    [b] => 2
    [c] => 44
    [a] => 1
)

The more key/value pairs that you enumerate defaults for, the better the code-golf becomes.

1 Comment

This is actually pretty useful for $_GET, for example
2

There was a solution proposed by "Marc B" to use ternary shortcut $x = $foo ?: 'defaultvaluehere'; but it still gives notices. Probably it's a mistyping, maybe he meant ?? or it were written before PHP 7 release. According to Ternary description:

Since PHP 5.3, it is possible to leave out the middle part of the ternary operator. Expression expr1 ?: expr3 returns expr1 if expr1 evaluates to TRUE, and expr3 otherwise.

But it doesn't use isset inside and produces notices. To avoid notices better to use Null Coalescing Operator ?? which uses isset inside it. Available in PHP 7.

The expression (expr1) ?? (expr2) evaluates to expr2 if expr1 is NULL, and expr1 otherwise. In particular, this operator does not emit a notice if the left-hand side value does not exist, just like isset(). This is especially useful on array keys.

Example #5 Assigning a default value

<?php
// Example usage for: Null Coalesce Operator
$action = $_POST['action'] ?? 'default';

// The above is identical to this if/else statement
if (isset($_POST['action'])) {
    $action = $_POST['action'];
} else {
    $action = 'default';
}

?>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.