PDO连接失败却没报错,是因为默认不抛异常;必须在new PDO()时通过$options参数设置PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,否则对象无法创建,setAttribute()无效。

PDO 连接失败却没报错,是因为默认不抛异常——这是 PHP7.0+ 里最常被忽略的致命细节。必须显式开启异常模式,否则 PDO 遇到连接失败、SQL 语法错误等,只会返回 false 或静默失败,try-catch 根本捕不到。
必须在 new PDO() 时就设置 ERRMODE_EXCEPTION
很多人用 $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION) 补救,但这是无效的:连接失败时对象都创建不出来,setAttribute() 根本没机会执行。
- 正确写法是把错误模式作为第四个参数(
$options)传入构造函数 -
charset=utf8mb4要写在 DSN 字符串里,不是可选配置,而是防止连接后乱码的必要项 - DSN 中不要漏掉
port(如 MySQL 默认 3306),本地调试时容易因端口不对静默失败
$dsn = 'mysql:host=localhost;port=3306;dbname=test;charset=utf8mb4';
$options = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
];
try {
$pdo = new PDO($dsn, $user, $pass, $options);
} catch (PDOException $e) {
// 此处才能捕获连接失败、认证失败、数据库不存在等错误
error_log('DB connect failed: ' . $e->getMessage());
die('服务暂时不可用');
}
mysqli 报错模式需提前全局启用
mysqli 默认也不抛异常,必须用 mysqli_report() 提前开关,且该设置影响所有后续 mysqli 操作。
- 必须在创建连接前调用,否则新建的
mysqli实例仍走传统返回false路线 - 推荐组合:
MYSQI_REPORT_ERROR | MYSQLI_REPORT_STRICT,缺一不可 - 如果代码里混用了面向过程和面向对象风格(比如
mysqli_connect()+$conn->query()),报告模式可能失效
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
try {
$conn = new mysqli($host, $user, $pass, $dbname, $port);
$conn->set_charset('utf8mb4');
} catch (mysqli_sql_exception $e) {
// 这里能捕获连接超时、拒绝访问、SSL 握手失败等
http_response_code(500);
echo '数据库不可达';
}
PHP7.0+ 扩展缺失会导致“Class not found”而非连接错误
不是所有“连不上”都是网络或密码问题。PHP7.0 起彻底移除了 mysql_* 函数,且 pdo_mysql 和 mysqli 是独立扩展,需手动启用。
立即学习“PHP免费学习笔记(深入)”;
- 检查
phpinfo()页面,确认pdo_mysql和/或mysqli显示为 “enabled”,而非仅列出模块名 - Windows 下看
php.ini是否有extension=php_pdo_mysql.dll;Linux 下确认extension=pdo_mysql.so未被注释 - 修改
php.ini后必须重启 Web 服务器(Apache/Nginx),仅 reload 不生效 - 常见假象:
php -m | grep pdo显示已加载,但 Web SAPI(如 Apache 的 php module)用的是另一份php.ini
生产环境别直接输出 $e->getMessage()
开发时打印 $e->getMessage() 方便调试,但上线后暴露数据库结构、用户名、IP 等信息是严重安全风险。
- 生产环境应统一记录到日志文件,用
error_log()或 Monolog 等库写入 - 用户看到的只能是泛化提示,如“系统繁忙,请稍后再试”,不能含任何技术细节
- 注意
PDOException的getCode()返回的是 SQLSTATE(如HY000),不是 MySQL 错误码(如1045),判断具体错误类型要靠$e->getMessage()字符串匹配或$e->getPrevious()->getCode()
真正麻烦的不是写 try-catch,而是确保异常能被抛出来——PDO 的静默失败、mysqli 的报告模式延迟生效、扩展未加载却报 ClassNotFound,这三类问题在 PHP7.0+ 环境下最容易卡住人。每一步都得验证是否真的触发了异常,而不是以为捕获了、其实根本没抛。



















