0

I have a section of code that loops through an array of sql queries like so:

$cached_queries = array();

foreach ($queries as $index => $query2)
{
    $query2 = trim($query2);

    if ($query2 != "")
    {
        if (isset($cached_queries[$query2]))
        {
            $err_msg = $index . ": query [$query2] has already been executed on " .
                       "index [" . $cached_queries[$query2] . "]!";
            die($err_msg);
        }
        else 
        {
            $cached_queries[$query2] = $index;
            print "$index: executing query: [$query2]<br>" . PHP_EOL;
            $db->query($query2);
        }
    }
}

It works with the variable name $query2, but if I change that to $query. the script tries to execute the second to last query twice and dies with the error message, stating that the second to last statment (84th index) has already been executed when executing the last statement.

...
84: executing query: [INSERT INTO `world_bank_incomes` (`id`, `name`) VALUES (1, 'Low-income'), (2, 'Lower-middle-income'), (3, 'Upper-middle-income'), (4, 'High-income');]

85: query [INSERT INTO `world_bank_incomes` (`id`, `name`) VALUES (1, 'Low-income'), (2, 'Lower-middle-income'), (3, 'Upper-middle-income'), (4, 'High-income');] has already been executed on index [84]!

Please explain why a simple change to the variable name allows the script to work as expected.


Context Information

the last query is:

INSERT INTO `world_bank_regions` (`id`, `name`) VALUES (1, 'East Asia and Pacific'), (2, 'Europe and Central Asia'), (3, 'Latin America & the Caribbean'), (4, 'Middle East and North Africa'), (5, 'South Asia'), (6, 'Sub-Saharan Africa');

A direct copy of the original 'broken' code using the $query variable:

$cached_queries = array();

foreach ($queries as $index => $query)
{
    $query = trim($query);

    if ($query != "")
    {
        if (isset($cached_queries[$query]))
        {
            $err_msg = $index . ": query [$query] has already been executed on " .
                       "index [" . $cached_queries[$query] . "]!";
            die($err_msg);
        }
        else 
        {
            $cached_queries[$query] = $index;
            print "$index: executing query: [$query]<br>" . PHP_EOL;
            $db->query($query);
        }
    }
}

The value of $query before it reaches the first line of this section of code is:

INSERT INTO `world_bank_regions` (`id`, `name`) VALUES (1, 'East Asia and Pacific'), (2, 'Europe and Central Asia'), (3, 'Latin America & the Caribbean'), (4, 'Middle East and North Africa'), (5, 'South Asia'), (6, 'Sub-Saharan Africa');

I know it's bad practice to re-use variables but it shouldn't have an affect in this situation should it?

Framework: codeigniter (so $db is the CI mysql driver).

Query is used as a reference just before the code section:

foreach ($queries as &$query)
{
    $query = trim($query);

    if ($query != "")
    {
        $query = "INSERT " . $query;
    }
} 

$cached_queries = array();
...
Programster
  • 12,242
  • 9
  • 49
  • 55
  • What happens if you unset the `$query` variable before the loop? – Peter Bloomfield Dec 06 '13 at 11:35
  • @PeterR.Bloomfield that works, so it appears to be an issue with variable re-use, but why? whats going on? – Programster Dec 06 '13 at 11:37
  • Is it a case of: If foreach tries to set $query to the same value it was outside the foreach loop it doesn't update the variable? – Programster Dec 06 '13 at 11:38
  • DOes `$index` maintain `84` for two cycles of the foreach loop? can you `print "$index\n";`? What about a `var_dump($queries);` at each iteration (although the foreach subject should not change)? or a `var_dump($cached_queries)`; to verify the cache is building as intended? Is there anything that could be passed by reference earlier in the script? – zamnuts Dec 06 '13 at 11:54
  • @zamnuts It is not maintaining index 84 for two cycles because the output shows `85: query [INSERT INTO `` The 85 there shows that it is using index 85 instead of index 84 I performed a vardump just before die, and it does appear the $queries array is being manipulated such that the last two indexes are the same. You are right about the reference, I will add it to the context of the question. No doubt this is the kicker. – Programster Dec 06 '13 at 12:00
  • The reference would be maintained indefinitely after the initial `foreach` since control structures are not subject to block scope in PHP. However, at first glance, I don't see how this reference would push another value into `$queries`. – zamnuts Dec 06 '13 at 12:09
  • possible duplicate of: http://stackoverflow.com/questions/8220399/php-foreach-pass-by-reference-last-element-duplicating-bug – zamnuts Dec 06 '13 at 12:12
  • 1
    @zamnuts yes unfortunately it does appear to be a duplicate of that. I can only see that now after having had these comments which made me look further back at the code to see the reference as an issue. Is it possible for me to mark my own question as a dupe? or should I just delete the question? – Programster Dec 06 '13 at 12:19
  • @Stu2000 i flagged it as a duplicate for moderator attention, just leave it, they'll do with it as they see fit :) probably maintain it as a duplicate for future reference – zamnuts Dec 06 '13 at 12:21
  • @zamnuts yes, it appears that I need to reach 250 rep before I can mark my own question as a duplicate unfortunately. – Programster Dec 06 '13 at 12:22
  • possible duplicate of [PHP Pass by reference in foreach](http://stackoverflow.com/questions/3307409/php-pass-by-reference-in-foreach) – mirabilos Dec 06 '13 at 12:48

0 Answers0