【问题标题】:Getting Invalid argument supplied for foreach as a result of pass by reference variable由于通过引用变量传递,获取为 foreach 提供的无效参数
【发布时间】:2018-12-09 03:20:27
【问题描述】:

我正在升级一个使用按引用传递的代码库 主要功能

    function splitSqlFile(&$ret, $sql)
    {
       $sql               = trim($sql);
       $sql_len           = strlen($sql);
       $char              = '';
       $string_start      = '';
       $in_string         = false;

      for ($i = 0; $i < $sql_len; ++$i) {
           $char = $sql[$i];
        if ($in_string) {
            for (;;) {
                $i = strpos($sql, $string_start, $i);
                if (!$i) {
                    $ret[] = $sql;
                    return true;
                }else if ($string_start == '`' || $sql[$i-1] != '\\'){
                    ......
                }else {
                    ......
                } // end if...elseif...else
            } // end for
        }
        else if ($char == ';') {
            $ret[]    = substr($sql, 0, $i);
            $sql      = ltrim(substr($sql, min($i + 1, $sql_len)));
            $sql_len  = strlen($sql);
            if ($sql_len) {
                $i = -1;
            } else {
                // The submited statement(s) end(s) here
                return true;
            }
        }else if (($char == '"') || ($char == '\'') || ($char == '`')) {
            $in_string    = true;
            $string_start = $char;
        } // end else if (is start of string)

        // for start of a comment (and remove this comment if found)...
        else if ($char == '#' || ($char == ' ' && $i > 1 && $sql[$i-2] . $sql[$i-1] == '--')) {
            ......
            if (!$end_of_comment) {
            // no eol found after '#', add the parsed part to the returned
            // array and exit
                $ret[]   = trim(substr($sql, 0, $i-1));
                return true;
            } else {
                .....
            } // end if...else
        } // end else if (is comment)
    } // end for

    // add any rest to the returned array
    if (!empty($sql) && trim($sql) != '') {
        $ret[] = $sql;
    }
    return true;
}

调用函数

    $sqlUtility->splitSqlFile($pieces, $sql_query);
    foreach ($pieces as $piece) 
    {
      .......
    }

如果上面的变量splitSqlFile(&$ret, $sql)前面有“&”,程序确实运行成功,但是如果去掉,现在splitSqlFile($ret, $sql),会开始返回'foreach 提供的参数无效'错误。当我尝试使用“is_array”函数检查它是否为数组时,结果始终为“NULL”。

【问题讨论】:

  • 如果我应该使用 splitSqlFile(&$ret, $sql) ,它会工作,但如果它 splitSqlFile($ret, $sql) ,它会返回一个错误
  • 嗯,错误是由于 $pieces 是一个死的和空的变量(foreach 会拒绝)。它已经死了,因为一旦你从 $ret 中删除 & ......那么 $ret 就不再是对 $pieces 的引用。因此,您在该函数中对 $ret 所做的任何事情都不会对 $pieces 进行。希望这更清楚?
  • 是的,谢谢我现在明白了
  • 但是在其他方面纠正问题,有什么建议吗?
  • 决定把所有这些都写下来给你。

标签: php foreach pass-by-reference


【解决方案1】:

为什么会出现错误:

通过从$ret 中删除&amp;,您将不再在函数调用中引用该变量。在这种情况下,$pieces。所以当你在调用函数后对$pieces 执行foreach 时会出错,因为此时$pieces 基本上是一个空变量。

function splitSqlFile(&$ret,$sql) {
    $ret[] = 'stuff';
}
splitSqlFile($pieces,$sql);
// $pieces will be an array as 0 => 'stuff'
foreach ($pieces as $piece) { } // will not error

对比:

function splitSqlFile($ret,$sql) {
    $ret[] = 'stuff';
}
splitSqlFile($pieces,$sql);
// $pieces will be a null variable, since it was never assigned anything
foreach ($pieces as $piece) { } // will error

替代无参考:

因此,如果您想删除 &amp; 并且不再通过引用传递,则必须对函数进行其他更改以恢复该值。根据代码库的不同,这可能意味着在使用该函数的任何地方都需要大量工作!

例子:

function splitSqlFile($sql) {
    $ret = [];
    $ret[] = 'stuff';
    return array('result'=>true,'ret'=>$ret);
}
// $result will contain multiple things to utilize

// if you will only need that variable once (does not accumulate)
$result = splitSqlFile($sql);
foreach ($result['pieces'] as $piece) { }

// if that variable is added by multiple calls, and displayed later... merge
$pieces = [];
$result = splitSqlFile($sql_1);
$pieces = array_merge($pieces,$result['pieces']);
$result = splitSqlFile($sql_2);
$pieces = array_merge($pieces,$result['pieces']);
foreach ($pieces as $piece) { }

第二个示例(在执行过程中传入数组...会让人感到困惑):

function splitSqlFile($pieces_in,$sql) {
    $pieces_in[] = 'stuff';
    return array('result'=>true,'pieces_out'=>$pieces_in);
}
$pieces = [];
$result = splitSqlFile($pieces,$sql_1);
$pieces = $result['pieces_out'];
$result = splitSqlFile($pieces,$sql_2);
$pieces = $result['pieces_out'];
foreach ($pieces as $piece) { }

如您所见,它不仅改变了必须处理的返回值,而且还改变了调用方式。再一次,如果这个函数在代码中的一千个地方使用......严重的头疼!

结论:

老实说,我会保持参考原样。这样做是为了使积累调试数据更容易、更直接。否则,您需要对代码进行大量更改才能摆脱引用。

不过,这只是我对此事的看法。

【讨论】:

  • 非常感谢,非常感谢您的解释
  • 希望它不会带来挑战。我自己也经历了一些这样的事情,将许多“全局完成的函数”转换为类对象,并且每个函数都有数千行代码更改:(所以......我希望你的项目一切顺利!
  • 第三种选择 .... 如果这个 splitSqlFile 函数将在一个类中... 可以将所有 $ret[]= 更改为使用类变量,如 $this-&gt;ret[]= .. . 然后创建一个新的类方法,在 foreach 之前说完之后只返回那个变量。
猜你喜欢
  • 1970-01-01
  • 2018-08-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-27
  • 2011-05-17
  • 2011-02-07
相关资源
最近更新 更多