71

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)

Community
  • 1
  • 1
Yauhen Yakimovich
  • 13,635
  • 8
  • 60
  • 67
  • 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 https://bugs.php.net/bug.php?id=40792 – Yauhen Yakimovich Jul 19 '11 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? – Jens Mar 08 '13 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` – smci Sep 08 '15 at 19:25
  • Also you might like to add the tag [tag:language-design] – smci Sep 08 '15 at 19:28
  • 2
    The updated answer is use the null coalescing operating "??" for PHP 7: https://stackoverflow.com/a/41246606/1151229 – Brian Peterson Mar 10 '20 at 20:26

8 Answers8

75

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';
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Ivan Yarych
  • 1,931
  • 17
  • 15
  • 1
    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 https://stackoverflow.com/a/22389397/330624 more accurate. – msemelman Mar 27 '20 at 14:11
  • 1
    @msemelman The question was about getting default key from array - nothing about checking whether array exists. – Ivan Yarych Mar 27 '20 at 14:42
  • @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. – Izkata Apr 25 '23 at 19:00
53

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!

stepmuel
  • 1,208
  • 12
  • 6
24

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

$bar = @$foo['bar'] ?: 'defaultvalue';
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
romanlv
  • 1,432
  • 15
  • 15
  • Interesting. What else can go wrong except for syntax errors in such a small portion of code (which is maybe not that dramatic)? – Yauhen Yakimovich Jun 24 '14 at 08:06
  • 1
    I just noticed this pattern in our codebase. It feels very wrong somehow, but it works. – André Laszlo Mar 24 '15 at 16:44
  • 5
    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`. – Jesse Aug 21 '15 at 12:28
  • To the top with you! – Mr. Lance E Sloan Mar 17 '16 at 21:12
  • 2
    Doesn't work for values like `0`.`$foo = ['bar' => 0]; $bar = @$foo['bar'] ?: 'defaultvalue';` gives "defaultvalue". – Adam Barnes Feb 25 '19 at 21:08
  • To underscore @AdamBarnes comment: This answer is a bad habit to get into, as it returns default value for any "value equivalent to false" (0, empty string, ..). Instead, use any answer that only returns default value for `null` (e.g. uses `isset` or null-coelescing). Or if `null` should be allowed, then use an answer that does `array_key_exists`. – ToolmakerSteve Jun 03 '20 at 22:36
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.

rbento
  • 9,919
  • 3
  • 61
  • 61
  • Yup, `array_key_exists` would be better instead of `isset`. – Kerem Feb 14 '15 at 02:07
  • 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) – malhal Apr 06 '15 at 21:08
  • small bug, if it is not an array it returns false instead of default_value – malhal Apr 06 '15 at 21:09
  • @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)) ? ...`. – ToolmakerSteve Jun 03 '20 at 22:12
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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Marc B
  • 356,200
  • 43
  • 426
  • 500
  • 1
    The short ternary syntax is equivalent to `$x = isset($foo) ? isset($foo) : 'defaultvaluehere';`, not `$x = isset($foo) ? $foo : 'defaultvaluehere';` – NikiC Jul 14 '11 at 16:16
  • 1
    @Marc B But that doesn't work if false or the empty string are valid values in the dictionary, does it? – phihag Jul 14 '11 at 16:19
  • @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 (`===`/`!==`) – Marc B Jul 14 '11 at 16:20
  • 26
    This DOES NOT WORK. The short ternary syntax still generates a `NOTICE` error when the variable is undefined, unlike the `isset` function. – Chris B. Jun 24 '13 at 00:30
  • 2
    yeah, this won't work. Ternary operators don't give implicit isset() calls – kdazzle Jul 01 '13 at 19:43
  • How about ``$x = @$foo ?: 'defaultval';`` ? – TeNNoX Jun 15 '16 at 13:03
  • @TeNNoX that works but the error still shows on psysh console. – CMCDragonkai Feb 17 '17 at 02:25
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.

NikiC
  • 100,734
  • 37
  • 191
  • 225
  • 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. – Grumdrig Nov 10 '21 at 06:38
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';
}

?>
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.

Rusty Fausak
  • 7,355
  • 1
  • 27
  • 38