I've been programming in php for about 2 years now.
I just stumbled into this for loop:
// Check the URI namespace for a context
$wsDir = basename(dirname(__FILE__));
$uriArr = explode("/", $_SERVER['REQUEST_URI']);
for (
$i = 0, $uriSize = sizeof($uriArr);
$i < $uriSize && $uriArr[$i] != $wsDir && $i++;
);
$i++;
self::$executionContext = isset($uriArr[$i]) && !empty($uriArr[$i]) && substr($uriArr[$i], 0, 1) != '?'
? strtoupper($uriArr[$i]) : 'SOAP';
and I have no idea how this is supposed to work.
Can someone explain this to me ?
It is just a normal three-part for loop without its main statement and an empty third part.
From the manual:
for (expr1; expr2; expr3)
statement
The first expression (expr1) is evaluated (executed) once unconditionally at the beginning of the loop.
In the beginning of each iteration, expr2 is evaluated. If it evaluates to TRUE, the loop continues and the nested statement(s) are executed. If it evaluates to FALSE, the execution of the loop ends.
At the end of each iteration, expr3 is evaluated (executed).
So:
for (
# initializes two variables
$i = 0, $uriSize = sizeof($uriArr);
# conditional, expr2
$i < $uriSize && $uriArr[$i] != $wsDir && $i++;
# no expr3
);
If the expr2 evaluates to true the loop continues. Of course there is no statement or block to execute, so it just jumps to the next iterarion, meaning expr2 will be executed repeatedly until it evaluates to false at some point.
As pointed out by R. Chappell in the comments, this is probably to find a position in a string. You could rewrite this with a similar logic but in a more "descriptive" way:
$uriSize = sizeof($uriArr)
for ($i = 0; $i < $uriSize; $i++) {
if ($uriArr[$i] == $wsDir) break;
}
# now $i will be the index of the first $wsDir occurrence in the $uriArr array
Coming late, but none seems to have cached this : this for loop is equivalent to :
$i = 1;
Why ? Because in the condition part of the for loop, you have 3 conditions that are bound with AND:
$i < $uriSize
&&
$uriArr[$i] != $wsDir
&&
$i++;
In the first iteration, $i++ evaluates to 0 which is equivalent to false, and is incremented only after. So the loop stops after only one iteration, and $i is 1, and you have a bug. Unless it's a typo in your code...
This is another example (not an answer as such) of using a for without a third statement. It's a little clearer than the original question.
for ($i=0; $i >= $i++ && $i <= 10; /* third statement */ );
echo $i;
This will basically count to 10 and echo it out, and it's only made possible with the increment operator in PHP.
First we set $i to zero;
Second we check and increment $i to ensure it's equal to or greater than itself whilst less than or equal to 10.
Third we do nothing... no point really...
However, normal people would write the same thing as:
for ($i = 0; $i <= 10; $i++);
echo $i;
You'll have to imagine a better use case though and yes you can just do $i = 10; but it doesn't go as far as to explaining the question.
Related
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";
}
I am trying to get rooms that are not written already to a residential apartment short: RA. So as long array_shift is dragging the rooms out of the array, the loop should count further and it have to check allRARooms if there are still some rooms left for me.
So is that ok to check for isset in a for condition?
for($i = 1; count($ra) <= $quantity && isset($this->allRARooms); $i++)
Yes you can set for loop terminating condition to whatever expression you like.
You can even skip it ! for example:
for ($i=0;;$i++) {
if ($i>10) break;
echo "$i\n";
}
Or maybe you want an everlasting loop with for? here it is :
for (;;) echo ++$x . "\n";
In essence you can skip whatever part in for loop you need
Condition optimization
Your condition count($ra) <= $quantity && isset($this->allRARooms) can be factored a bit.
In 99% cases it's enough of count($ra) <= $quantity && $this->allRARooms, because '',null,[] - all evaluates to false.
Further you should put allRARooms var check at first place, like:
$this->allRARooms && count($ra) <= $quantity. In that way you will employ a short-circuit evaluation trick for boosting condition check speed, because if var allRARooms is not set - count($ra) will not be evaluated - thus saving CPU ticks
Yes it is.
A for loop can be described as such : for (initialisation; alive condition; last loop statement)
it can be translated using a while loop that way :
initialisation
while (alive condition)
{
// some code
last loop statement
}
You put what you want as long as it respects the differents statements
for($i = 1; count($ra) <= $quantity && isset($this->allRARooms); $i++) { }
is equivalent to
$i = 1;
while (count($ra) <= $quantity && isset($this->allRARooms))
{
// some code
$i++;
}
You can add many initialisation and last instruction statements and the alive condition can be independant of them.
$aConditionIndependantOfInit = true;
for ($i = 0, $j = 42; $aConditionIndependantOfInit; $i++, $j--)
{
echo "foo\n";
if ($i >= $j)
$aConditionIndependantOfInit = false;
}
This output 21 foo
I'm trying to loop through the first 5 items of an array that contain a specific value.
The code below causes an infinite loop.
$i = 0;
while ($i < 5):
if ($counselor[$i]->state == $state || !$state):
// do stuff
$i++;
endif;
endwhile;
Essentially I want to end the loop after the if statement has run 5 times.
While others have explained why your solution currently does not work, and some ways around it, the best alternative is to loop the entire array until you find 5 matches - by using a foreach-loop instead.
By using an foreach-loop, you will never run into issues if the array has less than 5 matching elements (if it has less than 5 matching elements, it will never break).
$i = 0;
// Loop the array
foreach ($counselor as $k=>$v) {
// Check if there is a match
if ($v->state == $state || !$state) {
// Do whatever if a match here
$i++;
}
// If we have found 5 matches, break out of the loop!
if ($i == 5) {
break;
}
}
You can now check how big $i is, and if less than 5, you found less than 5 matches. If it's exactly 5, you found your matches, and ended the loop.
If the if statement is not met, then you will only stay checking the $counselor[0]->state.
You need a separate counter for when the if statement is met.
$i = 0;
$containsCount = 0;
while ($containsCount < 5 || !isset($counselor[$i])):
if ($counselor[$i]->state == $state || !$state):
// do stuff
++$containsCount;
endif;
++$i;
endwhile;
I've also added a bounds check by checking if the $counselor[$i] is null. (could also check $i < $arrayLength)
If your if statement is false the first time ($i = 0) it will never match...since $i always will be 0. The same goes for $i = 1, 2, 3 or 4. If any of those are false, the loop will be stuck. $i will never increase. You need another solution.
You increment $i only when the if statement returns true. If you take your $i++; out of the if statement the loop will always run exactly 5 times.
$i = 0;
while($i < 5):
if($counselor[$i]->state == $state || !$state):
// do stuff
endif;
$i++;
endwhile;
You have to exit $i++ of your if condition :
$i = 0;
while($i < 5):
if($counselor[$i]->state == $state || !$state):
// do stuff
endif;
$i++;
endwhile;
if the if() condition is not met 5 times, it will be infinite as the while() condition will never be met.
Instead of while statement, use a loop that matches the length of the array and it will end then when it reaches the end of the array.
Maybe you can try altering your if statement a bit like this :
$i = 0;
while($i < 5):
if($counselor[$i++]->state == $state || !$state):
// do stuff
endif;
endwhile;
I know this below statement works
for ($i= 0; $i=<10; $i++)
will output till 0 to 10
but when I write this code
for ($i=0; $i=10; $i++)
it print 10 for unlimited times... why why it not print 0 to 10...
what error I have done to get the result 0 to 10 for it....
The middle term in a for loop is the condition that says whether the loop should continue running. i=10 assigns 10 to i, and it also evaluates to the number 10, which is not zero, so it's considered true. Since the loop's condition is always true, it never stops running.
because i=10 always be true i==10 will be good
The second statement in the for loop is the condition. When its not true it will leave the loop. i=10 is not a comparator. i will be set to 10 every round that loop goes and because it "works" its resulting in true. i == 10 would be a comparator but i would never be 10 in its first round.
if $i< or =10 Will perform,if $i> 10 will break,each time $i will +1 you can set the $i begin to value or set the maximum to achieve the result that you want.
" = " sign is an assignment operator and can't be used as conditional where as >= , == , <= such operators are conditional, so while checking for a condition you should not use " = "
please write like this below
for($i=0; $i <= 10; $i++)
{
echo $i;
}
Why doesn't the double equals work in a PHP for loop? The following works:
$cnt = 5;
for ($z = 0; $z <= $cnt; $z++) {
echo $z."!<br />";
}
But why doesn't this work?
$cnt = 5;
for ($z = 0; $z == $cnt; $z++) {
echo $z."!<br />";
}
The loop executes only if the condition evaluates to true. At the first iteration, $z == $cnt is false, so the loop never executes.
A common loop strategy is to use a sentinel value:
$cnt = 5;
$stop = $cnt + 1;
for ($z = 0; $z != $stop; $z++) {
. . .
}
Note that the comparison is negative (!= or !==). Using a sentinel is usually unnecessary for numerical loop variables (I wouldn't recommend it for your posted code), but is useful for other situations (e.g., when $stop represents null, an illegal value, etc.). It's particularly helpful when the loop variable changes in a pattern that is not easy to characterize succinctly.
because on the first iteration $z != $cnt, so the loop stops immediately.
Let us look at this from the computer's perspective:
If I am a computer this is what you told me to do:
set $cnt = 5;
set $z = 0;
check if $z equals $cnt
if so do whatever is in the loop, then increment $z
Trouble is, 5 does not equal 0 and never will, so the loop will simply be skipped. If you had $cnt = $z+1 inside the loop this would be an infinite loop.
So, you see, == works just fine, it simply doesn't do what you think it should do.
Hope this helps!
A for loop works by looping until a condition is made false. In the first example the loop will execute until z = 5 after that z will not longer be less than or equal to cnt (z starts at 0 and increments through each loop). In the second example you have $z == $cnt as your condition, and since z = 0 and cnt = 5 the loop will stop automatically because the condition is made false. You can use not equals instead like following:
$cnt = 6;
for ($z = 0; $z != $cnt; $z++) {
echo $z."!<br />";
}
The syntax of for is:
for (<init>; <condition>; <increment>)
The loop tests <condition> before each iteration. If it's true, it executes that iteration; if it's false, the loop terminates.
In your case, since $z == $cnt is false before the first iteration, the loop terminates immediately.
To do what you want, invert the test. You also need to bump the end value up by one, since the original version used <=, not <. Note that in both cases, the loop executes $cnt+1 times, because you start from 0.
for ($z = 0; $z != $cnt+1; $z++)