Understanding operator precedence in php - php

I have the following code in production that appears to be causing an infinite loop.
$z=1;
while (!$apns = $this->getApns($streamContext) && $z < 11)
{
myerror_log("unable to conncect to apple. sleep for 2 seconds and try again");
$z++;
sleep(2);
}
How are the precedence rules getting applied that cause this behavior?
http://php.net/manual/en/language.operators.precedence.php
I see this note in the docs:
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.
Which makes me think the the = should be evaluated first. then the ! then the &&, which would not cause an infinite loop.

Your code is evaluating like this:
while (!($apns = ($this->getApns($streamContext) && ($z < 11))))
which is why you see the infinite loop (as soon as $z >= 11, $apns is false, so the condition is always true). The reason for this precedence is that the special rules only apply to ! on the left of the assignment being valid (having lower precedence than =). It has no effect on the boolean operator on the right, which behaves as it would in any sane language.
Your style is bad. Try this, which is much more readable and only differs in the final value of $z (and if that's important you can tweak the break statement.
for( $z = 1; $z < 11; ++ $z ) {
// note extra brackets to make it clear that we intend to do assignment not comparison
if( ($apns = $this->getApns($streamContext)) ) {
break;
}
myerror_log("unable to conncect to apple. sleep for 2 seconds and try again");
sleep(2);
}

Your code is clear example of why it's good habit to always put all the conditions in brackets (and the same applies to code block. Even oneliners should be surrounded by { and }). So instead of error-prone:
while (!$apns = $this->getApns($streamContext) && $z < 11)
do
while (!($apns = $this->getApns($streamContext)) && ($z < 11))
and you will be safe.

Related

PHP loop increment quirk - (FizzBuzz in one line)

I am learning PHP. I decided to adapt a solution to the famous FizzBuzz problem from Javascript to PHP, just to see how JS and PHP compare.
For those who forgot what the FizzBuzz problem is :
Write a short program that prints each number from 1 to 100 on a new
line. For each multiple of 3, print "Fizz" instead of the number.
For each multiple of 5, print "Buzz" instead of the number. For
numbers which are multiples of both 3 and 5, print "FizzBuzz" instead
of the number.
I am using a lot of short-circuit evaluations in the following examples.
Here's the clever solution (not written by me) that I adapted to PHP:
Works great!
Then, for the sake of challenge, I decided to try and rewrite it in one single line.
Here's how I started:
Seems like a good start. My condition in the while loop: if $i is not set set, then I set it to zero, but if it is already set, I skip the first part and check if it's inferior to 100.
As you can see on this picture, my loop works.
Since my goal is to write it in one line, I need to increment $i inside my conditional statement, just as with my previous multi-line solution. But when I write $i++ < 100 as before, something weird happens. My loop only runs once and stops.
Very weird indeed.
Even weirder, if I use both increments (one in the condition and one in the loop), the loop then works fine, and applies both increments.
I'm puzzled. Is it because there is a safeguard for infinite loops somewhere? With all those short-circuit evaluations, even my IDE PHP Storm says 'variable $i is probably undefined'. But it gets defined, and my loop works fine under certain conditions. What did I miss ?
EDIT:
Here's the code:
Multi-line working FizzBuzz:
$i = 0;
while ($i++ < 100) {
$msg = '';
($i % 3) || ($msg = $msg . 'Fizz');
($i % 5) || ($msg = $msg . 'Buzz');
echo $msg . "\n";
}
Weird loop iteration (you can delete the increment either in the loop or in the condition, or leave both to see the different effects):
while ( (!isset($i) && ($i=0 || true) ) || ($i++ < 100) ) {
echo $i . "\n";
$i = $i +1;
}
if $i is not set set, then I set it to zero
This is not quite right. Here's what's going on.
Your statement ($i=0 || true) sets $i to TRUE.
PHP's type juggling with print "1" for "$i". Consider $v = TRUE; echo "$v"; to see this in effect.
On the next iteration, your second condition is evaluated as TRUE < 100 which evaluates to FALSE, thereby exiting the loop.
So, in order to fix your problem, simply drop the || true and be on your merry way.
$i=0 || true results in $i being true.
true++ doesn’t actually do anything.
true < 100 is false.
echo true outputs 1.
An explicit true + 1 creates 2.
OMG, Yes! I thought I needed the || true part because ( !isset($i) && ( $i = 0 ) ) will either do both sides of the &&, or neither. I never thought that ( $i = 0 ) would evaluate to "true". But it looks like it does :)
OBSOLETE COMMENT: I found the origin of the quirk. Not sure why it happens though.
If I rewrite $i++ as ($i = $i + 1), it works fine.
while ( (!isset($i) && ($i=0 || true) ) || (($i = $i + 1) < 100) ) {
echo $i . "\n";
}

PHP: Use the short if-statement without else?

I'm a fan if the short if-version, example:
($thisVar == $thatVar ? doThis() : doThat());
I'd like to cut out the else-statement though, example:
($thisVar == $thatVar ? doThis());
However, it wont work. Is there any way to do it that I'm missing out?
You can't use it without the else. But you can try this:
($thisVar != $thatVar ?: doThis());
or
if ($thisVar == $thatVar) doThis();
The ternary operator is designed to yield one of two values. It's an expression, not a statement, and you shouldn't use it as a shorter alternative to if/else.
There is no way to leave out the : part: what value would the expression evaluate to if you did?
If you're calling methods with side effects, use if/else. Don't take short cuts. Readability is more important than saving a few characters.
hmm interesting, because executing the below code is valid. Observe:
for ($i = 1; $i <=10; $i++) {
if ($i % 2) {
echo $i;
}
}
The above code indeed, will output 13579
Notice no 'else' clause was used in the above.
If you wanted to inform the user of whether $i % 2 == FALSE ($i's divisor yielded remainder 0), you could include an else clause to print out the even numbers like shown below:
for ($i = 1; $i <=10; $i++) {
if ($i % 2) {
echo "$i is odd";
echo "<br />";
} else {
echo "$i is even";
echo "<br />";
}
}
Giving you the output:
1 is odd
2 is even
3 is odd
4 is even
5 is odd
6 is even
7 is odd
8 is even
9 is odd
10 is even
I hope my amazingly easy to understand examples will help all newcomers to PHP, hands down the 'best' server-side scripting language for building dynamic web applications :-)
USE NULL TO SKIP STATEMENTS WHEN IT IS IN SHORTHAND
$a == $b? $a = 10 : NULL;
Just use logical operators : AND, OR, &&, ||, etc.
($thisVar === $thatVar) && doThis();
a frequent use is :
$obj = doSomething($params) or throw new \Exception('Failed to do');
Working for me:
$leftHand != $rightHand?doThis():null;
$leftHand == $rightHand?null:doThis();

PHP while loop with 'or' condition always stops at higher value

I have a while loop with 2 conditions separated by 'or':
while (($i <= 8) || ($x <= 5)){
$i++;
$x++;
}
I want the loop to end when the lowest number is reached - in the above case 5. However, the above stops when the first, higher condition is met and it loops 8 times.
This still occurs when I swap the values, eg:
while (($i <= 5) || ($x <= 8))
The loop will still ignore <= 5 and loop through 8 times.
Would anyone know how I can fix this to have the loop cycle stop at the lower number?
It's doing what you have written. Use && instead of || and it will stop as soon as one of the conditions is no longer true.
The loop will execute as long as the expression inside while(expression) is true. In this case, your expression is $i <= 8 || $ x<=5 wich translates into:
$i is less or equal than 8 OR $i is less or queal than 5.
From php documentation:
$a || $b is TRUE if either $a or $b is TRUE.
If you want the smallest number, then you want both conditions to be true to continue the loop, so you use AND, which documentation is:
$a && $b is TRUE if both $a and $b are TRUE
So your code should be:
while (($i <= 8) && ($x <= 5)){
$i++;
$x++;
}
You can also use the keywords andand or, but be careful, because they have different operator precedence, in particular, they have less precedence:
http://php.net/manual/en/language.operators.precedence.php
Use and instead of or
while (($i <= 8) && ($x <= 5)){
}
If you use or it needs only one of its condition to be true for foing inside the loop. And will ensure that both of the condition should be true. So it will exit the loop when any one of the condition becomes false
You should use 'and' operation.
while($x<5 && $y<8) {//Your code here}
I suggest reading about this logic operators, these operators are basic in development.

Laziest way to check number constraints

This question is born out of pure laziness and the desire to do more with less code.
Say I have a variable $x which needs to be greater than 0 and less than 12, what is the fastest (least amount of code written) way to check. Is there a faster way than this.
if($x < 0 || $x > 12) {
die("invalid x value");
}
It would be nice (and I think some languages have it) to do this:
if(0 > $x > 12) {
die("invalid x value");
}
Very curious to see if there is some PHP magic I am missing out on.
You can use filter_var as a native PHP function : http://de2.php.net/manual/en/function.filter-var.php But I don't find it any better, as you will need to pass an array with min and max range, which is not fast, nor short.
Maybe a user-defined function for this will fit? Yes, you will need to write the code once, but only once.
function between($value, $from, $to) {
if ($value < $from || $value > $to) {
return false;
}
return true;
}
The function return false, if the value is less than the min bound, or greater than the max bound. Otherwise it returns true. So if you need to stop you script, if the value is NOT between, you would need to ask for the false response if(!between...
So you only call it this way:
$x = 14;
if(!between($x, 0, 12)) {
die("invalid x value");
}
Output:
invalid x value
if, for example your $x is 5 and you want to check if it's between, and if it is - to continue the script, you ask for the true response.
if(between($x, 0, 12)

change a variable based on even/odd status of another variable?

for($i=0;$i<$num;$i++) {
if($i==even) $hilite="hilite";
dothing($i,$hilite);
}
This is basically what I want to accomplish.
What is the most efficient way to determine if $i is even?
I know I could check if half == mod 2 ... but that seems a little excessive on the calculations? Is there a simpler way?
if ($i % 2 == 0)
The already mentioned % 2 syntax is most used, and most readable for other programmers. If you really want to avoid an 'overhead' of calculations:
for($i = 0, $even = true; $i < $num; $i++, $even =! $even) {
if($even) $hilite = "hilite";
dothing($i,$hilite);
}
Although the assignment itself is probably more work then the '%2' (which is inherently just a bit-shift).
It doesn't get any simpler than $i % 2 == 0. Period.
Change the i++ in the loop statement to i+=2, so that you only examine even values of i?
Typically, a number is odd if it's LSB (Least Significant Bit) is set. You can check the state of this bit by using the bitwise AND operator:
if($testvar & 1){
// $testvar is odd
}else{
// $testvar is even
}
In your code above, a more efficient way would be to have $i increment by 2 in every loop (assuming you can ignore odd-values):
for($i=0;$i<$num;$i+=2){
// $i will always be even!
}

Categories