What are the shortest equivalents to the two following statements which do not generate a notice (when error_reporting is E_ALL):
$foo = empty($row['foo']) ? 42 : $row['foo'];
$foo = empty($row['foo']) ? null : $row['foo'];
The variable in empty() could be non existent or an empty string.
This has been bugging me for so long. I have been searching for alternatives across the Internet, including SO.
The null coalescing operator does not seem to work:
$foo = $bar ?? 42;
var_dump($foo);
returns '', not 42.
Are my examples really the shortest form to write it?
The null-coalescing operator (??) will only check for either null or undefined variables.
The shorthand ternary operator (?:) will check for falsy values but can't handle undefined variables by itself.
Therefore, the only shorter way to write these two lines would be to use a combination of both:
$foo = ($row['foo'] ?? null) ?: 42;
and:
$foo = ($row['foo'] ?? null) ?: null;
Whether this is easily readable is debatable, but that should work.
Related
According to PHP documentation, the null coalescing operator ?? is right-associative, i.e.
$a ?? $b ?? $c
is equivalent to
$a ?? ($b ?? $c)
What is the significance of this? Does it make any difference for developers that it is left-associative or right-associative?
To those who are thinking it will call less functions, this is not true because the right operand will not be evaluated if the left operand is not null according to my test:
function f(string $a) : string {
echo "f($a)\n";
return $a;
}
var_dump(f("a") ?? f("b") ?? f("c") ?? f("d"));
echo "===\n";
var_dump(((f("a") ?? f("b")) ?? f("c")) ?? f("d"));
Output:
f(a)
string(1) "a"
===
f(a)
string(1) "a"
I am thinking about performance issues, where you have to evaluate ?? multiple times (if the first value is non null) if it is left-associative while once only if it is right-associative. Compare these two cases:
(("a" ?? "b") ?? "c") ?? "d"
"a" ?? ("b" ?? ("c" ?? "d"))
On the first line, after the second (inner) parentheses ?? "b" is resolved, the value inside the first (outer) parentheses ?? "c" is resolved, then the final ?? "d" is resolved. ?? is performed 3 times.
On the second line, since "a" is not null, the whole chunk of ("b" ?? ("c" ?? "d")) does not need to be resolved. ?? is only performed once.
Although the right-hand-side value is not resolved, checking !== null fewer times might still be beneficial.
(Still, I wonder if this is the only reason?)
As (left) operands don't have to be defined variables (containing alternatively null i.e. undefined value) and just the most right one operand within multiple (stacked) coalesce expressions without explicit parentheses grouping should be "backup value", main effect of implicit right associativity for this case seems to be that warning(s) of undefined variable(s) is (are) prevented. So, that's correct (acceptable) to use for all left operands (except for the most right) variables that aren't (or needn't to be) defined at all.
This code without explicit grouping using parentheses, so applying implicit right associativity
$a = $b ?? $c ?? $d;
echo $a;
outputs
Warning: Undefined variable $d
since all operand variables (including the most right "backup" $d) are undefined.
However, this code with explicit grouping simulating left associativity
$a = ($b ?? $c) ?? $d;
echo $a;
outputs
Warning: Undefined variable $c
Warning: Undefined variable $d
as both (all) right operands are evaluated and considered existing "backup" value.
Check here:
https://3v4l.org/ZvTl9
https://3v4l.org/pEWk9
What is the significance of this?
Compared to the ternary operator (which is left-associative), the null coalesce operator being right-associative allows for stacking. For example:
This won't work
echo isset($foo) ? $foo :
isset($bar) ? $bar :
isset($baz) ? $baz :
'default';
This is how you might expect the ternary operator to work by default coming from C or C++, but you would be wrong. The new ?? operator allows for the following alternative:
This will work
echo $foo ?? $bar ?? $baz ?? 'default';
With isset(), you can do this:
$foo = [];
$baz = [];
$bar = isset($foo['bar'], $baz[$foo['bar']]) ? $baz[$foo['bar']] : '';
Is it possible to achieve the same thing with null coalescing like:
$bar = ($foo[$bazz['foo']]) ?? ''; //Undefined variable: bazz
Im expecting $bar to be:
$bar = '';
??: Null coalescing operator
You have to use the ?? operator twice.
$foo = [];
$baz = [];
$bar = $baz[$foo['bar'] ?? ''] ?? '' ;
See it in action on 3v4l.org
When you are not sure whether a variable/array element exists, you will need to use isset to avoid errors. You can use the # operator to avoid displaying error messages, but the error will just not be displayed, it will occur nonetheless. Avoid the # operator when possible.
The simple answer to that question is that you cannot achieve the same behavior with ?? simply because the operator passes only 1 argument to isset() and you need to pass multiple ones.
The solution with # is really bad and should be avoided. So I suggest just using isset() even for code readability.
According to PHP documentation, the null coalescing operator ?? is right-associative, i.e.
$a ?? $b ?? $c
is equivalent to
$a ?? ($b ?? $c)
What is the significance of this? Does it make any difference for developers that it is left-associative or right-associative?
To those who are thinking it will call less functions, this is not true because the right operand will not be evaluated if the left operand is not null according to my test:
function f(string $a) : string {
echo "f($a)\n";
return $a;
}
var_dump(f("a") ?? f("b") ?? f("c") ?? f("d"));
echo "===\n";
var_dump(((f("a") ?? f("b")) ?? f("c")) ?? f("d"));
Output:
f(a)
string(1) "a"
===
f(a)
string(1) "a"
I am thinking about performance issues, where you have to evaluate ?? multiple times (if the first value is non null) if it is left-associative while once only if it is right-associative. Compare these two cases:
(("a" ?? "b") ?? "c") ?? "d"
"a" ?? ("b" ?? ("c" ?? "d"))
On the first line, after the second (inner) parentheses ?? "b" is resolved, the value inside the first (outer) parentheses ?? "c" is resolved, then the final ?? "d" is resolved. ?? is performed 3 times.
On the second line, since "a" is not null, the whole chunk of ("b" ?? ("c" ?? "d")) does not need to be resolved. ?? is only performed once.
Although the right-hand-side value is not resolved, checking !== null fewer times might still be beneficial.
(Still, I wonder if this is the only reason?)
As (left) operands don't have to be defined variables (containing alternatively null i.e. undefined value) and just the most right one operand within multiple (stacked) coalesce expressions without explicit parentheses grouping should be "backup value", main effect of implicit right associativity for this case seems to be that warning(s) of undefined variable(s) is (are) prevented. So, that's correct (acceptable) to use for all left operands (except for the most right) variables that aren't (or needn't to be) defined at all.
This code without explicit grouping using parentheses, so applying implicit right associativity
$a = $b ?? $c ?? $d;
echo $a;
outputs
Warning: Undefined variable $d
since all operand variables (including the most right "backup" $d) are undefined.
However, this code with explicit grouping simulating left associativity
$a = ($b ?? $c) ?? $d;
echo $a;
outputs
Warning: Undefined variable $c
Warning: Undefined variable $d
as both (all) right operands are evaluated and considered existing "backup" value.
Check here:
https://3v4l.org/ZvTl9
https://3v4l.org/pEWk9
What is the significance of this?
Compared to the ternary operator (which is left-associative), the null coalesce operator being right-associative allows for stacking. For example:
This won't work
echo isset($foo) ? $foo :
isset($bar) ? $bar :
isset($baz) ? $baz :
'default';
This is how you might expect the ternary operator to work by default coming from C or C++, but you would be wrong. The new ?? operator allows for the following alternative:
This will work
echo $foo ?? $bar ?? $baz ?? 'default';
In PHP I find myself writing code like this frequently:
$a = isset($the->very->long->variable[$index])
? $the->very->long->variable[$index]
: null;
Is there a simpler way to do this? Preferably one that doesn't require me to write $the->very->long->variable[$index] twice.
An update, because PHP 7 is now out and is a game-changer on this point ; the previous answers are about PHP 5.
PHP 7 solves this issue. Because you are true at saying that it is frequent to write this in PHP, and that's absolutely not elegant.
In PHP 7 comes the Null Coalesce Operator (RFC), which is a perfect shorthand for the isset ternary condition.
Its goal is to replace this type of condition:
$var = isset($dict['optional']) ? $dict['optional'] : 'fallback';
By that:
$var = $dict['optional'] ?? 'fallback';
Even better, the null coalesce operators are chainable:
$x = null;
# $y = null; (undefined)
$z = 'fallback';
# PHP 7
echo $x ?? $y ?? $z #=> "fallback"
# PHP 5
echo isset($x) ? $x : (isset($y) ? $y : $z)
The null coalesce operator acts exactly like isset() : the subject variable's value is taken if:
The variable is defined (it exists)
The variable is not null
Just a note for PHP beginners: if you use the ternary condition but you know that the subject variable is necessarily defined (but you want a fallback for falsy values), there's the Elvis operator:
$var = $dict['optional'] ?: 'fallback';
With the Elvis operator, if $dict['optional'] is an invalid offset or $dict is undefined, you'll get a E_NOTICE warning (PHP 5 & 7). That's why, in PHP 5, people are using the hideous isset a ? a : b form when they're not sure about the input.
Sadly no, because the RFC has been declined. And because isset is not a function but a language construct you cannot write your own function for this case.
Note: Because this is a language construct and not a function, it cannot be called using variable functions.
If you only assign null instead of the non set variable, you can use:
$a = #$the->very->long->variable[$index];
# makes that instruction throw no errors
Assuming you know that $the->very->long->variable is set, and you're just worried about the array index....
$x = $the->very->long->variable;
$a = isset($x[$index]) ? $x[$index] : null;
Or for a more generic variant that you can use around you code:
function array_valifset($arr,$k, $default=null) {
return isset($arr[$k]) ? $arr[$k] : $default;
}
then call it like this for any array value:
$a = array_valifset($the->very->long->variable,$index);
I stumbled across the same problem and discovered that referencing an array element does not issue a notice or warning but returns null (at least PHP 5.6).
$foo = ['bar' => 1];
var_dump($bar = &$foo['bar']); // int 1
var_dump($baz = &$foo['baz']); // null
Inside an if statement:
if($bar = &$foo['bar']) {
echo $bar;
}
if($baz = &$foo['baz']) {
echo $baz;
}
In PHP I often write lines like
isset($foo)? NULL : $foo = 'bar'
In ruby there is a brilliant shortcut for that, called or equals
foo ||= 'bar'
Does PHP have such an operator, shortcut or method call? I cannot find one, but I might have missed it.
As of PHP7, you can use the Null Coalesce Operator:
The coalesce, or ??, operator is added, which returns the result of its first operand if it exists and is not NULL, or else its second operand.
So you can write:
$foo = $foo ?? 'bar';
and it will use $foo if it is set and not null or assign "bar" to $foo.
On a sidenote, the example you give with the ternary operator should really read:
$foo = isset($foo) ? $foo : 'bar';
A ternary operation is not a shorthand if/else control structure, but it should be used to select between two expressions depending on a third one, rather than to select two sentences or paths of execution
I really like the ?: operator. Unfortunately, it is not yet implemented on my production environment. So, if I were to make this look ruby-ish, I would go for something like:
isset($foo) || $foo = 'bar';
Or, if you want it even shorter (slower, and may yield unexpected results):
#$foo || $foo = 'bar';
You could create your own function:
function setIfNotSet(&$var, $value) {
if(!isset($var)) {
$var = $value;
}
}
I find it readable, concise and performant to just do:
isset($foo) or $foo = 'bar';
As of PHP 5.3 it's possible to use $foo ?: 'bar'
Unless you expect $foo to be false
[edit]
Forget it. It still raises E_NOTICE if $foo is no set.
From the manual:
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.
It's not exactly the same though. Hope it helps anyway.
No. According to w3schools, that operator doesn't exist.
Also, the PHP code you posted is rather cryptic. I prefer something like this:
if (!isset($foo)) $foo = 'bar';
The most similar with ruby is this:
$foo or $foo = 'bar';
$foo is false if
$foo = 0;
$foo = '0';
$foo = NULL;
$foo = '';
$foo = array();
$foo = FALSE;