PHP operator precedence bug? - php

The result of:
var_dump(null != $a = 15);
var_dump($a);
is:
bool(true)
int(15)
Why is this script not triggering an error?
Since != (not equal operator) has a higher precedence than = (assignment operator), $a should be compared to null first?

The only reason I can find is that the documentation says that this is still legal: http://php.net/manual/en/language.operators.precedence.php#example-129
It seems to be an exception to what is shown in the table above.

This is not about operator precedence but about: operator precedence lists don't tell you the details (really, they never do), e.g. about the bison rules and the resulting pattern matching and stack reducing.
Let's take the statement null != $a = 15;, for simplicity without the var_dump.
This is how the parser "sees" this statement - or: sees that it is a statement.
(I hope this will be rendered with a fix-width font everywhere...)
null != $a = 15 ;
T_VARIABLE
identifier compound_variable T_LNUMBER
namespace_name reference_variable common_scalar
general_constant base_variable scalar
scalar base_variable_with_functions_calls expr_without_variable
expr_without_variable variable = expr
expr T_IS_NOT_EQUAL \______ expr_without_variable _________/
\__________________ expr ____________________________________________/ ;
\_________________ unticked_statement _______________________________________________/
statement
( You can look up the rules at https://github.com/php/php-src/blob/PHP-5.6.15/Zend/zend_language_parser.y )
There's no special rule for the assignment operator in this case; there simply isn't another way for the parser to match the statement, so precedence doesn't apply.

Related

Operator precedence of ?? operator

I am a bit confused about the precedence of PHP's ?? operator.
https://www.php.net/manual/en/language.operators.precedence.php says that it has lower precedence than the / (division) operator. If that is true, $a??1/0 should always give an error, not only if $a is Null, as it first evaluates 1/0. As it doesn't give a "division by zero" error, I assume that the two sides of the ?? operator are evaluated as two different expressions and the second is only evaluated if the first yields Null. While it absolutely makes sense to implement it this way, doesn't this contradict the precedence stated on above page?
The null coalescing ?? operator will evaluate the right part of the expression only if the left part is Null.
This means it will evaluate the left part first in order to know if it is worth evaluating the right part.
See this as an optimisation of the interpreter to prevent executing some code if it does not reach it, and it doesn't look like an operator precedence issue.
Same in an if statement, when if (expression=true), the following elseif will not be evaluated.
Referring in the link you provided in the question, consider the following when you code:
Use of parentheses, even when not strictly necessary, can often
increase readability of the code by making grouping explicit rather
than relying on the implicit operator precedence and associativity.
The example you provide
<?php
//$a=12;
$a=Null;
echo $a??1/0;
will really be parsed as:
<?php
//$a=12;
$a=Null;
echo ($a) ?? (1/0) ;
The extra parentheses will prevent the next developer that works on your code from getting it wrong.

Operator and assignment at the same time in PHP?

In this statement:
1 + $newVar = 200;
$newVar gets created and assigned the value of 200. How does this work? Precedence rules show that the addition takes place first, before the assignment. I can't wrap my head around this. How does the assignment take place if the variable is evaluated with the + operator first?
PHP provides this little nugget. Does this mean these rules apply except when they don't?
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.
In this instance, it would not make sense for PHP to evaluate the arithmetic expression before the assignment, as arithmetic operator returns a value and you can't assign a value to a value. So it seems like PHP breaks its rules a bit in this case and does the assignment first.
Some code to show assignment is taking place first:
$nVar = 0;
echo (1 + $nVar = 200); //201
echo $nVar; //200
If the + occurred first (and somehow was legal and made sense), they would both echo 200, because (1 + $nVar) does not set $nVar to 1, and you would get the result of the assignment.
Check out this example as well:
$nVar = true;
echo (!$nVar = false); // true | 1
echo '<br/>br<br/>';
var_dump($nVar); // false
! has higher precedence and is also right associative, yet assignment still evaluates first.
How does the assignment take place if the variable is evaluated with the + operator first?
The + operator is not necessarily evaluated first and the docs are pretty clear about this
Operator precedence and associativity only determine how expressions are grouped, they do not specify an order of evaluation. PHP does not (in the general case) specify in which order an expression is evaluated and code that assumes a specific order of evaluation should be avoided, because the behavior can change between versions of PHP or depending on the surrounding code.
And testing a few scenarios on a different versions it becomes clear that the assignment operator is evaluated first (as you observed), since evaluating the + operator first would throw a parse error:
(1 + $n) = 200;
PHP Parse error: syntax error, unexpected '=' in php shell code on line 1
If you want to be sure of how this will execute across versions, best practice would be to provide more explicit grouping for your desired results - for example:
1 + ($n = 200);
I'm not entirely sure about this answer. Someone could help me here if knows better.
This file is from PHP source (https://github.com/php/php-src/blob/eb39d8d6863ab1f327417b274fed7a41b58daf1e/Zend/zend_language_parser.y)
If you take a look, this is the point where the interpreter evaluates the expression.
At the first time, it will fetch the number 1, then + sign, that evaluates to expr '+' expr (link).
Next time, the second expr is evaluated as variable '=' expr ($newVar = 200) (link).
By solving that, internally, will do
expr(1) + expr($newVar = 200)
expr(1) + expr($newVar(200))
201
If you do echo (1.00 + $newVar = 200); it will output 201, but $newVar got 200;

Difference between "&&" and "and" : Operator precedence and short circuiting

I was going through operator precedence section of php.net and came across this example which says
$a = 1;
$b = null;
$c = isset($a) && isset($b);
$d = ( isset($a) and isset($b) );
$e = isset($a) and isset($b);
var_dump($a, $b, $c, $d, $e);
//Result:
int(1)
NULL
bool(false)
bool(false) <== I get this
bool(true) <== I do not get this
I use quite a lot of debugging and verbose print(_r) statements in my code to keep track of where I am in the code. So I use $debug and print_r($dataArray) or $verbose and print "Updating dataArray\n" as separate statements in between the code, allowing me to control these print(_r) statements. This comes from my BASH experience where I used to write lot of [[ $condition ]] && { #Do if true } || { #Do if false }. In BASH, I knew they are short circuited and used this fact to write lot of simple one liners.
Now I am observing that lot of this practice(of writing $verbose and print) is slowly creeping into my if statements. I am aware this is NOT a recommended practice and can bite me in the back. However, I do want to master this skill as I enjoy writing such one liners and want to use it as my personal style.
So my question(s) is(are) :
Which operator (&& or and) is short circuited ?
The manual says && takes precedence over and, but can someone exemplify this by mixing the short circuited operator feature/functionality/characteristic with operator precedence. (basically mix and match of precedence and short-circuiting)
Kindly elaborate on both the short-circuiting as well as return value nature of the operators.
PS: 1. I hope associativity of these operators is same and intuitive, but if you know any quirks, please enlighten me.
PS: 2. If you still feel like warning me against the perils of such practice, kindly include examples.
EDIT : After changing my simple $var = mysql_(...) or die() code by replacing or with ||, I discovered how annoying it can be to use the || and && operators instead of and and or. The code simply didn't work ! To my understanding, the former construct assigns a return value of TRUE or FALSE to $var, which in turn make all sequential use of $var to generate warning/error/unexpected behavior. The latter construct assigns result of mysql_(...) to $var first and then evaluates the compound of = and die.
This is a good lesson for me, I better 1. Start using PDO/mysqli and handle errors on my own 2. Think twice before writing something I called above as personal style.
//note to self : don't use experience of one scripting/interpretive language while writing code in another, each one is unique and has its own quirks and pitfalls, and thats just sad *sigh*
The code
$e = isset($a) and isset($b);
is parsed the same as
($e = isset($a)) and isset($b);
Therefore $e, as determined by isset($a) and the assignment, is true - independent of evaluating isset($b).

Usage of or operator in php?

The following code:
$result = (false or true);
echo("With extra parentheses: ".($result?"true":"false"));
$result = false or true;
echo("<br />With no parentheses: ".($result?"true":"false"));
generates the output:
With extra parentheses: true
With no parentheses: false
I do not understand why. Shouldn't php evaluate $result = false or true; by first testing false and then, since it isn't true, going on to evaluate true?
Any suggestions would be much appreciated.
The or operator has a weaker precedence than the assignment operator. What really happens in the second case is ($result = false) or true, so the part or true really has no effect.
The assignment operator yields the assigned value as its result, false in this case. Think of the assignment operator as an ordinary binary operator that yields a result (like +, < and or), with the only difference that it has a side effect.
If you want to avoid the parentheses, you can swap or for ||, which has a stronger precedence.
Always be careful when using the English versions of the logical operators, because their precedence is different.

Difference between "not equal" operators <> and != in PHP

In PHP, is there any difference between the != and <> operators?
In the manual, it states:
$a != $b Not equal TRUE if $a is not equal to $b after type juggling.
$a <> $b Not equal TRUE if $a is not equal to $b after type juggling.
I guess there are no huge differences but I'm curious.
In the main Zend implementation there is not any difference. You can get it from the Flex description of the PHP language scanner:
<ST_IN_SCRIPTING>"!="|"<>" {
return T_IS_NOT_EQUAL;
}
Where T_IS_NOT_EQUAL is the generated token. So the Bison parser does not distinguish between <> and != tokens and treats them equally:
%nonassoc T_IS_EQUAL T_IS_NOT_EQUAL T_IS_IDENTICAL T_IS_NOT_IDENTICAL
%nonassoc '<' T_IS_SMALLER_OR_EQUAL '>' T_IS_GREATER_OR_EQUAL
They are the same. However there are also !== and === operators which test for exact equality, defined by value and type.
<> means either bigger or smaller. != means not equal. They basically mean the same thing.
As everyone is saying they are identical, one from one language branch C-style/shell, one from some others including MySQL which was highly integrated in the past.
<> should be considered syntactic sugar, a synonym for != which is the proper PHP style for not-equal.
Further emphasised by the triple character identity function !==.
The operators <> and != are the same.
However, as a matter of style, I prefer to use <> when dealing with numerical variables.
That is, if:
$a is an integer
$b is an integer
instead of asking:
// if $a is not equal to $b
if ($a != $b)
I will ask:
// if $a is either less than or greater than $b
if ($a <> $b)
This is a visual hint / reminder in my code that $a and $b are definitely both intended to be numerical rather than one or both being intentionally strings.

Categories