Seerumi
Seerumi

Reputation: 2037

PHP: variable scope in multiple foreach

EDIT:
Problem solved by using $i+1 in function call.

I have trouble using variables in multiple foreaches. The problem commeth when I try to call a function inside foreach. Whenever I do this the main loop's iterator value is suddenly zero (no matter which lap it's on) but when I comment out the function call the iterator value shows as it should again.

Could someone point me to the right direction in accessing variables in following examples:

This works as it shoulds

for($i=0; $i<3; $i++)
{
    echo $i; // 1, 2, 3
    foreach($something as $value)
    {
        echo $i; // main loop's iterator value
        foreach($value as $moreSomething)
        {
            echo $i; // main loop's iterator value 
        }
    }
}

But this doesn't work, iterator shows up as a 0.

for($i=0; $i<3; $i++)
{
    echo $i; // 1, 2, 3
    foreach($something as $value)
    {
        echo $i; // 0
        foreach($value as $moreSomething)
        {
            echo $i; // 0
            $object->addStuff($i, $moreSomething); // i = 0, moreSomething is correct
        }
    }
}

EDIT: I'm adding example code to reproduce the problem. Note that this is about the behaviour of $i's value, not the correct order of the names or something like that. I'm just concerned as why the $i's value suddenly changes. Might be my logic, but see for yourself

(when using $i or $b in addName() function $i's output is 000000000012, when using $a then $i's output suddenly changes to 00000000001111111222)

class RockPaperScissors
{
    private $nameArray;

  // constructor
  function RockPaperScissors () 
  {
    $this->nameArray = array();
  }

  function addName($level, $name)
  {
    $this->nameArray[$level][] = $name;
  }

  function getNames($level)
  {
    $array = array();
    foreach ($this->nameArray as $key => $value)
    {
        if ($key == $level)
        {
            foreach ($value as $name)
            {
                $array[] = $name;
            }
        }
    }
    return $array;
  }

  function printArray()
  {
    print_r($this->nameArray);
  }
}

function getNewNames($name)
{
    $array = array();
    switch ($name)
    {
        case "Mickey":
            $array[] = "Morty";
            $array[] = "Ferdie";
            break;

        case "Donald":
            $array[] = "Houie";
            $array[] = "Dewey";
            $array[] = "Louie";
            break;

        case "Goofy":
            $array[] = "Gilbert";
            break;

        case "Morty":
            $array[] = "Morty-B";
            break;

        case "Ferdie":
            $array[] = "Ferdie-B";
            break;

        case "Houie":
            $array[] = "Houie-B";
            break;

        case "Dewey":
            $array[] = "Dewey-B";
            break;

        case "Louie":
            $array[] = "Louie-B";
            break;

        case "Gilbert":
            $array[] = "Gilbert-B";
            break;
    }

    return $array;
}

$MAX_LEVELS = 3;
$RPS = new RockPaperScissors();
$RPS->addName(0, "Mickey");
$RPS->addName(0, "Donald");
$RPS->addName(0, "Goofy");

$a = 0;
$b = 0;
for ($i=0; $i<$MAX_LEVELS; $i++)
{
    $namesFromRPS = $RPS->getNames($i);
    echo $i;
    foreach($namesFromRPS as $name)
    {
        echo $i;
        $newNames = getNewNames($name);
        foreach($newNames as $newName)
        {         
            echo $i;
            // try switching $i to $a or $b and notice the behaviour change of $i
            $RPS->addName($i, $newName);
        }
        $a++;
    }
    $b++;
}

//$RPS->printArray();

Upvotes: 1

Views: 6295

Answers (6)

Greg
Greg

Reputation: 21909

<?php

$something = array(
    "one"   => array("oneMore1", "oneMore2", "oneMore3"),
    "two"   => array("twoMore1", "twoMore2", "twoMore3"),
    "three" => array("thrMore1", "thrMore2", "thrMore3"));

for($i = 0; $i < 3; $i++)
{
    echo "FIRST LOOP: " . $i;
    echo "\n";
    foreach($something as $value)
    {
        echo "SECOND LOOP: " . $i;
        echo "\n";
        foreach($value as $moreSomething)
        {
            echo "THIRD LOOP: " . $i;
            echo "\n";
            //$object->addStuff($i, $moreSomething);
        }
    }
}

I see no problem executing this code here: http://www.ideone.com/HsVDj

Upvotes: 0

Felix Kling
Felix Kling

Reputation: 816374

Not a real answer, but your code is actually the same as:

foreach($something as $value)
{
    foreach($value as $moreSomething)
    {
        for($i=0; $i<3; $i++)
        {
            $object->addStuff($i, $moreSomething); // i = 0, moreSomething is correct
        }
    }
}

Are you sure this logic is correct? What do you want to do? Not knowing what addStuff is doing, it seems a bit odd to add the same value three times with another index to something.

Here is an example with a method call:

$something = array(array(1,2), array(1,2));

class F {
   public function b($i, $value) {
       echo 'In function: i: ' . $i . ' value: ' . $value . PHP_EOL;
   }
}

$f = new F();

for($i=0; $i<3; $i++)
{
    echo "in for: " . $i . PHP_EOL; // 1, 2, 3
    foreach($something as $value)
    {
        echo "in 1. foreach: " . $i . PHP_EOL;
        foreach($value as $moreSomething)
        {
            echo "in 2. foreach: " . $i . PHP_EOL;
            $f->b($i, $moreSomething);

        }
    }
}

which prints (as expected):

in for: 0
in 1. foreach: 0
in 2. foreach: 0
In function: i: 0 value: 1
in 2. foreach: 0
In function: i: 0 value: 2
in 1. foreach: 0
in 2. foreach: 0
In function: i: 0 value: 1
in 2. foreach: 0
In function: i: 0 value: 2
in for: 1
in 1. foreach: 1
in 2. foreach: 1
In function: i: 1 value: 1
in 2. foreach: 1
In function: i: 1 value: 2
in 1. foreach: 1
in 2. foreach: 1
In function: i: 1 value: 1
in 2. foreach: 1
In function: i: 1 value: 2
in for: 2
in 1. foreach: 2
in 2. foreach: 2
In function: i: 2 value: 1
in 2. foreach: 2
In function: i: 2 value: 2
in 1. foreach: 2
in 2. foreach: 2
In function: i: 2 value: 1
in 2. foreach: 2
In function: i: 2 value: 2

So the error must be somewhere else.

Upvotes: 1

Bojangles
Bojangles

Reputation: 101473

I'm not sure, but it could be something to do with the fact that $i is defined within the for() loop. I'd try defining $i just before the for() loop is defined.

James

Upvotes: 0

DKSan
DKSan

Reputation: 4197

i added your variable $something as a 2d Array

$something = Array(1 => Array(1 => "1", 2 => "2"), 2 => Array(3 => "3", 4 => "4"))

Using this, your foreach shows just fine:

000000011111112222222

Upvotes: 0

Greg
Greg

Reputation: 21909

In your second example, $i will be 0 for the first iteration of the base loop.

It will stay set to 0 for all $somethings and all $values, then it will move on to the next base iteration, setting $i to 1 and so on...

Upvotes: 0

Pekka
Pekka

Reputation: 449415

foreach loops do not have their own scope.

The way this is expected to work is:

Set $i to 0
 Enter the first foreach loop with `$i = 0`
  Enter the second foreach loop with `$i = 0`

Set $i to 1
 Enter the first foreach loop with `$i = 1`
  Enter the second foreach loop with `$i = 1`

etc.

I'll bet a beer that the loops work as expected, but there is nothing to do for the inner loops when $i reaches 1.

Upvotes: 5

Related Questions