PDO连接失败时try-catch未生效,是因为new PDO()本身抛出PDOException,但若display_errors=Off或set_exception_handler()劫持异常,则catch无法捕获;必须将new PDO()直接置于try块首行并传入含PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION的选项数组。

PDO连接失败时为什么try-catch没生效
因为PDO构造函数本身就会在连接失败时直接抛出PDOException,但前提是——你没在连接前关闭了PHP的致命错误显示或屏蔽了异常。如果try-catch完全没进catch,常见原因只有两个:display_errors = Off导致错误被静默吞掉,或者set_exception_handler()劫持了异常未交还给try块。
注意:PDO连接失败属于“构造时异常”,它不依赖PDO::ATTR_ERRMODE设置——这个选项只影响后续query()、execute()等操作,对new PDO()本身无效。
- 确保
php.ini中display_errors = On(开发环境)且error_reporting = E_ALL - 不要在
try外层提前调用set_exception_handler(),否则catch收不到 - 连接字符串写错(比如
mysql:host=xxx;dbname=xxx漏了;)会触发Parse error,这不属于PDOException,try-catch根本捕获不到
正确写法:new PDO()必须包在try里
new PDO()是唯一可能抛出连接级PDOException的地方,必须放在try块第一行,不能先实例化再设属性。
错误示范:$pdo = new PDO($dsn, $u, $p);$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
——连接失败发生在第一行,第二行根本执行不到。
立即学习“PHP免费学习笔记(深入)”;
正确写法:
try {
$pdo = new PDO($dsn, $username, $password, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);
} catch (PDOException $e) {
// 这里能捕获连接拒绝、用户名密码错、DB不存在等所有连接失败
error_log('PDO connect failed: ' . $e->getMessage());
die('服务暂时不可用');
}
- 选项数组必须作为第4个参数传入
new PDO(),不能用字符串(会报TypeError: Argument #4 ($options) must be of type ?array) -
PDO::ATTR_ERRMODE放这里只为后续查询统一行为,对连接异常无影响,但建议一并设置 - 不要用
@抑制错误——它连PDOException都压制,catch彻底失效
区分连接异常和查询异常的堆栈线索
连接失败的异常堆栈顶部一定是PDO::__construct();而查询失败(如execute())堆栈顶是PDOStatement::execute()或PDO::query()。这点能快速定位问题阶段。
例如:
-
SQLSTATE[08001] SQLDriverConnect: -1 [Microsoft][ODBC Driver 17 for SQL Server]Login timeout expired→ 连接超时,查网络或SQL Server状态 -
SQLSTATE[HY000] [1045] Access denied for user 'xxx'@'yyy'→ 认证失败,核对账号密码及host白名单 -
SQLSTATE[HY000] [2002] Can't connect to local MySQL server through socket '/tmp/mysql.sock'→ MySQL进程没起来或socket路径不对
这些信息全在$e->getMessage()里,别只看$e->getCode()(那是SQLSTATE,不是MySQL原生错误号)。
PHP 7.1下事务提交失败也要单独try
PHP 7.1没有改变PDO事务语义,但容易忽略:commit()和rollback()本身也可能抛PDOException,比如唯一键冲突延迟到提交时才检测(InnoDB的约束检查时机)。它们不在prepare()/execute()里,必须额外包裹。
典型陷阱:
try {
$pdo->beginTransaction();
$stmt = $pdo->prepare("INSERT INTO users (email) VALUES (?)");
$stmt->execute(['test@example.com']);
$pdo->commit(); // ← 这里可能失败!
} catch (PDOException $e) {
$pdo->rollback(); // ← 但如果$pdo->inTransaction()为false,这行会报错
}
-
commit()失败后,连接可能已处于不可恢复状态,不要尝试重试 -
rollback()前必须加if ($pdo->inTransaction())判断,否则空事务下调用会抛新异常 - 不要把
commit()和rollback()塞进同一个try——它们逻辑相反,异常处理策略不同
最常被跳过的其实是连接失败后的降级策略:比如备库切换、重试间隔、或返回结构化错误码而非裸die()。这些不写进catch块,try-catch就只是个摆设。



















