What does double question mark (??) operator mean in PHP [duplicate] - php

This question already has answers here:
PHP short-ternary ("Elvis") operator vs null coalescing operator
(15 answers)
Closed 4 years ago.
The community reviewed whether to reopen this question 4 months ago and left it closed:
Original close reason(s) were not resolved
I was diving into Symfony framework (version 4) code and found this piece of code:
$env = $_SERVER['APP_ENV'] ?? 'dev';
I'm not sure what this actually does but I imagine that it expands to something like:
$env = $_SERVER['APP_ENV'] != null ? $_SERVER['APP_ENV'] : 'dev';
Or maybe:
$env = isset($_SERVER['APP_ENV']) ? $_SERVER['APP_ENV'] : 'dev';
Does someone have any precision about the subject?

It's the "null coalescing operator", added in php 7.0. The definition of how it works is:
It returns its first operand if it exists and is not NULL; otherwise it returns its second operand.
So it's actually just isset() in a handy operator.
Those two are equivalent1:
$foo = $bar ?? 'something';
$foo = isset($bar) ? $bar : 'something';
Documentation: http://php.net/manual/en/language.operators.comparison.php#language.operators.comparison.coalesce
In the list of new PHP7 features: http://php.net/manual/en/migration70.new-features.php#migration70.new-features.null-coalesce-op
And original RFC https://wiki.php.net/rfc/isset_ternary
EDIT: As this answer gets a lot of views, little clarification:
1There is a difference: In case of ??, the first expression is evaluated only once, as opposed to ? :, where the expression is first evaluated in the condition section, then the second time in the "answer" section.

$myVar = $someVar ?? 42;
Is equivalent to :
$myVar = isset($someVar) ? $someVar : 42;
For constants, the behaviour is the same when using a constant that already exists :
define("FOO", "bar");
define("BAR", null);
$MyVar = FOO ?? "42";
$MyVar2 = BAR ?? "42";
echo $MyVar . PHP_EOL; // bar
echo $MyVar2 . PHP_EOL; // 42
However, for constants that don't exist, this is different :
$MyVar3 = IDONTEXIST ?? "42"; // Raises a warning
echo $MyVar3 . PHP_EOL; // IDONTEXIST
Warning: Use of undefined constant IDONTEXIST - assumed 'IDONTEXIST' (this will throw an Error in a future version of PHP)
Php will convert the non-existing constant to a string.
You can use constant("ConstantName") that returns the value of the constant or null if the constant doesn't exist, but it will still raise a warning. You can prepended the function with the error control operator # to ignore the warning message :
$myVar = #constant("IDONTEXIST") ?? "42"; // No warning displayed anymore
echo $myVar . PHP_EOL; // 42

$x = $y ?? 'dev'
is short hand for x = y if y is set, otherwise x = 'dev'
There is also
$x = $y =="SOMETHING" ? 10 : 20
meaning if y equals 'SOMETHING' then x = 10, otherwise x = 20

Related

Message: Trying to get property of non-object error in php [duplicate]

Is there in PHP something similar to JavaScript's:
alert(test || 'Hello');
So, when test is undefined or null we'll see Hello, otherwise - we'll see the value of test.
I tried similar syntax in PHP but it doesn't seem to be working right... Also I've got no idea how to google this problem..
thanks
Edit
I should probably add that I wanted to use it inside an array:
$arr = array($one || 'one?', $two || 'two?'); //This is wrong
But indeed, I can use the inline '? :' if statement here as well, thanks.
$arr = array(is_null($one) ? "one?" : $one, is_null($two) ? "two ?" : $two); //OK
you can do echo $test ?: 'hello';
This will echo $test if it is true and 'hello' otherwise.
Note it will throw a notice or strict error if $test is not set but...
This shouldn't be a problem since most servers are set to ignore these errors. Most frameworks have code that triggers these errors.
Edit: This is a classic Ternary Operator, but with the middle part left out. Available since PHP 5.3.
echo $test ? $test : 'hello'; // this is the same
echo $test ?: 'hello'; // as this one
This only checks for the truthiness of the first variable and not if it is undefined, in which case it triggers the E_NOTICE error. For the latter, check the PHP7 answer below (soon hopefully above).
From PHP 7 onwards you can use something called a coalesce operator which does exactly what you want without the E_NOTICE that ?: triggers.
To use it you use ?? which will check if the value on the left is set and not null.
$arr = array($one ?? 'one?', $two ?? 'two?');
See #Yamiko's answer below for a PHP7 solution https://stackoverflow.com/a/29217577/140413
echo (!$test) ? 'hello' : $test;
Or you can be a little more robust and do this
echo isset($test) ? $test : 'hello';
As per the latest version use this for the shorthand
$var = $value ?? "secondvalue";
One-liner. Super readable, works for regular variables, arrays and objects.
// standard variable string
$result = #$var_str ?: "default";
// missing array element
$result = #$var_arr["missing"] ?: "default";
// missing object member
$result = #$var_obj->missing ?: "default";
See it in action: Php Sandbox Demo
I'm very surprised this isn't suggested in the other answers:
echo isset($test) ? $test : 'hello';
From the docs isset($var) will return false if $var doesn't exist or is set to null.
The null coalesce operator from PHP 7 onwards, described by #Yamiko, is a syntax shortcut for the above.
In this case:
echo $test ?? 'hello';
If you want to create an array this way, array_map provides a more concise way to do this (depending on the number of elements in the array):
function defined_map($value, $default) {
return (!isset($value) || is_null($value)) ? $default : $value;
// or return $value ? $default : $value;
}
$values = array($one, $two);
$defaults = array('one', 'two');
$values = array_map('defined_map', $values, $defaults);
Just make sure you know which elements evaluate to false so you can apply the right test.
Since php7.4, you can use the null coalescing assignment, so that you can do
$arr = array($one ??= "one?", $two ??= "two ?");
See the docs here
There may be a better way, but this is the first thing that came to my mind:
echo (!$test) ? "Hello" : $test;
Null is false in PHP, therefore you can use ternary:
alert($test ? $test : 'Hello');
Edit:
This also holds for an empty string, since ternary uses the '===' equality rather than '=='
And empty or null string is false whether using the '===' or '==' operator. I really should test my answers first.
Well, expanding that notation you supplied means you come up with:
if (test) {
alert(test);
} else {
alert('Hello');
}
So it's just a simple if...else construct. In PHP, you can shorten simple if...else constructs as something called a 'ternary expression':
alert($test ? $test : 'Hello');
Obviously there is no equivalent to the JS alert function in PHP, but the construct is the same.
alert((test == null || test == undefined)?'hello':test);
I recently had the very same problem.This is how i solved it:
<?php if (empty($row['test'])) {
echo "Not Provided";}
else {
echo $row['test'];}?></h5></span></span>
</div>
Your value in the database is in variable $test..so if $test row is empty then echo Not Provided

Negated null coalescing operator (double question mark - ??)

I want to do this
if( !$result['success'] ?? false){
... //handle error case
But it doesn't work. Why not?
Workaround is this:
$isSuccess = $result['success'] ?? false;
if((!$isSuccess){
... //handle error case
Is there a better workaround?
Test to reproduce:
<?php
$a = [];
$x = !$a['x'] ?? 'bbb';
echo $x;
2 problems. Throws a notice. And: echos '1'
You can group the expression you're trying to negate.
if (!($result['success'] ?? false)) {
It's an operator precedence issue. The negation is of higher precedence than the null coalesce, so it is evaluated before.
So with the example $x = !$a['x'] ?? 'bbb';
We're saying "if !$a['x'] is null then 'bbb'". Well, $a['x'] is null, since it's undefined, but !$a['x'] isn't null, it is actually true (because !null === true), so the part of the expression after ?? is never evaluated.
You see 1 because that's the string representation of true.
If it was mine, I would write it instead as
if (empty($result['success'])) {
since empty will check for existence and truthiness simultaneously.

PHP - assignment by reference with null coalescing operator [duplicate]

This question already has answers here:
PHP - foreach lose reference with null coalescing operator
(3 answers)
Closed 12 months ago.
I tried to create a variable that contains the reference to another variable if defined, or an other value:
$arr = &$original ?? [];
In this example, everything is okay when $original is already defined. When it isn't, the null coalescing operator seems to be skipped as $arr contains NULL and not the default empty array.
Using a ternary operator would cause a parse error:
$arr = isset($original) ? &$original : [];
The only way to achieve this would be with a if statement which is a longer syntax:
if (isset($original)) {
$arr = &$original;
} else {
$arr = [];
}
Would there be a shorter way to achieve this ?
Why does the null coalescing operator can't help here ? Should it ?
EDIT:
It appears that the assignment by reference will create the variable if it does not exists:
$b = &$a ?? [];
var_dump($a); // without the previous line, triggers a "Notice: Undefined variable: a in ..."
But still, $a is null, and $b should contain an empty array.
You can approach this as
$arr = isset($original) ? $arr = &$original : [];

Why PHP isset and Null coalescing operator is throwing Notice with concatenation operator?

I have read that PHP isset and null coalescing operator used to ignore PHP Notice: Undefined index:
I have seen this post also
PHP ternary operator vs null coalescing operator
But I am getting PHP notice with both of them while using them with string concatenation operator:
<?php
$array = ['a'=>'d'];
$c = $array['c'] ?? '';
$d = isset($array['c']) ? $array['c'] : '';
$val = "sgadjgjsd".$array['c'] ?? ''; // PHP Notice: Undefined index: c in /home/cg/root/986045/main.php on line 6
$val2 = "sgadjgjsd".isset($array['c']) ? $array['c'] : ''; // PHP Notice: Undefined index: c in /home/cg/root/986045/main.php on line 7
?>
EDIT:
I know This can be solved by the following methods
1) assigning to variable like
$val = "sgadjgjsd".$c = $array['c'] ?? '';
2) using #
$val = "sgadjgjsd".#$array['c'] ?? '';
3) adding brackets (and as Karsten suggested )
$val = "sgadjgjsd".($array['c'] ?? '');
But I am looking for the reason behind it.
Every operator has its own 'importance' (operator precedence, as #Karsten-koop pointed out) which dictates in what order they are executed. For example:
echo 10 + 5 * 3; // 25 (10+15), not 45 (15×3)
In this case:
$val = "sgadjgjsd".$array['c'] ?? '';
PHP will do the following steps:
Concatenate (.) the string sgadjgjsd with the value of $array['c'].
$array['c'] does not exist, so a notice is emitted.
The end result (sgadjgjsd) is then run through the null coalescing operator, and since the string is not equal to null, the string is returned (not '').
The end result is assigned to a variable named $val.
So why does 10 + 5 * 3 equal 25? Look up the * and + operators in the table on the linked page. Notice that * is higher up in the list, so it goes first.
For the other example, the concatenation opereator . is (quite a bit) higher up than ??.
Using brackets is the proper solution; they allow you to specify what goes first:
echo (10 + 5) * 3;
$val = "sgadjgjsd".($array['c'] ?? '');
// Does ?? first and returns either the value of $array['c'] or ''
// and only then it does the string concatenation and assignment to $val.
http://php.net/manual/en/language.operators.precedence.php
Side-note: You might recognise this same concept from school because the same thing exists in mathematics (some historical background on why).
This is because you want to concatenate a string with a null in first case and in second case with boolean value
$val = $array['c'] ?? '';
will not gonna throw any error but concatenating total different data type with different one will throw error.

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