"Undefined variable" when used after assignment in an if-clause - php

if (true && 1 == $x = 1 && $x) {
echo "Hello world!";
}
I get an Undefined Variable Notice for $x. Shouldn't the parser know $x at this point, because it solves the If-Clause from left to right?

The observed behavior is due to how the expression is parsed. The following
if (1 == $x = 1 && $x) {
echo "Hello world!";
}
results in an "Undefined variable: x" and does not echo, but
if (1 == ($x = 1) && $x) {
echo "Hello world!";
}
"works" and emits Hello world! as expected.
This is because the former is equivalent to
if (1 == ($x = (1 && $x))) {
echo "Hello world!";
}
and the use of $x is inside the expression that will be used for the assignment. Since this is evaluated before the assignment takes effect, $x is still an "undefined variable" according to PHP.
Solutions:
Use parenthesis as shown; or,
Avoid variable assignments in conditional expressions, which I recommend.
Although operator precedence is the primary scapegoat, the first form is not equivalent to (1 == $x) = (1 && $x) which is the derivation when directly following the precedence table. If it were parsed in such a manner it would result in a "syntax error, = unexpected".
This parsing is a quirk of PHP and differs from C grammar rules. (The YACC rules for PHP can be found in zend_language_parser.y.)
PHP: 1 && $x = 2 -> 1 && ($x = 2), as per the above stated equivalency. This happens even though && is stated to have a higher precedence.
C: 1 && x = 2 -> (1 && x) = 2, invalid syntax: "lvalue required as left operand of assignment". In C, the && operator really does have higher precedence in an expression.
This same behavior is evident when replacing && with ==: despite an assignment having a lower documented priority, the simple precedence table is misleading and inaccurate when assignments are part of expressions.
In conclusion, $x = expr as part of an expression is parsed equivalently to ($x = (expr)) where expr can itself be a complex expression containing operators and other expressions.

Related

logical vs assignment operator precedence in PHP

Recently i came upon such snippet:
$x = 2 && $y = 3; echo (int)$x.':'.(int)$y;
Which produces output 1:3.
By looking at operator precedence sheet i see that logical operators || and && has higher precedence than assignment operator =. So first expression should be evaluated as $x = (2 && $y) = 3; which becomes $x = (2 && null) = 3; and finally evaluates to $x = false = 3; Secondly - assignment operator has right associativity, so interpreter should try to execute false = 3 which is illegal of course. So in my opinion above mentioned code snippet should not compile at all and must throw parse or run-time error. But instead of that script produces 1:3. Which means that interpreter executed actions are:
a) $y=3
b) 2 && $y
c) $x = (2 && $y)
Why it is so and not according to operator precedence ?
The operator precedence sheet you link to states as a separate note:
Although = has a lower precedence than most other operators, PHP will
still allow expressions similar to the following: if (!$a = foo()), in
which case the return value of foo() is put into $a.
So, in effect, an assignment inside an expression will be treated somewhat like a sub-expression. Exactly how and when this will happen isn't clear from the documentation, which just states that "similar" expressions will work this way.

PHP "or" syntax statement

Moving over to PHP from another language and still getting used to the syntax...
What's the proper way to write this statement? The manual on logical operators leaves something to be desired..
if($var !== '5283180' or '1234567')
Generally, comparison is by using == and the reverse is !=. But if you want to compare values along with its data type, then you can use === and the reverse is !==.
Please refer to the documentation for more information.
You can use the following:
if($var!='5283180' || $var!='1234567')
Try this
if($var != '5283180' || $var != '1234567')
PHP's or functions identically to the normal ||, but has a lower binding precedence. As such, these two statements:
$foo = ($bar != 'baz') or 'qux';
$foo = ($bar != 'baz') || 'qux';
might appear to be otherwise identical, but the order of execution is actually quite
different. For the or version, it's executed as:
($foo = ($bar != 'baz')) or 'qux';
- inequality test is performed
- result of the test is assigned to $foo
- result of the test is ORed with the string 'qux';
For the || version:
$foo = (($bar != 'baz') || 'qux');
- inquality test is performed
- result of test is ||'d with 'qux'
- result of the || is assigned to $foo.
To build on the others, as the OP mentioned they are new to PHP, there is a couple things to be considered.
First off, the PHP or that you're looking for is the double line (||) and each item must be a statement on each side of the ||.
if ( $var !== '5283180' || $var !== '1234567')
addition:
As mentioned in the PHP Manual
The or operator is the same as the || operator but takes a much lower precedence.
Such as the given example (from manual):
// The constant false is assigned to $f and then true is ignored
//Acts like: (($f = false) or true)
$f = false or true;
Now as mentioned, there is the general comparison (== or 'Equal') and the type comparison (=== or 'Identical'), with both having the reverse (not). In the given example, the test will check that $var is not identical to the values.
From PHP Manual:
$a !== $b | Not identical | TRUE if $a is not equal to $b, or they are not of the same type.
With this said, double check that this is what you're actually trying to accomplish. Most likely you're looking for !=.

What is the difference between the OR and || operator in PHP? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Logical Operators, || or OR?
I've always thought that OR is another way of writing the || operator in PHP. The only way I prefer using OR over || is that it makes the code easier to read since || can be confused with II or 11 or whatever...
One day I stumbled upon this thing though:
<?php
$a = 'string_b';
$active = ($a == 'string_a') OR
($a == 'string_b') OR
($a == 'string_c');
var_dump($active); // Prints FALSE;
?>
Why is this happening?
The only difference is operator priority, see Operator precedence. || has a higher priority than OR.
By the way, var_dump($a) returns null but prints the right thing, string_b.
But, var_dump($active) will indeed produce an unexpected result, false.
In fact, = has higher priority than or, so your code is equivalent to:
($active = ($a == 'string_a')) OR ($a == 'string_b') OR ($a == 'string_c');
It first assigns false to active, then execute the right part of the first OR.
It's the same. But || has higher precedence than OR
http://php.net/manual/en/language.operators.precedence.php
= has a higher precedence than OR. So, $active = ($a == 'string_a') is evaluated first, which is false. Enclose the entire right hand side in it's own set of brackets and you'll get the result you were expecting.
<?php
$a = 'string_b';
$active = (
($a == 'string_a') OR
($a == 'string_b') OR
($a == 'string_c')
);
var_dump($active); // Prints TRUE;
?>

PHP Logical Operators precedence affects variable assignment results strangely

$var4 = 123;
function fn1($p1)
{
return array('p1' => 1, 'p2' => 2);
}
if ($var1 = fn1(1) AND $var4 == 123)
{
print_r($var1);
}
if ($var2 = fn1(1) && $var4 == 123)
{
print_r($var2);
}
if (($var3 = fn1(1)) && $var4 == 123)
{
print_r($var3);
}
If you run this simple script it will output strange results, at
least for me!! First output from first if expression will result in
an array returned from the function & assigned to the $var1
variable, which is what I'm expecting, well?
Second output from second if expression will result in an integer
'1' assigned to the $var2 variable, which is NOT expected at all!!
Please note that the only changed thing is the logical operator,
I've used '&&' rather than 'AND', that's all!!
Third output from third if expression will result again the expected
array returned from the function & assigned to the $var3 variable,
exactly as the first if expression, but wait: I've just embraced the
assignment statement in the if expression within brackets, while
still using the second if expression code!!
Can anyone explain technically -in details- why this strange behavior? php.net reference links will be appreciated.
I know that '&&' has higher precedence than 'AND' but that doesn't explains it to me!!
PHP: Operator Precendence
&& has a higher precedence than =, so in the second if, you are assigning the value of fn1(1) && $var4 == 123 (true or false) to $var2.
In the first if, AND has a lower precedence than =, so the assignment happens first, then the result is compared.
In the third if, the assignment happens first again because everything in parens gets processed first.
&& has a higher precedence than =, so what's really happening is something more like:
if ($var1 = (fn(1) && $var4 == 123))
So what is really being assigned to $var1 is the boolean result, which is why you get 1.
PHP's AND and && operators both are logical ands, but the and version has a lower binding precedence, see: http://php.net/manual/en/language.operators.precedence.php

The strange ways of the "or" in PHP

PHP's or is an weird keyword. Here it is in a code snippet that makes me confused:
echo 0 or 1; // prints 1
$foo = (0 or 1);
echo $foo; // prints 1
$foo = 0 or 1;
echo $foo; // prints 0 for some reason
Why does the last one print 0 and not 1?
This is because of different operator precedence. In the third case, the assignment is handled first. It will be interpreted like this:
($foo = 0) or 1;
The || operator has a different precedence. If you use
$foo = 0 ||1;
It will work as you expect.
See the manual on logical operators
No, I wouldn't, that's because of operator precedence:
$foo = 0 or 1;
// is same as
($foo = 0) or 1;
// because or has lower precedence than =
$foo = 0 || 1;
// is same as
$foo = (0 || 1);
// because || has higher precedence than =
// where is this useful? here:
$result = mysql_query() or die(mysql_error());
// displays error on failed mysql_query.
// I don't like it, but it's okay for debugging whilst development.
It's ($foo = 0) or 1;. or has a lower operator precedence than = .
You should use || in this case, since it has a higher precedence than =, and thus will evaluate as you'd expect.
IIRC, the assignment operator (=) has higher precedence than or. Thus, the last line would be interpreted as:
($foo = 0) or 1;
Which is a statement that assigns 0 to $foo, but returns 1. The fist statement is interpreted as:
echo(0 or 1);
An as such will print 1.
Order of operations. The word "or" has much lower precedence than the corresponding "||". Lower, even, than the assignment operator. So the assignment happens first, and the value of the assignment is the first operand to the "or".
"or" is meant more to be used for flow control than for logical operations. It lets you say something like
$x = get_something() or die("Couldn't do it!");
if get_something is coded to return false or 0 on failure.
In the first two snippets, you are comparing 0 or 1 (essentially true or false). In the third snippet you are assigning 0, which works, and thus is true, so therefore the or condition is not executed.emphasized text
In your third example, the = operator has a higher precedence than or, and thus gets done first. The || operator, superficially the same, has a higher precedence than =. As you say, interesting.

Categories