Operator Precedence && and? - php

Am I reading https://www.php.net/manual/en/language.operators.precedence.php that && binds tighter than ?? ?
Why the heck would a construct that's designed to provide a "default value" of an optional array element not bind very tightly to that element?
$ok = $ok && $someArray['optionalElement'] ?? true; // Wrong
$ok = $ok && ( $someArray['optionalElement'] ?? true ); // Right

From the PHP Docs
https://www.php.net/manual/en/migration70.new-features.php#migration70.new-features.null-coalesce-op
The null coalescing operator (??) has been added as syntactic sugar for the common case of needing to use a ternary in conjunction with isset(). It returns its first operand if it exists and is not NULL; otherwise it returns its second operand.
From this, the null coalescor treats everything to it's left as an expression argument to isset()
So $ok && $someArray['optionalElement'] is taken as a full expression.
Wrapping ( $someArray['optionalElement'] ?? true ) in parentheses causes only $someArray['optionalElement'] to be the expression and so it works as you would expect.

Related

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 if null or false return empty array by default?

Why this line of code does not work in php like as in JS:
$id = [];
$id = null || [];
if (count($id)) {
echo 'd';
}
Why $id still is null instead empty array []? Therefore count() gives an error.
In PHP, logical operators like || always return a boolean, even if given a non-boolean output.
So your statement is evaluated as "is either null or [] truthy?" Since both null and an empty array evaluate to false, the result is boolean false.
There are however two operators which would do something similar to JS's ||:
$a ?: $b is short-hand for $a ? $a : $b; in other words, it evaluates to $a if it's "truthy", or $b if not (this is documented along with the ternary operator for which it is a short-hand)
$a ?? $b is similar, but checks for null rather than "truthiness"; it's equivalent to isset($a) ? $a : $b (this is called the null-coalescing operator)
<?php
// PHP < 7
$id = isset($id) ? $id : [];
// PHP >= 7
$id = $id ?? [];
As of PHP 7 and above
Null Coalesce Operator
Another helpful link
Watch out!
I find PHP's handling of empty to be highly questionable:
empty($unsetVar) == true (fabulous, no need to check isset as warning is suppressed!)
empty(null) == true
empty('') == true
All fine, until we get to this nonsense:
empty(false) == true. Wait, WHAT???
You've GOT to be kidding me. In no world should false be taken to mean the same thing as no value at all! There's nothing "empty" about a false assertion. And due to this logical fallacy, you cannot use empty to check ANY variable that might have a VALUE of false.
In my projects, I use a static method:
public static function hasValue($value) {
return isset($value) && !is_null($value) && $value !== '';
}
Of course, using this method, I no longer get the free warning suppression provided by empty, so now I'm also forced to remember to call the method above with the notice/warning suppression operator #:
if(self::hasValue(#$possiblyUnsetVar)) {}
Very frustrating.

PHP Ternary Operators don't work as expected? [duplicate]

This question already has answers here:
Stacking Multiple Ternary Operators in PHP
(11 answers)
Closed 4 years ago.
I have a method that I'm checking if param is null, but if I use a ternary operator to make sure the false result isn't a string, I don't get the same expected result... I am a full stack .NET dev by day, but do some PHP free lance and this just stumped me...
$param = null;
// $active evaluates to true
$active = is_null($param) ? true : false;
// $active evaluates to false
$active = is_null($param) ? true : is_string($param)
? (strtolower($param) === 'true')
: true;
I have used nested ternary operators in C# and JavaScript what feels like countless times, but I don't know if I have ever tried in PHP... does PHP attempt to evaluate all nested ternary operations prior to expressing a result or is there something I'm missing here since from my understanding in this case, the ternary operator should be short circuited and evaluated to true in both circumstances.
The ternary operator is left associative unlike most other languages such as C#. The code:
$active = is_null($param)
? true
: is_string($param)
? (strtolower($param) === 'true')
: true;
is evaluated as follows:
$active = ((is_null($param) ? true : is_string($param))
? (strtolower($param) === 'true') : true);
You must explicitly add parenthesis to make sure ?: works the way it does in familiar languages:
$active = is_null($param)
? true
: (is_string($param)
? (strtolower($param) === 'true')
: true);
You need to wrap your second ternary condition with parenthesis (),
<?php
$param = null;
// $active evaluates to true
$active = is_null($param) ? true : false;
echo "Simple ternary result = $active".PHP_EOL;
// $active evaluates to true
$active = is_null($param) ? true : (is_string($param)? (strtolower($param) === 'true'): true);
echo "Nested ternary result = $active";
?>
Note:
It is recommended that you avoid "stacking" ternary expressions. PHP's
behaviour when using more than one ternary operator within a single
statement is non-obvious:
See Example #4 here at http://php.net/manual/en/language.operators.comparison.php
Example #4 Non-obvious Ternary Behaviour
<?php
// on first glance, the following appears to output 'true'
echo (true?'true':false?'t':'f');
// however, the actual output of the above is 't'
// this is because ternary expressions are evaluated from left to right
// the following is a more obvious version of the same code as above
echo ((true ? 'true' : false) ? 't' : 'f');
// here, you can see that the first expression is evaluated to 'true', which
// in turn evaluates to (bool)true, thus returning the true branch of the
// second ternary expression.
?>
DEMO: https://3v4l.org/gW8pk
This is a well-known problem with PHP. I doubt it will ever be fixed. Use parentheses, or if..else or switch statements, to get the behaviour you want.
(In technical terms, the ternary operator in PHP is "left associative", while that in every other language with this operator is "right associative". The latter is the more logical behaviour for this operator.)

Can't replicate condition with ternary operator

I'm trying to replicate this condition:
$sth = $this->getResult();
if($sth !== true){
return $sth;
}
with ternary operator I tried with;
($sth !== true) ? return $sth : ($sth == true) ? null;
But I got:
Expected colon
What you're ultimately trying to achieve is not possible with a ternary operator.
What you try to do is ONLY return in 1 situation, and continue the code in the other. The only way you could do this using a ternary operator is like this:
$result = ($sth !== true) ? true : false;
if ($result) return;
But that kinda defeats the purpose of the ternary operator.
In fact, your code has a couple of problems:
Ternary operator always needs 3 parts (*)
There is no need to check a condition twice, as that's the entire point of the operator
The operator returns a value, so you can't put a return inside the truthy or falsy part.
A ternary operator needs 3 parts
(condition) ? truthy result : falsy result
Note: 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. - source: php.net
No need to check things twice:
Again, let's take our operator:
($sth !== true)
? "I return if '$sth' is not true"
: "I return if '$sth' is true";
There is no need to have a 2nd check. You have both situations covered already. :)
No return values inside the truthy or falsy part
Finally: it is an operator that returns a value. You can't put a return in the truthy or falsy part, you need to put it in front of it:
return ($sth !== true)
? "I return if '$sth' is not true"
: "I return if '$sth' is true";
You have two '?'s in your code, and only one ':'. I believe that's the matter. You must have the same amount of question marks as you have colons.
The ternary operator is nice and useful, but it's not a block of commands. It is a decision on a value. Rule of thumb, if you can assign something as value to a variable you can use it within the ternary operator :
$var = 3; // valid
($condition?3:0); // valid
$var = return 3; //Invalid
($condition?return 3:null) //Invalid
You need to:
return ($sth !== true?$sth:null);
If you are using it to return from a function I would do it like so:
function testFunc()
{
//arbitrary setting of $sth only to explain the point
$sth = false;
//condition ? true : false
$outcome = ($sth === true) ? true : false;
return $outcome;
}
// false
var_dump(testFunc());
Remember, (condition) ? value if true : value if false;
You can also miss out the [value if true] parameter, like so:
$outcome = ($sth === true) ?: false;
That expression would assign the value of $sth to $outcome if $sth was true.
Your replacement code indeed misses a colon at the very end. It is syntactically incorrect.
If $sth !== true you return $sth. If not, you start a new ternary statement (that is not complete) which I cannot properly fathom. It now does not look at all like the original statement.

Newer style PHP ternary operator has undesired result with isset function

I am having issue with PHP's ternary operator, since PHP version 5.3 you are able to replace the shorthand ternary operator with an even shorter version
// Older version
$route = isset($test) ? $test : 'test is NOT set';
// Newer version as of 5.3
$route = isset($test) ?: 'test is NOT set';
Now on the newer version, if $test is not set. it works fine. However when it is set because of the isset() method, it is returning true or 1 instead of the value.
Do I have to use the older longer method to get $route to equal the value of $test instead of a boolean value of 1 ?
You have to use the longer version.
Quoting the docs:
Expression expr1 ?: expr3 returns expr1 if expr1 evaluates to TRUE, and expr3 otherwise.
So the correct behaviour for your shorthand version is to return the result of evaluating isset($test). Not of $test as you want.
Starting from PHP 7, you can use the null coalescing operator:
$route = $test ?? 'test is NOT set';
which is equivalent to
$route = isset($test) ? $test : 'test is NOT set';
Here you can find some details:
The null coalescing operator (??) has been added as syntactic sugar for the common case of needing to use a ternary in conjunction with isset(). It returns its first operand if it exists and is not NULL; otherwise it returns its second operand.

Categories