MySQLi
debt(d5/e7/b5/t5)
Closest to 'specialist tool catches it' (d5). The detection_hints list semgrep, phpstan, and rector — all specialist tools — as capable of flagging mysqli_query() with string interpolation and missing prepared statements. These are not default linter catches; they require deliberate tool configuration and invocation.
Closest to 'cross-cutting refactor across the codebase' (e7). The quick_fix states 'migrate from mysqli to PDO' — swapping database abstraction layers touches every database call site across the codebase, requiring systematic replacement of connection setup, query execution, error handling, and parameter binding patterns throughout multiple files.
Closest to 'persistent productivity tax' (b5). MySQLi applies to both web and cli contexts, and its MySQL-specific API (versus PDO's agnostic interface) imposes ongoing costs: every new query must follow the same procedural/OO style choice, every developer must know its quirks, and it locks the project to MySQL. It affects many work streams but doesn't fully define the system's shape.
Closest to 'notable trap — a documented gotcha most devs eventually learn' (t5). The misconception field documents that developers believe MySQLi is the best PHP database extension, not realising it is MySQL-specific and that PDO is generally preferred. Additionally, common_mistakes show that string interpolation instead of prepared statements is a well-documented but frequently made error — traps most developers encounter and learn over time.
Also Known As
TL;DR
Explanation
MySQLi (mysql improved) replaces the deprecated mysql_* functions removed in PHP 7. It supports both procedural and object-oriented interfaces and provides prepared statements via prepare(), bind_param(), execute(), and get_result(). The type string in bind_param() ('ssi' etc.) specifies s=string, i=integer, d=float, b=blob for each bound parameter. Always use MySQLi or PDO — never the removed mysql_query() function.
Common Misconception
Why It Matters
Common Mistakes
- Using mysqli_query() with string concatenation instead of prepared statements — SQLi vulnerability.
- Not checking return values — mysqli_query() returns false on error; ignoring it hides failures.
- Mixing procedural (mysqli_*) and OO ($mysqli->) styles in the same codebase.
- Not closing connections and statements — in long-running scripts, connection exhaustion becomes a problem.
Code Examples
// String concatenation — SQL injection:
$id = $_GET['id'];
$result = mysqli_query($conn, "SELECT * FROM users WHERE id = $id");
// Safe with prepared statement:
$stmt = $conn->prepare('SELECT * FROM users WHERE id = ?');
$stmt->bind_param('i', $id);
$stmt->execute();
\$db = new mysqli(\$_ENV['DB_HOST'], \$_ENV['DB_USER'], \$_ENV['DB_PASS'], 'myapp');
\$db->set_charset('utf8mb4');
if (\$db->connect_error) throw new \RuntimeException(\$db->connect_error);
// Prepared statement
\$stmt = \$db->prepare('SELECT * FROM users WHERE email = ? AND active = ?');
\$stmt->bind_param('si', \$email, \$active); // s=string, i=integer
\$email = 'alice@example.com';
\$active = 1;
\$stmt->execute();
\$user = \$stmt->get_result()->fetch_assoc();
\$stmt->close();
// Prefer PDO for new projects — more portable and ergonomic