PHP "continue" stops foreach loop - php

This seems like a very stupid question, but without making any changes to the server.. the continue function in PHP seems to have started working incorrectly.
For example:
function contTest(){
$testers = array(1, 3, 4, 5);
foreach($testers as $test){
echo "Got here<br>";
continue;
echo $test."<br>";
}
}
Outputs:
Got here
Got here
Got here
Got here
Whereas:
function contTest(){
$testers = array(1, 3, 4, 5);
foreach($testers as $test){
echo "Got here<br>";
echo $test."<br>";
}
}
Ouputs:
Got here
1
Got here
3
Got here
4
Got here
5
I have used this function before and it did not seem to have this effect. Any ideas? Like I said, nothing on the server has changed so the PHP version is the same.

I don’t know what effect you want, but this example is working correctly. continue needs to break current iteration and go to the next without executing code below this operator. And this function works in this case all the time since PHP 4.

I think you need to understand how continue works. i wanted to add some, so that if some body else confronts the same, may have this as reference.
You need to use the key word continue When you want ignore the next iteration of a loop. continue is always used with if condition
According to example here .
function contTest(){
$testers = array(1, 3, 4, 5);
foreach($testers as $test){
echo "Got here<br>";
**continue;**
echo $test."<br>";
}
}
This works as expected and perfect and here is why.
You are looping through the array $testers which has four elements inside and
after getting every element , you are telling php to ignore the elements by using
continue and that is the reason why it will not output the elements of the array
$testers.
Let me try to rewrite your Example here.
function contTest(){
$testers = array(1, 3, 4, 5);
foreach($testers as $test){
echo "Got here<br>";
if ($test == 1):
continue;
endif;
echo $test."<br>";
}
}
echo contTest();
I have just used continue if element is equal to 1, meaning that element will be
skipped (ignored).
The output is :
Got here
Got here
3
Got here
4
Got here
5
As you can see 1 is ignored.

The continue basically says ignore the rest of the code after continue and start with the next step of the foreach loop. So the result you are getting is perfectly okay (see http://www.php.net/manual/de/control-structures.continue.php). There must be some other effect that changed your output.

This is what continue does exactly:
continue is used within looping structures to skip the rest of the current loop iteration and continue execution at the condition evaluation and then the beginning of the next iteration.
-- http://www.php.net/manual/en/control-structures.continue.php

Related

Continue to the next loop in a foreachloop with another nested foreachloop

I have 2 Foreach-Loops. One of them is nested inside the other, e.g.:
foreach(...){
foreach(...){
//if statement
}
}
Within the inner loop I got an if statement and if this statement returns true I want to break the inner loop and continue with the next outter loop. Is there any way to realize this? "break 2; " won't work as I need to continue with the outter loop.
Like with break, you can add a number to continue as well:
foreach(...) {
foreach(...) {
if (...)
continue 2;
}
// this will be skipped
}
From the docs:
continue accepts an optional numeric argument which tells it how many
levels of enclosing loops it should skip to the end of. The default
value is 1, thus skipping to the end of the current loop.
Per PHP documentation, the default is 1, which only breaks out of the immediately encapsulating control struct
break ends execution of the current for, foreach, while, do-while or
switch structure.
break accepts an optional numeric argument which tells it how many
nested enclosing structures are to be broken out of. The default value
is 1, only the immediate enclosing structure is broken out of.
For example, the below code:
<?php
foreach(array(1,2,3,4,5) as $num){
foreach(array('a', 'b') as $char){
if($char === 'b') {
break;
}
echo $char;
}
echo $num;
}
// Result: a1a2a3a4a5
It breaks before it prints 'b', but continues with the loop to 5.

Break first loop and continue next loop if true

I have a foreach loop inside another foreach loop as this code:
foreach($array1 as $uid => $somevalues) {
$status = true;
foreach($somevalues as $somevalue)
if(!isset($somevalue->$someothervalue))
$status = false;
if($status) {
$content .= "some content added";
$count++;
}
}
How it works
As you see I am looping through all entries in $array1. For each entry I loop through the contained $somevalues array to look for a certain condition (that $someothervalue exists there). Now, if this condition (that something isset) is not true then the $status changes to false.
Only if the $status is still true after all loops of the inner foreach, I wish some action to be made (some content added to a text string $content and a raised counter).
The goal
The arrays I'm working with can be quite large and I have been looking at the break and continue commands to try to skip unnecessary parts to improve the code and have the script running through fewer loops.
As you can see, if a loop from the inner foreach results in $status=false then we can stop it right there (no further loops here are necessary). The break is good for this to skip the rest of the forearch loops.
If this is the case then we can also skip the rest of the current outer loop from the outer foreach, since there will not be run any code now that $status is false. For this purpose the continue command would be useful to let us skip the rest of the current outer loop and go on to the next array row right away.
The question
I have been looking at the command lines break 2 and continue 2 e.g. that let you skip the current AND the outer loop (two steps up) with their respective results (either stopping the loop entirely or skipping the rest of current loop). They work as double break - as a "break break" - and as double continue - as a "continue continue" - respectively.
But here I need something like break continue instead, if you get my point. The first loop should break, the next should continue. I am trying to avoid reaching the if statement in the code if not necessary. The check is already made and it feels like doing an unnecessary double check.
I hope the question is clear. Is there a method for this purpose to double skip from within the inner foreach but with the effect of two different commands break continue?
"Flag" variables are mostly a code smell, and an indication that you actually should be using a function:
function all_valid($somevalues) {
foreach($somevalues as $somevalue)
if(!isset($somevalue->$someothervalue))
return false;
return true;
}
foreach($array1 as $uid => $somevalues) {
if(all_valid($somevalues) {
do stuff
If you're prepared to sacrifice readability for performance, then how about:
foreach($array1 as $uid => $somevalues) {
foreach($somevalues as $somevalue)
if(!isset($somevalue->$someothervalue))
goto next; // I don't believe I wrote this
$content .= "some content added";
$count++;
next:
}
Just use continue 2, it will 'break' out of the first loop and continue the outer loop.
foreach([1, 2, 3] as $i) {
echo $i . PHP_EOL;
foreach([4, 5, 6] as $ii) {
continue 2;
}
echo 'this will never print' . PHP_EOL;
}
Will print:
1
2
3
foreach($array1 as $uid => $somevalues) {
// Task of loop 1
foreach($somevalues as $somevalue){
// Task of loop 2
if(!isset($somevalue->$someothervalue))
{
continue 2;
break;
}
}
}

Inverse factorial loop with PHP

I got bored and created this script for the sole purpose of just practicing looping. I'm trying to use a factorial number for example 479001600 which is the factorial of 12! and I'm feeding it to the loop to find what number is 479001600 a factorial of. Using the technique 479001600/2 -> 239500800/3 -> 79833600/4 ...-> previous_int/n+1 I've come up with the following code which works only till 12! but fails on 13! onwards:
<?php
function inv($int){
$j=2;
for($i=0;$i<$j;$i++){
$prod = $int/$j;
if($prod !== 1){
$int = $prod;
echo $prod. "<br>";
$j++;
} elseif($prod == 1) {
return $j;
}
}
}
echo inv(6227020800); // 13!
?>
When I try to compute the 6227020800 to 13! I get the following output:
3113510400
1037836800
259459200
51891840
8648640
1235520
154440
17160
1716
156
13
1
0.071428571428571
0.0047619047619048
0.00029761904761905
1.750700280112E-5
9.7261126672891E-7
5.1190066669943E-8
2.5595033334971E-9
1.2188111111891E-10
...etc
Even though it gets to the integer 1 through the loop division, it carries on ignoring the if statement. Is there something I'm doing wrong? Any help will be appreciated, also, I want to avoid using the gmp_ functions.
This is because 1 and 0.071428571428571 are not the same type.
Your comparison operator, !==, checks that the values are identical and since they are of different types, the check fails. To see for yourself try this:
echo gettype(1);
echo gettype(0.12);

Reusing collection var as iteration var in a PHP foreach

I have a coworker that I noticed was using his foreachs in the following fashion:
foreach ($var as $var) {
// do stuff here
}
I have tested the code above and it works correctly to my surprise. Would the PHP gurus like to hop in and tell me why this is wrong? It feels very wrong.
Because it changes the value of $var. After the foreach() it's no longer an array but is set to the last value in the array.
$var = array('apple', 'orange');
foreach ($var as $var) {
echo $var."<br/>";
}
echo $var; //orange
If you don't want to change the variable's value, it'll need to be a different variable name:
$var = array('apple', 'orange');
foreach ($var as $fruit) {
echo $fruit."<br/>";
}
echo $var; //array
As #UselessIntern pointed out, it's fine if you're not going to use the variable after looping through it, but it's definitely not encouraged because it can lead to confusion.
As #PLB pointed out, it iterates over a copy of the $var not $var itself. So every iteration the value of $var is changing, but it doesn't break the loop because it's looping over the copy that was created.
Because it's a loop. Performing:
array > string
array > string
So
foreach ($var AS $var){
/*
Is basically being re-read at the end so your foreach can read the array again to get the next step of the array
array > string
recheck array > string
recheckarray > string
*/
}
Even it feels wrong, it still works because the moment the foreach starts, PHP internally has already access on the data.
So even $var gets overwritten, in memory this data is still present (the original array) and $var is set in each iteration to the current value of it.
The concrete problem you've spotted and about which you say is wrong is also known as variable-reuse which you should prevent because this is a code-smell.
It not only feels wrong, it is wrong to write such code. Tell your co-worker so you can write better code together.
Your coworker seems to don't need $var as an array after the loop anymore. When PHP initializes the foreach loop (what is done only once) it uses the original values from $var as it is an array at the moment. Then on every step in the loop the current element of the element is assigned to a new var called var. Note that the orginal array $var doesn't exist anymore. After the loop $var will have the value of the last element in the original array.
check this little example which demonstrates what I've said:
$a = array(1,2,3);
foreach($a as $a) {
echo var_dump($a);
}
// after loop
var_dump($a); // integer(3) not array
I could imaging that your coworker does this to save a little memory as the reference to the array will get overwritten and therefore the garbage collector will remove it's memory on next run, but I would not advice you to do the same, as it's less readable.
Just do the following, which is the same but is much more readable:
$array = array(1,2,3);
foreach($array as $value) {
echo var_dump($value);
}
delete($array);
delete($value);
Check this expression:
$x = array(1,2,3);
foreach ($x as $x) {
echo $x; //prints 123
}
What is happening here is that the foreach extracts the first element of array $x and overrides into $x itself. However the array variable $x which was on the left side of the as keyword remains in internal scope of the foreach argument which is why the looping works without problems.
Once the loop completes the $x which was internal to the foreach (the array) loses scope and no longer exists, and what remains is the $x variable which now contains the last element of the original $x array. In this case that would be 3.

How do I use multiple list()/each() calls in a while loop?

I'm working with 3 different arrays (although I'm only testing with two for the time being) and I'm trying to process the arrays on $_POST. I'm currently using:
while(list($key_member,$member)=each($_POST['member_ids'])
&& list($key_amount,$amount)=each($_POST['payment_amounts']))
{
echo "MEMBER: $member<br>";
echo "AMOUNT: $amount<br><br>";
}
If I use one list() on either array it will print the info for that particular item. However, if I attempt to use multiple list() commands in the while, only the last list()ed item gets filled properly. Is list() doing some trickery in the background that's preventing it from working in a while loop?
Obviously the "easy" solution would be to use an index and simply force the issue, but I prefer enumerating -- and I'm honestly just curious as to
What am I doing wrong, and/or what is "broken" with list()?
bug? dunno.
here's a workaround.
while(list($key_member,$member)=each($_POST['member_ids'])){
list($key_amount,$amount)=each($_POST['payment_amounts']);
echo "MEMBER: $member<br>";
echo "AMOUNT: $amount<br><br>";
}
You could extract each array's keys using array_keys(), which produces an indexed array, then keep separate loop counters for each array:
$member_ids = array_keys($_POST['member_ids']);
$amounts = array_keys($_POST['payment_amounts']);
$mi = 0;
$am = 0;
while(1) {
...
$mi++
$am++;
if (count($member_ids) >= $mi) && (count(amounts) >= $am) {
break;
}
}
&& is evaluated in a short-circuit manner, the first statement to return false jumps out of it. In your case it stops to iterate as soon as the first array is at its end. list should work fine here, as it's a language construct which assigns variables.

Categories