我正在使用以下代码示例:
$ca=$db->query('SELECT * FROM `clients`');
while ($c=$ca->fetch_assoc())
{
try {
$mysqli->query('ALTER TABLE `client_'.$c['id'].'_data` ADD COLUMN `updated` datetime DEFAULT NULL');
}catch (Exception $e) {
// handling/ignoring the exception
}
}
因此在这种情况下 - 有些客户的表已经有“已更新”列,有些则没有。 因此,当其中一个表已经具有此列时,我会按预期收到异常,然后我会忽略该异常。但是,当我忽略第 4 行中的此错误时 - 异常发生后 - 当循环尝试执行下一个
$c=$ca->fetch_assoc
时 - 脚本因相同的异常而崩溃
我收到类似的东西
Fatal error: Uncaught mysqli_sql_exception: Duplicate column .....
Stack trace:
#0 /home/api/www/test.php(2): mysqli_result->fetch_assoc()
#1 {main} thrown in /home/api/www/test.php on line 2
此异常与
$c=$ca->fetch_assoc()
代码没有任何关系。它与循环内的查询有关,我处理了这个异常,但看起来它以某种方式存储在 mysqli 错误堆栈中,下一个 fetch_assoc
看到它并且脚本崩溃了。
当然,我可以防止在循环中使用
fetch_assoc
,但我不想这样做,因为在很多情况下我有这样的构造,并且我不想使用大数组来存储所有结果。
为什么会发生这种情况?我可以采取什么措施来解决这个问题?
我相信您已经发现了 mysqli 中的一个错误。看起来这个 bug 至少从 PHP 7.4 就已经存在了,直到现在还没有修复。
要理解这一点,您需要了解 mysqli 将最后执行的命令的错误代码和消息保留在 mysqli 类的“属性”中。当启用异常模式时(从 PHP 8.1 开始默认),每个 mysqli 函数都会检查错误代码,如果它不为 0,则会抛出异常。这意味着 mysqli 在内部自动执行以下操作:
$mysqli->query(/*...*/);
if ($mysqli->errno !== 0) {
throw new mysqli_sql_exception($mysqli->error, $mysqli->errno);
}
存储在这些属性中的值在每个命令之前都会被清除。
当涉及到
fetch_*
函数时,它们只会在无缓冲模式下向服务器执行命令。无缓冲模式会阻塞连接,因此无法创建这样的情况。唯一可能的错误情况是服务器停止响应获取请求。 PHP 7.4.26 中的 mysqli 添加了一个修复,以便在发生这种情况时抛出异常。但缓冲获取永远不会出现错误情况。这就是我们最终遇到的情况:我们有代码在函数中抛出异常,但在执行之前没有清除错误代码。
对此的潜在解决方案可能是将所有获取操作视为命令并每次都清除错误状态。您提供的代码示例不是很人为,但除了您已经提到的方法之外,我看不到一种简单的方法可以为您提供临时修复:将所有行加载到数组中并丢弃 mysqli 结果。我相信它不应该使用任何额外的内存,因为所有行都已经在 PHP 内存中。如果您确实不想这样做,那么您可以尝试一些深奥的解决方案来清除错误状态,例如在您的
$mysqli->query('DO 1');
块中执行
catch
。