【问题标题】:Apache crashes in a large foreach loop on an associative array in PHPApache 在 PHP 中的关联数组上的大型 foreach 循环中崩溃
【发布时间】:2011-08-07 09:55:12
【问题描述】:

编辑:为了响应 Robus 的回答,我尝试从命令行运行 PHP 脚本。结果如下:

但有趣的是;位于我的代码中foreach 循环之后 的 echo 语句将其文本输出到控制台窗口。因此我只能假设 CLI 在运行脚本结束时崩溃。


我有一个脚本,其功能是将 XLSX 文件中的所有行加载到 MySQL 表中。我为此使用 PHPExcel。我编写了函数loadFromXLS 来从XLSX 文件中加载数据并返回一个包含数据的二维数组。在这种特殊情况下,这意味着 3100 行和 29 列。

这是函数:

function loadFromXLS($filepath)
{
    $retval = array();
    $cols = array();
    $rownum = 0;

    $reader = PHPExcel_IOFactory::createReaderForFile($filepath);
    $reader->setReadDataOnly(true);

    $phpObject = $reader->load($filepath);
    $sheet = $phpObject->getActiveSheet();

    foreach($sheet->getRowIterator() as $row)
    {
        $celliterator = $row->getCellIterator();
        $celliterator->setIterateOnlyExistingCells(false);
        $cellnum = 0;

        foreach($celliterator as $cell)
        {
            if($rownum === 0)
            {
                $cols[$cellnum] = $cell->getValue();
            }
            else
            {
                if(is_array($retval[$rownum-1]))
                    $retval[$rownum-1] += array($cols[$cellnum] => $cell->getValue());
                else
                    $retval[$rownum-1] = array($cols[$cellnum] => $cell->getValue());
            }

            $cellnum++;
        }

        $rownum++;
    }

    unset($reader, $phpObject, $sheet);

    return $retval;
}

文件的第一行是列名。

无论如何,我已经通过var_dumping 前几行并检查数组长度来确认它正确加载了数据。

这就是问题所在。只要我添加这一行:

foreach($data as $i => $row) {};

Apache 在达到这一点时会崩溃:

那是怎么回事? PHP 不能通过大型关联数组处理循环吗?任何解释性答案将不胜感激。如果我可以提供其他信息,请发表评论


我在 Windows 7、Intel i5 处理器、4GB RAM 上运行 XAMPP。我已将 php.ini 中的 memory_limit 增加到 512MB,这已经绰绰有余(当它设置为 128MB 时,它曾经给我一个内存错误)。该脚本包括 PHPExcel.php 和 dBug.php。二维数组只填充字符串,没有时髦的 PHPExcel 数据类型。

我正在运行 PHP 5.3.1 版

加载的模块:核心mod_win32 mpm_winnt http_core mod_so mod_actions mod_alias中mod_asis mod_auth_basic mod_auth_digest的mod_authn_default mod_authn_file模块mod_authz_default mod_authz_groupfile mod_authz_host mod_authz_user mod_cgi一样的mod_dav mod_dav_fs可以mod_dav_lock的mod_dir mod_env mod_headers中mod_include负责mod_info mod_isapi mod_log_config mod_mime mod_negotiation模块的mod_rewrite mod_setenvif mod_ssl的mod_status的mod_autoindex_color mod_php5的mod_perl mod_apreq2 EM>

phpinfo() screenshot

【问题讨论】:

    标签: php crash foreach associative-array phpexcel


    【解决方案1】:

    简单地取消设置 $phpObject 和 $sheet 将没有任何效果。这些包含循环对象引用,在 PHP 中不能很好地清理它们,因此除非您首先破坏这些引用,否则它们不会被取消设置。这可以使用

    $phpObject->disconnectWorksheets();
    

    如开发人员文档第 4.3 节所述(“从内存中清除工作簿”)。

    摆脱迭代器循环来填充数组,并使用 PHPExcel 的内置方法。

    return $sheet->toArray(); 
    

    您还可以将以下参数传递给 toArray() 方法:

    * @param  mixed    $nullValue            Value returned in the array entry if a cell doesn't exist
    * @param  boolean  $calculateFormulas    Should formulas be calculated?
    * @param  boolean  $formatData           Should formatting be applied to cell values?
    * @param  boolean  $returnCellRef        False - Return a simple array of rows and columns indexed by number counting from zero
    *                                        True - Return rows and columns indexed by their actual row and column IDs
    

    它不会像您自己的循环那样为您提供相当多的关联数组,但它会比您的循环更快,内存效率更高。

    【讨论】:

    • 这有点奇怪,因为我的代码几乎是 PHPExcel 示例的直接副本。无论如何,我之前得出的结论是,我的问题不是由内存不足引起的。此外,在 PHPExcel 的文档中很难找到东西,所以我敢肯定,要弄清楚如何将数据作为关联数组返回,并将值分配给具有正确列名的变量,这将是一场噩梦。但除此之外,感谢您的优化提示。
    • 我很抱歉,文档并不像您想的那样容易尝试并找到您想要的方式(令人惊讶的是有多少人甚至没有找到文档)......但是有限制一个人在空闲时间可以做什么。
    • 我假设你是作者 :-) 我无意冒犯。大多数 API 或库文档都是 Internet 上 HTML 中的类反射文档的形式。这样做的好处是每个函数都有记录并且很容易找到。我强烈建议你这样做。此外,整个文档将使用您的 cmets 自行创建(当然,在第三方应用程序的帮助下)。我假设您当然已经知道这一点:-P 只是指出它会有多有用。 (感谢您编写 PHPExcel!)
    • 虽然文档在 Internet 上不可用,但在生产下载中提供了完整的 API 文档,使用 PHPDocumentor 构建...在 /Documentation/API 目录中,因此每个方法都记录在案...所以是的,我知道这一点
    • 另一种可能性... Apache 会在脚本完成执行之前超时吗?通常,Apache 有 5 分钟的超时限制
    【解决方案2】:
    1. 为 PHP 添加一些内存。

    2. 在 PHP 中返回一个大数组并不是太幸运。你应该改用:

      function loadFromXLS($filepath,&$retval) {
        ...
      }
      

      那么你应该删除 $retval = array(); 并把它放在这个函数的调用之前。

    【讨论】:

    • 我现在已将内存限制设置为 1024M。加载 XML 文件所需的空间小于 256M。我还更改了函数以使用指针输入,但程序仍然崩溃。我现在确信问题不在于内存不足。
    【解决方案3】:

    更新你的 php 安装..

    【讨论】:

    • 您愿意详细说明吗?这是一个已知的问题?我正在使用 PHP 5.3。我会检查确切的版本并将其添加到问题中
    • 我不知道有任何此类已知问题,Codemonkey。不过,这可能是个不错的建议。
    • 如果它在 95% 的情况下崩溃是由于一些 PHP 错误。如果我是你,我会尝试更新 php 版本
    • 嗯,事实证明你是对的。我升级到 PHP 5.3.5,问题就消失了。如此无聊的结果:-P 现在我将无法在较旧的 PHP 安装上解决这个问题
    • 下次我们可能会尝试先重新启动 Windows。
    【解决方案4】:

    尝试完全省略 apache,只从命令行运行脚本,看看会发生什么

    【讨论】:

    • 你真的必须一次将所有数据添加到mysql,还是你可以单独添加每一行并丢弃数据?我们至少可以看看它是在设置阶段还是在某个特定的入口处死掉。
    • 我认为这无关紧要,因为 PHP 应该 能够非常轻松地处理这样的字符串(大约 500 000 个字符)。如果不是这样,它应该输出一个托管的、可理解的错误消息,而不是崩溃。因此,我想深入了解这一点,因为将来我可能会遇到类似的问题,您的解决方案不是一个选项。
    猜你喜欢
    • 2023-03-26
    • 2014-09-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-15
    • 1970-01-01
    相关资源
    最近更新 更多