PHP Apache 在执行存储过程时崩溃

PHP Apache crashes while executing a STORED PROCEDURE

问题

当我执行以下代码时(我正在调用一个带有 5 个 IN 参数和 1 个 OUT 参数的存储过程)

$conn->query("SET @res = ''");

$mysqli=$conn;
if (!($stmt = $mysqli->prepare("CALL retrieve_matches(5,3, 16, 2, false, @res)"))) {
    echo "Prepare failed: (" . $mysqli->errno . ") " . $mysqli->error;
}

if (!$stmt->execute()) {
    echo "Execute failed: (" . $stmt->errno . ") " . $stmt->error;
}

do {
    if ($res = $stmt->get_result()) { //Apache crash on this call
        printf("---\n");
        var_dump(mysqli_fetch_all($res));
        mysqli_free_result($res);
    } else {
        if ($stmt->errno) {
            echo "Store failed: (" . $stmt->errno . ") " . $stmt->error;
        }
    }
} while ($stmt->more_results() && $stmt->next_result());

apache 因错误而崩溃:

AH00428: Parent: child process 9628 exited with status 255 -- Restarting.

我试过的

  1. 此代码运行良好,并且正确返回结果:
$conn->query("SET @res = ''");
$res=$conn->query("CALL retrieve_matches(5,3, 16, 2, false, @res)");
var_dump($res->fetch_assoc());

注意:如果我只是更改存储过程输入中的数字,则上面导致 Apache 崩溃的相同代码可以正常工作,例如:

if (!($stmt = $mysqli->prepare("CALL retrieve_matches(5,6, 16, 2, false, @res)"))) {
  1. 从 MySQl workbench 开始尝试,两个调用都正常。
  2. 我在 WIN7 Enterprise 64b 上使用 WAMP 64b,我尝试使用 WAMP 32b 但遇到了同样的问题。
  3. 我检查了 windows 事件,发现 httpd.exe 崩溃是由 php5ts.dll
  4. 引起的
  5. 如果您在 google 上搜索 "httpd.exe php5ts.dll",您会发现很多人都在解决这个问题。但是我没有找到 WAMP 的解决方案...
  6. 尝试使用 AMPPS,问题完全相同

PHP 错误日志

[10-Jul-2015 15:30:03 UTC] PHP Warning:  PHP Startup: Unable to load dynamic library 'C:/Program Files/wamp/bin/php/php5.5.12/ext/php_ldap.dll' - Impossibile trovare il modulo specificato.

 in Unknown on line 0

[10-Jul-2015 15:30:04 UTC] PHP Warning:  PHP Startup: Unable to load dynamic library 'C:/Program Files/wamp/bin/php/php5.5.12/ext/php_intl.dll' - Impossibile trovare il modulo specificato.

APACHE 错误日志:

[Tue Jul 14 15:02:13.038276 2015] [mpm_winnt:notice] [pid 7044:tid 404] AH00428: Parent: child process 9448 exited with status 255 -- Restarting.
[Tue Jul 14 15:02:13.324305 2015] [mpm_winnt:notice] [pid 7044:tid 404] AH00455: Apache/2.4.9 (Win32) PHP/5.5.12 configured -- resuming normal operations
[Tue Jul 14 15:02:13.329306 2015] [mpm_winnt:notice] [pid 7044:tid 404] AH00456: Apache Lounge VC11 Server built: Mar 16 2014 12:13:13
[Tue Jul 14 15:02:13.329306 2015] [core:notice] [pid 7044:tid 404] AH00094: Command line: 'C:\Program Files\wamp\bin\apache\apache2.4.9\bin\httpd.exe -d C:/Program Files/wamp/bin/apache/apache2.4.9'
[Tue Jul 14 15:02:13.352308 2015] [mpm_winnt:notice] [pid 7044:tid 404] AH00418: Parent: Created child process 3140
[Tue Jul 14 15:02:14.528388 2015] [mpm_winnt:notice] [pid 3140:tid 332] AH00354: Child: Starting 64 worker threads.

我真的迷路了,我应该在哪里寻找问题? 非常感谢您的帮助

编辑

我意识到存储过程 "retrieve_matches" 正在根据更改的值调用不同的存储过程。我调试了导致 Apache 崩溃的存储过程 "retrieve_standalone"。这个程序正在做 select/insert,我检查了插入是否正确。 但是在 "retrieve_standalone" 我以一种奇怪的方式使用光标:

declare bNoMoreRows bool default false;
declare tmp_cursor cursor for
    select comp_id from to_match; -- to_match is a temporary table
declare continue handler for not found set bNoMoreRows := true;

如果我不打开游标

open tmp_cursor;

一切正常!!所以我想我现在发现了问题:我该如何解决它?

我认为您混淆了 $conn$stmt。这是我所拥有的。 (它将结果收集到 $out 中。)

if ($conn->multi_query($call)) {
    $raa = array();
    do {
        /* store first result set */
        if ($result = $conn->use_result()) {
            while ($row = $result->fetch_row()) {
                $raa[] = $row;
                printf("+\n");
            }
            $result->close();
        }
        $out[] = $raa;
        /* print divider */
        if ($conn->more_results()) {
            printf("-----------------\n");
        }
    } while ($conn->next_result());
}
else
    echo "err";

如果 php5ts.dll 或 Apache 的任何其他部分在您每次 运行 一个简短的脚本时以可重现的方式崩溃,您很可能遇到了 php 中的错误。特别是如果你 运行 一个最新的 apache 和从 php.net 下载的最新 php 版本,你很有可能得到 PHP 团队的支持。因此,通读 https://bugs.php.net/how-to-report.php, see if you can reproduce the crash when running the script without apache (in a separate script on the cli using php.exe) and generate a backtrace 然后您可以将其作为错误提交。

像这样的错误有时可以解决,但很少能从脚本的上下文中修复。 PHP 永远不应使网络服务器崩溃,如果它重复出现这种情况,则应提交错误以便修复

我找到了如何重现它,我相信这是一个 php/apache 错误

PHP代码:

if (!($stmt = $mysqli->prepare("CALL test()"))) {
    echo "Prepare failed: (" . $mysqli->errno . ") " . $mysqli->error;
}

if (!$stmt->execute()) {
    echo "Execute failed: (" . $stmt->errno . ") " . $stmt->error;
}

do {
    if ($res = $stmt->get_result()) {
        printf("---\n");
        //var_dump(mysqli_fetch_all($res));
        var_dump($res->fetch_assoc());
        mysqli_free_result($res);
    } else {
        if ($stmt->errno) {
            echo "Store failed: (" . $stmt->errno . ") " . $stmt->error;
        }
    }
} while ($stmt->more_results() && $stmt->next_result());

存储过程代码:

CREATE DEFINER=`root`@`localhost` PROCEDURE `test`()
BEGIN
declare test_var varchar(100) default "ciao";
declare bNoMoreRows bool default false;
declare test_cursor cursor for
    select id from tmp_folder;
declare continue handler for not found set bNoMoreRows := true;

create temporary table tmp_folder select "test" as id;

open test_cursor;

fetch test_cursor into test_var;

close test_cursor;

select test_var;

drop temporary table if exists tmp_folder;

END

打开的错误: 阿帕奇:https://bz.apache.org/bugzilla/show_bug.cgi?id=58136

Php: https://bugs.php.net/bug.php?id=70073

显然有一点推卸责任 - 即 scaricabarile - 在 PHP 人和 MySQLi 人之间。

似乎正在发生的事情是 MySQLi 以 "improper" 方式做出反应,return 一个意外值(我敢打赌它是 NULL)到 PHP, 适当的核心转储。这种行为 is documented 和来自 PHPland 的判决是:"Not a [PHP] bug"。在他们这边,MySQLi 人员坚持认为 PHP 没有正确检查 returned 结果。 'improper' 结果取决于 你的 查询。

所以我要冒险,假设你的问题与 "communication difficulties" 相同,那么问题就变成了:"Your query forces MySQLi to drop/reconnect"。为什么?显然(某些)存储过程 require mysqli_multi_query in order to behave properly.

并且mysqli_multi_querymysqli_prepare不兼容。

所以我建议在不准备查询的情况下尝试,并 运行 使用 mysqli_multi_query

$conn->query("SET @res = ''");
$conn->multi_query("CALL retrieve_matches(5,3, 16, 2, false, @res)");

do {
    if ($result = $conn->store_result()) {
        while ($row = $result->fetch_row()) {
            var_dump($row);
        }
        $result->free();
    }
} while ($conn->more_results() && $conn->next_result());

使用此代码,您的测试用例如预期的那样得到了我,

array(1) {
  [0] =>
  string(4) "test"
}

尝试更改您的服务器堆栈。在不同的环境中升级 WAMP 或 运行 您的代码。这个问题表明 WAMP 中存在一个错误,会导致 Apache 抛出 255 错误

php -> mysql_connect -> child process exited with status 255 -- Restarting

另一个在这里咬灰尘。刚刚加入以提供解决方法。

带光标的 SP 仅在使用 prepare() 时崩溃。

如果您将其更改为 multi_query() 调用,它将起作用。一个简单的可重现 POC

<?php
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli = mysqli_connect('127.0.0.1','user','pass','db');

$mysqli->query("DROP PROCEDURE IF EXISTS mysqli_crash");
$mysqli->query("CREATE PROCEDURE mysqli_crash ()
BEGIN
    DECLARE col TEXT;
    DECLARE res CURSOR FOR SELECT 1 from dual;
    OPEN res;
    FETCH res INTO col;
    CLOSE res;
    SELECT 1 from dual;
END;");

echo  "--- multi_query ---\n";

$mysqli->multi_query("call mysqli_crash()");
$result = $mysqli->store_result();
echo json_encode($result->fetch_all(MYSQLI_ASSOC)),"\n";
$mysqli->next_result();

echo  "--- prepare ---\n";

$stmt = $mysqli->prepare("call mysqli_crash()");
$stmt->execute();
$result = $stmt->get_result();
echo json_encode($result->fetch_all(MYSQLI_ASSOC)),"\n";
$stmt->next_result();

是的,prepare 是首选,但如果您的存储过程使用游标,则别无选择。