PDO Error Handling
debt(d5/e1/b5/t7)
Closest to 'specialist tool catches it' (d5), because the detection_hints list phpstan and semgrep — both specialist static analysis tools — as the means to catch missing ERRMODE_EXCEPTION on PDO construction. The pattern is invisible at runtime in default silent mode, so it won't be caught by a compiler or default linter, but semgrep/phpstan rules can flag the missing attribute.
Closest to 'one-line patch or single-call swap' (e1), because the quick_fix is explicitly a single addition to the PDO constructor options array — PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION — which is a one-line change at the point of instantiation.
Closest to 'persistent productivity tax' (b5), because PDO is used in both web and cli contexts (per applies_to) and the error handling mode shapes how every database interaction behaves across the codebase. If ERRMODE_SILENT is in effect, every query call site is implicitly affected, and any future maintainer must reason about silent failures. It does not quite reach b7 because fixing it at the construction point is localized, but the reach of the silent failure mode spans many work streams.
Closest to 'serious trap — contradicts how a similar concept works elsewhere' (t7), because the misconception is that checking execute() return values is sufficient — a pattern that works in many other DB layers and error-handling paradigms. The default ERRMODE_SILENT actively suppresses errors without any visible signal, directly contradicting the expectation that failed operations surface as errors. Developers familiar with exceptions-by-default frameworks (or other DB drivers) will assume failures are loud, making this a serious behavioral contradiction.
Also Known As
TL;DR
Explanation
PDO::ERRMODE_SILENT (default) suppresses all errors — queries fail silently. PDO::ERRMODE_WARNING triggers PHP warnings. PDO::ERRMODE_EXCEPTION throws PDOException on any database error, enabling try/catch and preventing silent data corruption. Always set ERRMODE_EXCEPTION. PDOException extends RuntimeException and exposes errorInfo() with the SQLSTATE code, driver error code, and driver message.
How It's Exploited
Common Misconception
Why It Matters
Common Mistakes
- Forgetting to set ERRMODE_EXCEPTION — silent failures corrupt data without any visible error.
- Catching PDOException and swallowing it with an empty catch block.
- Logging the exception message but not the SQLSTATE or full errorInfo array, losing diagnostic detail.
Avoid When
- Never expose PDOException messages directly in HTTP responses — they contain table names, column names, and query structure.
When To Use
- Always set ERRMODE_EXCEPTION immediately after constructing the PDO object.
- Catch PDOException at the service layer, log the full error, and rethrow as a domain exception.
Code Examples
// ERRMODE_SILENT — default — fails invisibly
$pdo = new PDO($dsn, $user, $pass);
$stmt = $pdo->prepare('INSERT INTO orders VALUES (?, ?)');
$stmt->execute([$userId, $total]); // returns false silently on error
// $total was never saved — you'll never know
$pdo = new PDO($dsn, $user, $pass, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
]);
try {
$stmt = $pdo->prepare('INSERT INTO orders (user_id, total) VALUES (?, ?)');
$stmt->execute([$userId, $total]);
} catch (PDOException $e) {
// Log full error detail — never expose to user
error_log('DB error: ' . $e->getMessage() . ' SQLSTATE:' . $e->getCode());
throw new RuntimeException('Order could not be saved', 0, $e);
}