php 8.1+中mysqli_connect()抛异常导致die()不执行,因默认启用mysqli_report_strict,连接失败直接中断脚本;须用try/catch捕获mysqli_sql_exception,而非依赖if(!$conn)判断。

mysqli_connect() 报错后 die() 不执行,是因为异常没被捕获
PHP 8.1+ 默认启用 MYSQLI_REPORT_ALL,mysqli_connect() 连接失败时直接抛出 mysqli_sql_exception,不会返回 false。所以像 if (!$conn) { die(...) } 这种老写法完全失效——脚本在进入 if 前就已中断,die() 根本没机会运行。
- 别再依赖
$conn->connect_error或mysqli_connect_error()做连接判断,它们在异常模式下不更新或不可靠 -
@抑制符对mysqli_sql_exception无效,硬加只会掩盖问题 - 必须用
try/catch包裹整个连接逻辑,否则就是裸奔
捕获 mysqli 异常并安全输出错误信息
异常对象自带结构化信息,但直接 echo $e->getMessage() 有风险:可能泄露数据库地址、表名、甚至密码(如果错误消息拼接了敏感变量)。
- 只对用户展示泛化提示,例如
"数据库服务暂时不可用",生产环境禁用详细错误输出 - 记录原始异常到日志时,保留
$e->getCode()(如1045、2002)和$e->getMessage(),便于排查 - 对要输出到 HTML 的消息,必须过一遍
htmlspecialchars(),防止 XSS - 示例片段:
try {
$conn = new mysqli($host, $user, $pass, $db);
} catch (mysqli_sql_exception $e) {
error_log(sprintf('[DB] %s [%d]: %s', $_SERVER['REQUEST_URI'], $e->getCode(), $e->getMessage()));
echo '系统繁忙,请稍后再试';
// 开发环境可临时放开:
// echo 'Error: [' . htmlspecialchars($e->getCode()) . '] ' . htmlspecialchars($e->getMessage());
}
PDO 连接异常处理更统一,但要注意默认行为
PDO 默认不开启异常模式,PDO::ATTR_ERRMODE 默认是 PDO::ERRMODE_SILENT,这意味着查询失败也不会抛异常,只会设 $pdo->errorInfo()。必须显式开启:
- 构造 PDO 实例时传入选项:
array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION) - 或者连接后调用:
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION) - 之后所有操作(
query()、prepare()、execute())失败都会抛PDOException,可用同一套try/catch处理 - 注意:PDO 构造函数本身失败(如 DSN 错误、认证失败)也会抛
PDOException,无需额外判断
日志内容要带上下文,不能只记“连接失败”
光记 mysqli_sql_exception: Connection refused 没用。下次排查时你得知道:谁触发的?从哪来的?当时连的是哪个库?
- 日志中至少包含:
$_SERVER['REQUEST_URI']、$_SERVER['REMOTE_ADDR']、当前时间戳、数据库主机和端口 - 避免把密码写进日志——检查你的 DSN 或连接参数是否意外拼接了明文密码
- 用
error_log()写入文件时,确保log_errors = On且error_log = /path/to/your/app.log在 php.ini 中生效 - 如果用 monolog 等日志库,记得设置
context参数传入请求 ID、用户 ID 等字段
最常被忽略的一点:异常处理代码本身也可能出错(比如日志目录不可写、error_log() 被禁用),建议加一层兜底,例如写入系统 syslog 或 fallback 到 /tmp 临时文件。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











