PHP foreach循环使用一个条目两次

我只是尝试使用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循环中,在第7个最初正确的数组的位置变为1个重复数组.

任何建议都会很棒.

编辑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($families as &$fam){
    $s->bindValue(':fID', $fam['ID']);
    $s->execute();
    $fam['ScentCount'] = $s->fetchColumn();
}

… $fam指的是$系列数组的最后一个元素.

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

foreach($families as $fam){

… $fam点不会改变的内存位置,它仍然锁定到$families数组的最后一个元素.因此在第一次迭代中,第一个元素的内容被复制到$fam中,即在最后一个条目中,然后在第二次迭代时,第二个值被覆盖在那里,依此类推.当最后一次迭代开始时,最后一个元素包含一个 – 但是最后一个值,并且它被……本身覆盖,这又是一个但是最后一个值.

这个bug report提出了同样的问题,给出的答案是这是预期的行为.在回答one of the duplicate bug reports时,这说得很清楚:

The current implementation is consistent. Granted, not very useful,
but it would be inconsistent to arbitrarily break the reference here.
PHP has no block scope, and breaking the reference would introduce a
special-case block-scope here.

这个blog用漂亮的插图解释了相同的行为.

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

foreach($families as $fam2){

或者,对于你可能使用$fam的任何其他代码更安全,就是在第二个循环之前取消设置($fam),如下所示:

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

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

documentation on foreach有关于此行为的警告,并建议取消设置:

Warning

Reference of a $value and the last array element remain even after the foreach loop. It is recommended to destroy it by 07004.

所有这些都很尴尬,当你阅读相关“bug”报告的回复时,很明显你并不是唯一一个碰到这种意想不到的副作用的人.因此,我想强调这一点:

避免使用危险的&

几乎没有真正需要使用这个&字首.通过避免它,这些类型的奇怪副作用将属于过去.

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

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

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

相关文章

统一支付是JSAPI/NATIVE/APP各种支付场景下生成支付订单,返...
统一支付是JSAPI/NATIVE/APP各种支付场景下生成支付订单,返...
前言 之前做了微信登录,所以总结一下微信授权登录并获取用户...
FastAdmin是我第一个接触的后台管理系统框架。FastAdmin是一...
之前公司需要一个内部的通讯软件,就叫我做一个。通讯软件嘛...
统一支付是JSAPI/NATIVE/APP各种支付场景下生成支付订单,返...