Right associativity of PHP's null coalesce operator - php

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';

Related

Nesting null coalescing operator [duplicate]

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';

What is the shortest way

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.

Getting literal expression value with OR operators instead of true or false in PHP [duplicate]

This question already has answers here:
Quickest PHP equivalent of javascript `var a = var1||var2||var3;` expression
(3 answers)
Closed 3 years ago.
In Javascript, we can conveniently get one of the various options that is offered in || operator. For example:
console.log('a' || '' || 0); // Yields 'a'
console.log(0 || 'b' || 'a'); // Yields 'b'
Those results above can easily be assigned to a variable like this:
let test = 'a' || '' || 0;
In PHP, though, when I try this:
$test = 'a' || '' || 0; gives me a $test = 1, with 1 meaning true. Is there a way in PHP to get the literal value of the expression which caused it to yield true?
You can use the Elvis operator for this purpose, e.g.
$test = 'a' ?: '' ?: 0;
var_dump($test);
> string(1) "a"
$test2 = 0 ?: 'b' ?: 'a';
var_dump($test2);
> string(1) "b"
There is also null coalescing operator (??) but it takes the second value only if the first is null, so e.g. 0 ?? 'a' will take 0 because it's not null.
Per some user-contributed comments on PHP's logical operators page, you can use the ternary operator:
$test = $a ? $a : ($b ? $b : 'default')
Or if you're running PHP 7+ and you only need to pass over null variables (rather than falsy variables) you can use the null-coalescing operator ??:
$test = $a ?? $b ?? 'default'
PHP's boolean operators always return a boolean value... as opposed
to other languages that return the value of the last evaluated
expression.
For example:
$a = 0 || 'avacado';
print "A: $a\n";
will print:
A: 1
in PHP -- as opposed to printing "A: avacado" as it would in a language like Perl or JavaScript.
This means you can't use the '||' operator to set a default value:
$a = $fruit || 'apple';
instead, you have to use the '?:' ternary operator:
$a = ($fruit ? $fruit : 'apple');
if you're using PHP 7+ then you can user null-coalescing operator ??: too.
<?php
// 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';
?>
For more information, you can refer to this link.
For null coalescing operator use this
link
.

Php Null coalescing operator

I am trying to understand how the null coalescing operator does really work. So, I tested many examples after reading the documentation in php.net and some posts on stackoverflow.
However, I can't understand this code:
<?php
$x = false ?? 'stackoverflow';
var_dump($x); // bool(false)
since it's equivalent to (from php.net#null-coalescing)
isset(false) ? false : 'stackoverflow';
and since isset(false) generates a fatal error.
Could, please, someone explain to me?
Null coalescing operator returns its first operand if it exists and is not NULL;
Otherwise it returns its second operand.
In your case first operand is false so it is assigned to the variable. For e.g; if you initialize null to first operand then it will assign second operands value as shown.
$a = null;
$x = $a ?? 'abc';
var_dump($x);
Result :
string(3) "abc"

Assign if variable is set

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;
}

Categories