PDO Introduction — Replacing mysql_* with Prepared Statements
debt(d5/e3/b3/t7)
Closest to 'specialist tool catches it' (d5). The detection_hints list semgrep, rector, and phpstan — all specialist/SAST tools that can detect interpolated variables in PDO::query() calls or legacy mysql_* usage. These are not default linters but require deliberate configuration and tooling setup.
Closest to 'simple parameterised fix' (e3). The quick_fix describes replacing PDO::query($sql) with string interpolation with PDO::prepare($sql)->execute($params) — a consistent pattern replacement. While it may touch multiple call sites, each individual fix is a small mechanical refactor rather than a deep architectural change, placing it squarely at e3.
Closest to 'localised tax' (b3). Although PDO applies to web, cli, and queue-worker contexts, the burden is localised to database-access code. Correctly migrating to prepared statements is a one-time effort that doesn't impose ongoing structural weight on the entire codebase — future maintainers inherit clean, standard patterns rather than a persistent productivity tax.
Closest to 'serious trap' (t7). The misconception field states exactly this trap: developers believe PDO prevents SQL injection automatically regardless of usage, but PDO::query() with string interpolation is still fully vulnerable. This directly contradicts the reasonable expectation that 'switching to PDO' is sufficient for safety — a serious trap that contradicts assumptions drawn from the marketing around PDO as a 'safe' API.
Also Known As
TL;DR
Explanation
Before PDO, PHP developers used database-specific extensions: mysql_* for MySQL, pg_* for PostgreSQL. None supported named prepared statements. PDO introduced a consistent interface: PDO::prepare() creates a parameterised query, execute() runs it with bound values — the driver handles escaping internally. PDO also introduced named parameters (:name) alongside positional (?) parameters. The extension became bundled with PHP 5.1 and is the standard database interface for all modern PHP.
Common Misconception
Why It Matters
Common Mistakes
- Using PDO::query() with interpolated variables instead of prepare/execute
- Not checking the return value of execute() for false
- Using ATTR_EMULATE_PREPARES without understanding it can fall back to string escaping
Code Examples
// mysql_ extension approach:
$result = mysql_query("SELECT * FROM users WHERE id=$id");
$row = mysql_fetch_assoc($result); // SQL injection risk + removed in PHP 7
// PDO with prepared statements:
$stmt = $pdo->prepare('SELECT * FROM users WHERE id = :id');
$stmt->execute([':id' => $id]);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
// PDO works across MySQL, PostgreSQL, SQLite, MSSQL