PDOStatement::rowCount()
debt(d5/e1/b3/t7)
Closest to 'specialist tool catches it' (d5). The detection_hints list phpstan as the tool, and the code_pattern is 'rowCount() used after SELECT statement' — this is a static analysis catch, not a compiler error or default linter, placing it squarely at d5.
Closest to 'one-line patch or single-call swap' (e1). The quick_fix is explicit: replace rowCount() with fetchAll() + count() or SELECT COUNT(*) — a direct single-call substitution at each call site.
Closest to 'localised tax' (b3). The misuse is scoped to specific query call sites (web and cli contexts), not load-bearing across the whole architecture. Each instance is a local fix, and the broader codebase is unaffected once corrected.
Closest to 'serious trap' (t7). The misconception field states it 'works reliably for SELECT queries' — it works on MySQL by accident, giving false confidence, but silently breaks on other drivers. This contradicts developer intuition because the method name implies it counts rows returned, not rows changed, and the MySQL-specific behavior masks the bug in the most common environment.
Also Known As
TL;DR
Explanation
rowCount() returns an integer reflecting rows modified by DML statements. For SELECT queries, the behaviour is driver-dependent — MySQL returns 0, making it unsuitable for counting result rows. Use SELECT COUNT(*) or fetchAll() + count() to count SELECT results. rowCount() is useful for detecting whether an UPDATE actually changed a row vs matched but left it unchanged.
Watch Out
Common Misconception
Why It Matters
Common Mistakes
- Using rowCount() to check if a SELECT found results — returns 0 on many drivers even with results.
- Expecting rowCount() to return rows matched — it returns rows changed. UPDATE with identical values returns 0.
- Not checking rowCount() after DELETE to confirm a row was actually removed.
Avoid When
- Never use rowCount() to check if a SELECT returned results — use fetch() === false or count(fetchAll()) instead.
When To Use
- Use after DELETE to verify a row was actually removed.
- Use after UPDATE to distinguish 'row not found' from 'row found but value unchanged'.
Code Examples
// Wrong — rowCount() on SELECT is not portable
$stmt = $pdo->prepare('SELECT * FROM users WHERE email = ?');
$stmt->execute([$email]);
if ($stmt->rowCount() > 0) { // unreliable — may return 0 even with results
$user = $stmt->fetch();
}
// Correct: count SELECT results
$stmt = $pdo->prepare('SELECT * FROM users WHERE active = 1');
$stmt->execute();
$users = $stmt->fetchAll();
$count = count($users); // reliable
// Correct: check rows affected by UPDATE
$stmt = $pdo->prepare('UPDATE users SET last_seen = NOW() WHERE id = ?');
$stmt->execute([$userId]);
if ($stmt->rowCount() === 0) {
// User not found or value unchanged
}