PHP OR surprising behavior [duplicate] - php

This question already has answers here:
The behaviour of the or operator in PHP
(4 answers)
Closed 8 years ago.
I have some surprising results using OR as a logical OR in php.
Considering the following code:
$a = false;
$b = false;
$c = true;
# Test 1, using OR and assigning to a variable
$value = $a OR $b OR $c;
var_dump( $value );
# return bool(false) but why?
# Test 2, using OR directly in var_dump
var_dump( $a OR $b OR $c );
# return bool(true) as expected
# Test 3, using || and assigning to a variable
$value = $a || $b || $c;
var_dump( $value );
# return bool(true) as expected
# Test 4, using || directly in var_dump
var_dump( $a || $b || $c );
# return bool(true) as expected
Why Test 1 and Test 2 give different results even though they do the same logical operation?

The || operator and OR operator do not behave the same. They cannot be used interchangably.
If you want || behaviour, then use it. Do not use OR unless you're in a situation where || would do the wrong thing.
As for your situation, these two lines of code will behave exactly the same:
$value = $a OR $b OR $c;
($value = $a) OR $b OR $c;
In other words, your code is basically just:
$value = $a;
If you used the || operator, then these two are identical as if you had braces like this:
$value = $a || $b || $c;
$value = ($a || $b || $c);
For more details: http://php.net/manual/en/language.operators.precedence.php

If you wrap test 1 in parenthesis, it will behave as expected:
$value = ($a OR $b OR $c);
When you run var_dump on test 2, you get the expected result because var_dump is wrapping the operation in parenthesis.
It is usually a good idea to wrap an operation in parenthesis like this, especially with variable assignment.
Also, the "OR" keyword and "||" do not behave the same way. See documentation here:
http://php.net/manual/en/language.operators.logical.php

Related

How to avoid undefined index warning for chains of 'default' values?

Let's say I want to check for a couple of different parameters, and then fall back to a default value, is there a way to do it without ugly and verbose writing of isset()?
For example in JS we can do:
var someVariable = otherVar || anotherVar || 'fallback here';
The equivalent in PHP would be something like:
$someVariable = (isset($otherVar) ? $otherVar : (isset($anotherVar) ? $anotherVar : 'fallback here'));
which is obviously a mess and horrible to read.
Lots of solutions exist for single fallbacks, i.e.:
$someVariable = $otherVar ?: 'fallback here';
but that doesn't help me with requiring more than one in the line of checks.
Given that I am only interested in whether or not the value is set or truth-y (i.e. I am happy for 1 to be accepted as the used value, and for 0/false/null to be skipped and for the next parameter in the chain to be used), what is the best way to avoid the undefined index warning?
In reality, I would be doing this on arrays in most cases, but not all, and it's probably that they will be different arrays. I may want to use $_POST for the first, then check $_GET under a different key, and then fall back to a default string for example.
Ignoring PHP warnings and notices is not a good idea at all.
But just for the experiment, I can suggest you the error control operator #.
PHP supports one error control operator: the at sign (#). When prepended to an expression in PHP, any error messages that might be generated by that expression will be ignored.
$someVariable = #$otherVar ? #$var : 'fallback here';
echo $someVariable;
// output: fallback here
http://php.net/manual/en/language.operators.errorcontrol.php
You can chain ?: but you'll still need to suppress errors with #.
For example, the following will work similarly to your JavaScript example:
$foo = #$bar ?: #$baz ?: #$qux ?: "I give up";
It's the equivalent of multiple "single fallbacks":
$foo = (#$bar ?: (#$baz ?: (#$qux ?: "I give up")));
Be careful though, ?: only checks for truthyness. There are scenarios where isset() and truthyness do not agree. Additionally, ?: fallback functionality was introduced in PHP 5.3.
If you only care about nullness and have PHP 7 available, it introduced a null coalescing operator.
This is ambiguous and it is deprecated...
$something = $a ? $b : $c ?: $d;
So, you have 2 alternatives for grouping:
$something = $a ? $b : ($c ?: $d); // version 1
$something = ($a ? $b : $c) ?: $d; // version 2
Which one is correct? Let's test this...
$posibilities = [TRUE, FALSE, NULL, [], new stdClass()];
$v1_is_correct = TRUE;
$v2_is_correct = TRUE;
foreach ($posibilities as $a) {
foreach ($posibilities as $b) {
foreach ($posibilities as $c) {
foreach ($posibilities as $d) {
$original = (int)($a ? $b : $c ?: $d);
$v1 = (int)($a ? $b : ($c ?: $d));
$v2 = (int)(($a ? $b : $c) ?: $d);
$v1_is_correct = $v1_is_correct && ($original === $v1);
$v2_is_correct = $v2_is_correct && ($original === $v2);
print "ORIG: $original - V1: $v1 - V2: $v2 \n";
}
}
}
}
print "\n";
$v1_is_correct ? print "V1 is correct \n" : NULL;
$v2_is_correct ? print "V2 is correct \n" : NULL;
Result: V2 is correct.
$a ? $b : $c ?: $d
is equivalent to...
($a ? $b : $c) ?: $d

Shorthand expression for an if ( $a == $b and $a == $c ) statement in PHP

Recently, I posted a question about if ( $a == $b or $a == $c ) expression. Now I want to know if there's also a shorthand expression for this code:
if ( $a == $b && $a == $c ) { /*statement*/ }
That code can be read like "if $a is equal to $b and $a is equal to $c".
Is there code that's more shorter that can be read like:
if ( $a is equal to ( $b and $c ) ) { /*statement*/ }
Because you are testing if $a is equal to $b and $c this logically results in $b being equal to $c, I don't know how much shorter this would be but if you wanted to quickly test if all prepossed values are the same you could create an array of them and call array_unique. If the length of the resulting array is 1 then they are all equal to each other. I imagine this would only be easier if you had a large set of values, as the syntax might obscure your intent.
I found an answer upon experimenting while my question is not yet posted:
if ( $a == ( $b & $c ) ) { /*statement*/ }
I just want to share it.. (And no, I'm developing a WordPress theme and this is unintentional!)

Shorthand expression for an if ( $a == $b || $a == $c ) statement

I know this code will work:
echo ( $a == $b || $a == $c ) ? "Yes" : "No";
That can be read like:
if $a is equal to $b or $a is equal to $c
Is there a way to make it more shorter like:
if $a is equal to $b or $c
I have tried a lot including this but still no luck:
echo ( $a == ( $b xor $c ) ) ? "Yes" : "No";
You can use in_array:
var_dump(in_array($a, [$b, $c]));
with your example:
echo in_array($a, [$b, $c]) ? 'Yes' : 'No';
Note: this syntax is only useful if you have more than 2 values. For few values $a == $b || $a == $c does the job well and is probably faster.
These are two alternatives, but they will both take longer to execute than the code you posted because they rely on more complex functions.
preg_match('/^('.$b.'|'.$c.')$/',$a) === 0
in_array($a,array($b,$c)) === true
If you put the condition more likely to be true as the first expression, in most cases, PHP will evaluate the expression as true and not test the second expression.

Fallthrough variable assignment in PHP? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Coalesce function for PHP?
I'm not sure what this is normally called, but I hope the title communicates well enough. What I have is a handful of variables some of which might be null.
I want to do:
$a = $b || $c || $d;
Where $a ends up being = to the first non-null variable.
To my knowledge, PHP doesn't support this in the same way JavaScript does.
You can, however do something like this:
$a = $b ? $b : ($c ? $c : $d);
A more general solution:
function fallthrough($arr) {
//$arr should be an array of possible values. The first non-null value is returned
do $a = array_shift($arr);
while($a === null && $arr);
return $a;
}
<?php
$a = 0;
$b = false;
$c = true; //should become this
$d = '1';
$e = $a ?: $b ?: $c ?: $d;
var_dump($e);
//bool(true)
//should be '1' if order is different
$e = $a ?: $b ?: $d ?: $c;
var_dump($e);
//string(1) "1"
... however ?: is kinda new, you will confuse your colleagues / fellow coders.
I don't think that's possible. I think you'd have to use some other, more laborious, way. I.e. make an array of the variables, iterate through it until you find a non-null value and break the loop, like so:
$vars = array("b" => $b, "c" => $c, "d" => $d);
foreach($vars as $var) {
if($var != null) {
$a = $var;
break;
}
}
Well, like some other answers here say, you can use the shorthand way of writing this, but writing readable code is important too. The above code is pretty readable.

Operator precedence issue in Perl and PHP

PHP:
$a = 2;
$b = 3;
if($b=1 && $a=5)
{
$a++;
$b++;
}
echo $a.'-'.$b;
$a = 2;
$b = 3;
if($a=5 and $b=1)
{
$a++;
$b++;
}
echo $a.'-'.$b;
Output 6-16-2.I don't understand the 1 here.
Perl :
$a = 2;
$b = 3;
if($b=1 && $a=5)
{
$a++;
$b++;
}
print $a.'-'.$b;
$a = 2;
$b = 3;
if($a=5 and $b=1)
{
$a++;
$b++;
}
print $a.'-'.$b;
Output 6-66-2, I don't understand the second 6 here.
Anyone knows the reason?
Actually I know && has higher precedence than and,but I still has the doubt when knowing this before hand.
UPDATE
Now I understand the PHP one,what about the Perl one?
Regarding Perl:
Unlike PHP (but like Python, JavaScript, etc.) the boolean operators don't return a boolean value but the value that made the expression true (or the last value) determines the final result of the expression† (source).
$b=1 && $a=5
is evaluated as
$b = (1 && $a=5) // same as in PHP
which is the same as $b = (1 && 5) (assignment "returns" the assigned value) and assigns 5 to $b.
The bottom line is: The operator precedence is the same in Perl and PHP (at least in this case), but they differ in what value is returned by the boolean operators.
FWIW, PHP's operator precedence can be found here.
What's more interesting (at least this was new to me) is that PHP does not perform type conversion for the increment/decrement operators.
So if $b is true, then $b++ leaves the value as true, while e.g. $b += 1 assigns 2 to $b.
†: What I mean with this is that it returns the first (leftmost) value which
evaluates to false in case of &&
evaluates to true in case of ||
or the last value of the expression.
First example
$a = 2;
$b = 3;
if($b=1 && $a=5) // means $b = (1 && $a=5)
{
var_dump($b); //bool(true) because of &&
$a++;
$b++; //bool(true)++ ==true, ok
}
echo $a.'-'.$b;
hope you will not use those codes in production)
I'm noob in perl but i can suggest a&&b returns a or b (last of them if all of them converted to bool), not boolean, then $b = (1 && $a=5) returns $b=5 (is 5)
here's the issue: 1 && 5 returns 5 in perl. you get the result you expect if you code the conditional as if(($b=1) && ($a=5))
For Perl, fig. 2: and has a very low priority in perl, it's not a synonym of &&'s. Therefore the sample is executed as (($a = 5) and ($b = 1)) which sets $a and $b to 5 and 1 respectively and returns a value of the last argument (i.e. 1).
After ++'s you get 6-2.
refer to http://sillythingsthatmatter.in/PHP/operators.php for good examples

Categories