Access variable from inside a nested foreach statement PHP - php

I am getting the key from a given value in a multidimensional array. It works fine except that I cannot seem to access the variable from OUTSIDE the nested foreach loop that I'm using to get the key.
so my foreach loop is: ($name_books is the multi-d array which contains 3 smaller arrays)
foreach($name_books as $test) {
foreach ($test as $key => $value) {
$book_code = array_search($row['name'],$test);
echo $book_code; //just to see if it works, which it does
break;
}
}
//But then if I go outside of the loop..
echo $book_code." is the book code"; // <--DOES NOT WORK
So I know that I'm dealing with variable scope issues here and I've tried declaring globals inside the foreach loop but nothing works.
I'm sure there is something absurdly simple I'm missing!
EDIT:
urg..I took a step back and realized something else,
all this is happening inside a while loop (getting stuff from a db)
so the code is more like:
while($row=mysql_fetch_assoc($result)) {
...original foreach loop from above
}
apologies for not including this, I was focusing on this tiny piece and forgot to back up and see where it fit.

break;
Will only exit the internal nested foreach. If there are more rows in $name_books, it will continue looping and eventually overwriting $book_code with 'false' values from array_search;
Once you've found the value you're looking for, use:
break 2;
Regarding your edit, where you break depends on what you're doing with the value you've found for $book_code. If you don't plan on continuing, change the parameter for break. break 3; will exit the while loop too. Change the value depending on the level of nesting.

This has nothing to do with variable scope so long as what you posted is exactly what you have in your script.
I think what the problem is, is that you are only breaking out of the inner loop. In each iteration of the outer loop, $book_code will get changed, so you need to stop the outer loop as well. Try changing break; to break 2; and see if it fixes your problem. That causes it to break out of both the inner and the outer loop.
Edit: I think you can also simplify your code:
foreach ($name_books as $test) {
$book_code = array_search($row['name'], $test);
if ($book_code !== FALSE) {
break;
}
}If I knew more about your structure, this could possibly be reduced down to a single SQL statement and 0 loops.

simshaun is right, but I would actually take a different approach.
I would check for the existence of $book_code in my foreach loops rather than dealing with the breaks.
New Code
foreach($name_books as $test) {
foreach ($test as $key => $value) {
if(!isset($book_code)){
$book_code = array_search($row['name'],$test);
echo $book_code; //just to see if it works, which it does
}
}
}
echo $book_code." is the book code";

Related

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;
}
}
}

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.

is there a way in PHP to restart a loop in a foreach, or change the test value in a switch?

if i'm looping over an array, and while in the middle of one of the loops i discover some small issue, change ...something..., and need to try again ... is there a way to jump back to the top of the loop without grabbing the next value out of the array?
i doubt this exists, but it would be some keyword like continue or break. in fact, it would be a lot like continue, except that it doesn't get the next item, it maintains what it has in memory.
if nothing exists, can i insert something into the array in such a way that it will become the next key/value in the loop?
maybe this would be easier with a while(array_shift())...
or i suppose a recursive function inside the loop might work.
well, my question is evolving as i type this, so please review this pseudo code:
foreach($storage_locations as $storage_location) {
switch($storage_location){
case 'cookie':
if(headers_sent()) {
// cannot store in cookie, failover to session
// what can i do here to run the code in the next case?
// append 'session' to $storage_locations?
// that would make it run, but other items in the array would run first... how can i get it next?
} else {
set_cookie();
return;
}
break;
case 'session':
set_session();
return;
break;
}
}
i'm sure there is no keyword to change the value tested against in the switch mid-stream... so how should i refactor this code to get my failover?
Not with a foreach, but with more manual array iteration:
while (list($key, $value) = each($array)) {
if (...) {
reset($array); // start again
}
}
http://php.net/each
http://php.net/reset
It seems like a simple fall through would do the trick though:
switch ($storage_location) {
case 'cookie':
if (!headers_sent()) {
set_cookie();
break;
}
// falls through to next case
case 'session':

Changes to a PHP Array Not "Sticking"

OK so I'm making something to do some data mining but I do changes to an array (by overwritting previous array values) in a loop and they show that they've been changed but once I get outside of a greater loop the values change back to their original values.
Probably easier to give an example:
It starts off like this, turning a bunch of the parts of the array into the word "MATCH".
Now if I was to immediately dump the values of the array it would show that some values have changed to "MATCH" (ie, right after changing the value I would echo the array slot and it would show it's value to be "MATCH") However after I get outside the loop the array changes back to it's original contents
Here is a compressed version of the code:
//i've got this big loop for doing the main work
do {
//Set dat ticker
$q = 0;
// Run through entire previous scrape array to check for matches and mark them as unchanged
do {
if ($itemTitle[$i] == $prodURLS[$q]) {
$prodURLS[$q] = "MATCH";
echo "When the value is printing immediately it shows that it's changed: ".$prodURLS[$q]."<br>";
}
$q++;
} while ($q < $urlArraySize);
$i++;
} while ($i < $itemtitleArraySize);
//If I were to try to print the variable down here it would be reverted to like it was before I changed it to "MATCH"
print_r($prodURLS);
From running your code, setting the variables as follow, it works for me:
$prodURLS = array('a','b','c');
$itemTitle = array('a');
$urlArraySize = count($prodURLS);
$itemtitleArraySize = count($itemTitle);
$i = 0;
My only recommendations with only this amount of information, are:
To provide more context information, as madth3 suggests.
To check the scope in which you are setting/checking values. You may need the & operator to pass variables by reference, or the global keyword to use global variables.
To use the foreach loop, it will make your code smaller and easier to read. Also you won't need to count the size of the arrays and will have other advantages, e.g. in the use of associative arrays. Again, be careful about the use of variables by reference. For example:
foreach ($itemTitle as $item) {
foreach ($prodURLS as &$prod) {
if ($item == $prod) {
$prod = 'MATCH';
}
}
}
unset($prod); //Unset variable set by reference if you are going to use it later on!
Also, you may find useful some of the php array functions like array_walk. Check out the PHP Manual on the array functions reference.
Really, there isn't a lot that can be said from just the code you provided.
Good luck.

PHP what is the best way to handle a variable that may not be set?

I have a foreach loop, that will loop through an array, but the array may not exist depending on the logic of this particular application.
My question relates to I guess best practices, for example, is it ok to do this:
if (isset($array))
{
foreach($array as $something)
{
//do something
}
}
It seems messy to me, but in this instance if I dont do it, it errors on the foreach. should I pass an empty array?? I haven't posted specific code because its a general question about handling variables that may or may not be set.
Just to note: here is the 'safest' way.
if (isset($array) && is_array($array)) {
foreach ($array as $item) {
// ...
}
}
Try:
if(!empty($array))
{
foreach($array as $row)
{
// do something
}
}
That's not messy at all. In fact, it's best practice. If I had to point out anything messy it would be the use of Allman brace style, but that's personal preference. (I'm a 1TBS kind of guy) ;)
I'll usually do this in all of my class methods:
public function do_stuff ($param = NULL) {
if (!empty($param)) {
// do stuff
}
}
A word on empty(). There are cases where isset is preferable, but empty works if the variable is not set, OR if it contains an "empty" value like an empty string or the number 0.
If you pass an empty array to foreach then it is fine but if you pass a array variable that is not initialized then it will produce error.
It will work when array is empty or even not initialized.
if( !empty($array) && is_array($array) ) {
foreach(...)
}
I would say it is good practice to have a 'boolean' other value that is set as 0 (PHP's false) to start, and any time some function adds to this array, add +1 to the boolean, so you'll have a definite way to know if you should mess with the array or not?
That's the approach I would take in an object oriented language, in PHP it could be messier, but still I find it best to have a deliberate variable keeping track, rather than try to analyze the array itself. Ideally if this variable is always an array, set the first value as 0, and use it as the flag:
<?PHP
//somewhere in initialization
$myArray[0] = 0;
...
//somewhere inside an if statement that fills up the array
$myArray[0]++;
$myArray[1] = someValue;
//somewhere later in the game:
if($myArray[0] > 0){ //check if this array should be processed or not
foreach($myArray as $row){ //start up the for loop
if(! $skippedFirstRow){ //$skippedFirstRow will be false the first try
$skippedFirstRow++; //now $skippedFirstRow will be true
continue; //this will skip to the next iteration of the loop
}
//process remaining rows - nothing will happen here for that first placeholder row
}
}
?>

Categories