Operator and assignment at the same time in PHP? - 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;

Related

Why do functions execute in PHP if statements?

So the syntax for if statements is:
if(condition){
//code to execute
}
Then why do functions when placed in place of conditions work in PHP.
In PHP (and many languages) there is no specific concept of a "condition"; the actual definition of an if statement is:
if(expression){
//code to execute
}
An "expression" is simply anything that can be evaluated and results in a value. A single value, like 42 is an expression on its own, as is a simple sum like 1 + 1. A function call like strlen('hello') is also an expression, evaluating to the result of the function; the function has to be run, which may have side effects, to determine that result. Expressions can be arbitrarily complex by linking then with operators, like strlen('hello') * 2 + 1.
Commonly in an if statement, you'd have something like $foo === $bar - this is just an expression that uses the === operator to give a boolean result, either true or false. PHP will evaluate that expression, and then decide whether to run the conditional code based on the result. The expression can be as simple or complex as you want - if(true) is valid, though not often useful, and so is if((strlen('hello') * 2 + 1) > 10).
If the result of the expression is not a boolean, PHP "coerces" it into one, as described on the manual page about the boolean type. For instance strlen($foo) evaluates to an integer, and all integers other than zero coerce to true, so if(strlen($foo)) acts like if(strlen($foo) !== 0).
As well as function calls, there are other expressions which have side effects. For instance, an assignment can also be used as an expression, evaluating to the value assigned. This lets you do things like $foo = $bar = 0; where $foo is assigned the result of running $bar = 0; which is of course 0. It also lets you put assignments inside if statements, like if ( $result = getData() ) { ... }, which is shorthand for $result = getData(); if ( $result ) { ... } This technique should be used with care, though, because at a glance it can be hard to spot the difference between = (assignment) and == (weak comparison).
The values returned in a PHP if condition are not restricted to be "strictly" Boolean, however the condition is expected to be Boolean. Why? Because all PHP variables types (inbuilt or user-defined) can be implicitly type-casted (converted automatically) to Boolean. According to the PHP manual:
To explicitly convert a value to bool, use the (bool) or (boolean) casts. However, in most cases the cast is unnecessary, since a value will be automatically converted if an operator, function or control structure requires a bool argument.
The PHP manual also explicitly specifies the falsy values for the different variable types including user defined types with all other values not specified being truthy:
the following values are considered false:
the boolean false itself
the integer 0 (zero)
the floats 0.0 and -0.0 (zero)
the empty string, and the string "0"
an array with zero elements
the special type NULL (including unset variables)
SimpleXML objects created from attributeless empty elements, i.e. elements which have neither children nor attributes.
Every other value is considered true (including any resource and NAN).
Therefore, to explicitly answer your question:
why do functions when placed in place of conditions work in PHP?
One reason I know of, is so you can conveniently perform assignments in the conditions:
function inverse_power($base, $exp)
{
if($power = pow($base, $exp)) {
return 1/$power;
}
else {
return "logical error: you can't divide by zero";
}
}
echo inverse_power(2,1); // 0.5
echo inverse_power(0,1); // logical error: you can't divide by zero
From the above example, you see the feature saves me multiple lines of code. Note that $power is not explicitly Boolean, but will be automatically converted to Boolean only to test the condition. The actual value of $power still persists throughout the function.
Because a condition is a boolean and functions can return booleans. And for conditions to work, the condition itself has to be evaluated, so it can be a function that is executed also.

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.

Why =! seems a legit condition in php?

I just experienced a sort of bug while coding in Php:
I was writing some condition like this:
if($showPrice != 0){
$worksheet->mergeCells('A'.$rowCount.':F'.$rowCount);
$worksheet->SetCellValue('A'.$rowCount, 'Total Weight');
$worksheet->SetCellValue('G'.$rowCount, $totalWeight);
}else{
$worksheet->mergeCells('A'.$rowCount.':D'.$rowCount);
$worksheet->SetCellValue('A'.$rowCount, 'Total Weight');
$worksheet->SetCellValue('E'.$rowCount, $totalWeight);
}
I noticed that instead of writing != as I should, I was wrongly writing =! without getting any error.
What could be the reason of this?
You didn't find any bug in PHP. You found a bug in your program and in your knowledge about the PHP.
$x =! 0 is, in fact, $x = !0.
! is the logical NOT operator.
0 is evaluated as FALSE in boolean context, consequently !0 is TRUE.
$x = ... is a regular assignment. As any assignment, it does two things:
stores the value of the right-hand side expression into the left-hand side variable; here the expression is !0 that, as explained above, is evaluated to TRUE;
the value of the entire expression ($x = !0) is the value of $x after the assignment (TRUE as described).
When the assignment it is used as a statement, its value is discarded. When it is used as a condition (in a for, while, if, switch statement), its value is used to control the execution of the code.
Here, if($showPrice =! 0) always takes the if branch (and never the else branch) because the value of the $showPrice = !0 expression is always TRUE as explained above.
More, the value of $showPrice also becomes TRUE after this statement is evaluated.
Two bugs in a single line of code.
From the PHP's point of view the code is perfectly valid and, even if it is unusual, maybe it represents the programmer's intention. The interpreter doesn't have any reason to complain about it. It can be, however, detected and flagged as a possible error by static code analysis tools and some PHP IDEs.

Is =! a declaration and is that why this alternating conditional works?

I have a terrible confession. I've been using variations of the below for ages.
for($x=0;10>$x;++$x){
echo '<li style="color:#'.(($c=!$c)? "fff":"eee").'">example $x</li>";
}
My dilemma is that I don't entirely understand how it works. I know that $c=!$c makes the ternary conditional alternate, but I don't understand how. Googling "php =!" and the likes yields nothing helpful (no surprise given the query).
!= is an operator, but this uses =!
Is this a ternary conditional declaration? If so, how does this one work? I understand the general conditional declarations, but not this one... assuming it even is one.
Any answer or link to documentation would be greatly appreciated.
They are two separate operators. You should read it as
$c = !$c;
In other words, assign the result of the expression !$c back to the variable $c. So indeed, it toggles the value of $c.
Additionally, an assignment like this also returns the assigned value, so you can evaluate it immediately. So in words, the expression ($c=!$c)? "fff":"eee" says:
"Invert the value of $c. If the new value is true, return 'fff', otherwise return 'eee'."
It's not an operator, it just needs better spacing:
$x = !$x;
it's just inverting the value.
=! is not an operator. It is two: = means assignment, and ! means negation.
If $c is true, then !$c is false (and vice versa).
And assignments evaluate to their new value.
You're setting $c to be not itself. So if $c == true you are saying $c = not true, I.e. false which will also count as false for the ternary.
Next iteration you set $c = not false I.e. true and so on.

Can someone explain this line of code please? (Logic & Assignment operators)

I've seen the following lines of code and I know what they do, but I don't know how the second line works (and hence how to apply it to another situation).
$user = User::model()->findByPk(123);
empty($user->profile) and $user->profile = new Profile();
The code tries to look up the User from the database, and if there isn't a profile, creates a new for use later on.
I have also seen code before that goes something like the following:
$variable1 = $variable2 = $variable3;
It did something a bit more complex than simple assigning three things to be the same, but I'm finding it impossible to search for this type of thing to find out any information about it, let alone find the original code that I came across. I think it originally had an 'and' in there somewhere. Does anyone know how to search for code that has more than one equals sign in it that wasn't just an if statement?
Sorry for the two questions in one (and vague at that) and the terrible title (I'll fix it up when I know what the names are, if it's anything like a tenary statement)).
Using Logical Operators to Skip Code:
As php evaluates a line with the AND operator, if the fist part is false, the second part is not evaluated since it would not change the result.
So in this case, if empty() returns true, then php evaluates the right side. If empty() returns false, no more evaluation is done and the profile is not effected.
The php manual logical operators page has some illustrations of this.
Multiple Assignment Operators: The assignment operator assigns the right expression to the variable on the left.
$variable1 = $variable2 = $variable3;
In this case $variable2 is set to the value of $variable3 then $variable1 is set to the value of $variable2. The php manual assignment operators page covers this.
empty($user->profile) and $user->profile = new Profile();
in and statement, first element is evaluated first; if it's true, then second statement is evaluated, simple assignment should always evaluate to true, I presume. if the first element was false, second element is not evaluated.
You can read more about operator precedence in php docs.
It's basically the same as
if (empty($user->profile))
$user->profile = new Profile();
Weird syntax indeed...
What you're seeing is used in many languages. Here is an article for using it in JavaScript... but it explains the concept well.
http://css.dzone.com/articles/guard-and-default-operators-ja
This is a fairly bizarre way to write this. With a PHP expression, if the first part evaluates false, the expression will stop rendering.
Like if I write:
if (x == 5 and y == 2)
that will test if x==5, then if it does, it will test if y==2. If x != 5, it will never test the y == 2. So the code above is using that fact to test whether $user->profile is empty. Then if it is, it runs the next part of the expression, which is the assignment of $user->profile = new Profile(); Basically the same as:
if (empty($user->profile))
$user->profile = new Profile();
or
empty($user->profile) ? $user->profile = new Profile();
As far as your second question, the operator = is just an assignment, so it means that the value of $variable1 will be set to the value of $variable2, which will be set to the value of $variable3.
PHP does some very nice things with expressions, which are really helpful to learn. Check out this link for more info:
http://us.php.net/manual/en/language.expressions.php
$variable1 = $variable2 = $variable3;
Assignment (via the equal sign) in PHP has right to left precedence. Every expression in PHP has a return value. So the return value of the expression $variable3 is assigned to $variable2. Then the reurn value of the expression $variable2 = $variable3 is assigned to $variable1.

Categories