【问题标题】:PHP foreach loop using one entry twicePHP foreach 循环使用一个条目两次
【发布时间】:2016-08-08 14:00:48
【问题描述】:

我只是在使用 PHP 和 PDO 与 MySQL 数据库进行一些试验,我有点困惑为什么在获得结果后,将它们正确存储在多维数组中并循环通过它们输出一个数组数据的两次。

基本上这是获取数据的查询:

SELECT b.Price, b.ImgURL, m.Name, f.ID, f.Family, f.URL FROM Products AS b INNER JOIN Manufacturers AS m ON m.ID = b.Manufacturer INNER JOIN FamilyLookUp AS l ON l.Product = b.ID INNER JOIN Families AS f ON f.ID = l.Family GROUP BY f.ID ORDER BY b.Price ASC

我希望这样可以为每个家庭返回 1 行,它在 PHPMyAdmin 查询和 print_r() 结果中都能正常工作。

然后我存储在:

$families[] = array('ID' => $f['ID'], 'Manufacturer' => $f['Name'], 'Family' => $f['Family'], 'URL' => $f['URL'], 'IMG' => $f['ImgURL'], 'Price' => $f['Price'], 'ScentCount' => 0);

这在执行 print_r() 时也可以正常工作,并且当仅使用 foreach 循环回显每个条目的 ID 时,它返回 1234567(所有 7 个家庭 ID)

然后我运行另一个查询:

try{
$sqlCmd = "SELECT COUNT(*) FROM FamilyLookUp WHERE Family=:fID";
$s = $pdo->prepare($sqlCmd);
foreach($families as &$fam){
$s->bindValue(':fID', $fam['ID']);
$s->execute();
$fam['ScentCount'] = $s->fetchColumn();
}
}

这也获得了正确的计数,并将它们正确地存储在数组中,用于每个系列中的项目数。到目前为止一切都很好。

问题发生在我:

foreach($families as $fam):
        ?>

        <div class="product-listing">
        <?php echo $fam['ID']; ?>
            <div class="product-listing-image">
                <a href="<?php echo $fam['URL']; ?>"><img alt="" src="<?php echo $fam['IMG']; ?>"></a>
            </div>
            <div class="product-listing-details">

                <a href="<?php echo $fam['URL']; ?>"><h3><?php echo strtoupper($fam['Manufacturer']); if($fam['Family'] != ""){ echo strtoupper(' - ' . $fam['Family']);} ?></h3></a>
                <?php if($fam['ScentCount'] == 1): ?>
                <span class="product-scent-count"><?php echo $fam['ScentCount']; ?> Scent</span>
                <span class="product-price-value">£<?php echo $fam['Price']/100; ?></span>
                <?php elseif($fam['ScentCount']>1): ?>
                <span class="product-scent-count"><?php echo $fam['ScentCount']; ?> Scents</span>
                <span class="product-price-value">From £<?php echo $fam['Price']/100; ?></span>
                <?php endif;?>
            </div>
        </div>

        <?php
            endforeach;
        ?>

执行此操作后,它会正确输出前 6 个数据系列,但由于某种原因,它会输出第 6 个数据的副本,而不是实际的第 7 个数据。在 foreach 循环开始之前对行中的所有数据执行 print_r 时,它会返回所有正确的数据,但在 foreach 循环中,会出现 1 个重复数组代替第 7 个原始正确数组。

任何建议都会很棒。

编辑 Kohloth 的答案(print_r 后跟 foreach vardump):

Array
(
    [0] => Array
        (
            [ID] => 1
        )

    [1] => Array
        (
            [ID] => 7
        )

    [2] => Array
        (
            [ID] => 2
        )

    [3] => Array
        (
            [ID] => 3
        )

    [4] => Array
        (
            [ID] => 4
        )

    [5] => Array
        (
            [ID] => 6
        )

    [6] => Array
        (
            [ID] => 5
        )

)
            array(7) {
  ["ID"]=>
  string(1) "1"
}

    array(7) {
  ["ID"]=>
  string(1) "7"
}

    array(7) {
  ["ID"]=>
  string(1) "2"
}

    array(7) {
  ["ID"]=>
  string(1) "3"
}

    array(7) {
  ["ID"]=>
  string(1) "4"
}

    array(7) {
  ["ID"]=>
  string(1) "6"
}

    array(7) {
  ["ID"]=>
  string(1) "6"
}

【问题讨论】:

  • 猜测:在foreach循环之后添加unset($fam);foreach ($families as &amp;$fam){ ...。你也可以停止使用bindParam。而是使用。 'bindValue' 还是只传递 'execute' 语句中的值? imo,您很少需要将'bindParam' 与PDO 一起使用。 imo,您也不需要'bindValue'。 imo,只需传递execute 中的数据数组即可。也许有趣? PDO made really easy most of the time?
  • 感谢您的评论瑞恩。我已经在使用 bindValue 了。虽然对于学习 PHP 来说相对较新,但肯定会更多地关注在 execute 方法中传递数据数组。有趣的是,当我在 foreach 循环的末尾添加 unset($fam) 时,kohloth 建议它更改输出。从按顺序输出数组 1723466 = ID 为 6 的重复族,到 1723461 = ID 为 1 的重复族
  • 天哪。非常感谢你的帮助。当放在 foreach 循环结束之后,未设置的东西实际上起作用了,我认为你的意思是在可执行代码结束后的循环内。我肯定会尝试仅在函数内需要时才使用 foreach 循环。无论如何,我无法想象有太多的参考文献是那么棒。非常感谢
  • 不,经常使用 foreach 循环! 不要在 PHP 中使用 '&$var',除非你真的必须这样做。你不应该这样做。很高兴你把它整理好了 - 感谢收听 :)
  • 查看@trincot 提供的答案以了解正在发生的事情。 :)

标签: php mysql arrays pdo foreach


【解决方案1】:

这是正在发生的事情:

在此循环的最后一次迭代中:

foreach($families as &$fam){
    $s->bindValue(':fID', $fam['ID']);
    $s->execute();
    $fam['ScentCount'] = $s->fetchColumn();
}

... $fam 指的是 $families 数组的最后一个元素。

然后当你的下一个循环开始时:

foreach($families as $fam){

... $fam 指向的内存位置没有改变,它仍然锁定到 $famlies 数组的最后一个元素。所以在第一次迭代中,第一个元素的内容被复制到 $fam 中,即在最后一个条目中,然后在第二次迭代中,第二个值被覆盖在那里,依此类推。当最后一次迭代开始时,最后一个元素包含最后一个值,这会被 ... 本身覆盖,这又是最后一个值。

这个bug report 提出了同样的问题,给出的答案是这是预期的行为。作为对one of the duplicate bug reports 的回答,这一点说得很中肯:

当前的实现是一致的。诚然,不是很有用, 但是在这里随意打断引用会是不一致的。
PHP 没有块作用域,打破引用会引入一个 这里是特殊情况的块作用域。

这个blog 用漂亮的插图解释了同样的行为。

解决方案是在第二个循环中使用另一个新变量,如下所示:

foreach($families as $fam2){

对于您可能使用 $fam 的任何其他代码,另外一种更安全的方法是在第二个循环之前 unset($fam),如下所示:

unset($fam);
foreach($families as $fam){

这是因为在 foreach 循环开始时,变量是从头开始重新创建的,因此指向它自己的新内存位置。

documentation on foreach 对此行为有警告,建议unset

警告

$value 的引用和最后一个数组元素即使在 foreach 循环之后仍然存在。建议unset()销毁。

这一切都很尴尬,当您阅读对相关“错误”报告的回复时,很明显您并不是唯一遇到这种意外副作用的人。因此,我想强调这一点:

避免使用危险的&amp;

几乎不需要使用这个&amp; 前缀。通过避免它,这些类型的奇怪副作用将属于过去。

您的代码可以在没有&amp; 的情况下重写如下:

foreach($families as $i => $fam){
    $s->bindValue(':fID', $fam);
    $s->execute();
    // use the index to put the value in place in the array:
    $families[$i]['ScentCount'] = $s->fetchColumn();
}

请注意,它也不会损害代码的可读性。

【讨论】:

  • 感谢您对正在发生的事情做出更清晰的解释。
  • 感谢您的详细解释。我只是假设 $fam 只存在于循环中。很酷,肯定会阅读链接的材料,然后再阅读一些!非常感谢
【解决方案2】:

不管怎样,这第二双眼睛看不出代码的 HTML 输出部分有什么问题。其中没有任何部分会改变您正在迭代的数组的结构,所以在我看来,您的诊断可能不正确?可能值得注释掉整个最后一个 foreach 循环,并将其替换为:

<?php foreach($families as $fam): ?>
    <?php var_dump($fam);?> 
<?php endforeach; ?>

只是在您输入 HTML 繁重的 foreach 块之前仔细检查 $families 数组是否正确。

编辑:我已经直接在它之前运行了带有显式数组声明的视图代码,它不会复制最后一个结果。将其直接放在视图代码之前会发生什么?

$families = [
    [
        'ID' => 1,
        'Manufacturer' => 'man1',
        'Family' => 'fam1',
        'URL' => 'url1',
        'IMG' => 'img1',
        'Price' => 'price1',
        'ScentCount' => 1
    ],
    [
        'ID' => 2,
        'Manufacturer' => 'man2',
        'Family' => 'fam2',
        'URL' => 'url2',
        'IMG' => 'img2',
        'Price' => 'price2',
        'ScentCount' => 2
    ],
    [
        'ID' => 3,
        'Manufacturer' => 'man3',
        'Family' => 'fam3',
        'URL' => 'url3',
        'IMG' => 'img3',
        'Price' => 'price3',
        'ScentCount' => 3
    ],

];

?

【讨论】:

  • 感谢您的回复。好吧,这似乎真的很奇怪。我已按照建议在 for 循环中添加了该 vardump,它给出了相同的结果(重复最后一个数组)。即使它直接在显示正确数据的 print_r 之后。为了让它更短一点,我删除了一些行,但留下了 ID 作为编辑
  • 当您在视图代码正上方显式声明类似结构的数组时会发生什么?
  • 当我使用显式声明的数组时,它似乎工作正常。知道在使用存储在我列出的数组中的数据库数据时可能导致它搞砸的原因吗?
猜你喜欢
  • 2011-06-01
  • 1970-01-01
  • 2020-04-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-25
  • 2016-04-18
  • 1970-01-01
相关资源
最近更新 更多