Type Coercion
debt(d5/e3/b5/t7)
Closest to 'specialist tool catches it' (d5). The detection_hints list phpstan, psalm, and rector — all specialist static analysis tools. Default linters won't catch implicit coercion bugs; they require configuring and running these dedicated type-checking tools, placing this squarely at d5.
Closest to 'simple parameterised fix' (e3). The quick_fix is enabling strict_types=1 per file, which is a one-line declaration, but the common_mistakes show multiple patterns (== comparisons, in_array without strict flag, switch statements) that need systematic replacement across a codebase — not a single one-line patch but also not a full architectural rework. Adding strict_types to every file and replacing == with === is a repeatable but multi-site fix, landing at e3.
Closest to 'persistent productivity tax' (b5). The applies_to covers web, cli, and queue-worker contexts — essentially all PHP execution contexts. Without strict_types enforcement, every developer touching the codebase must stay vigilant about coercion rules in comparisons, arithmetic, and function arguments, creating an ongoing cognitive and review tax across many work streams.
Closest to 'serious trap' (t7). The misconception field explicitly states developers believe coercion only affects comparisons, but it silently affects arithmetic, string concatenation, and function arguments. The common_mistakes reinforce this: '0e1234' == '0e5678' being true, in_array(0, ['a','b']) returning true, and switch type coercion all contradict reasonable expectations a developer familiar with other languages would hold — similar to how === vs == works differently than in JavaScript, making this a cross-ecosystem contradiction.
Also Known As
TL;DR
Explanation
PHP's loose comparison operator == performs type juggling — '0e123' == '0e456' evaluates to true because both are treated as scientific notation floats equal to zero. This has historically enabled authentication bypasses in MD5 hash comparisons. Similarly, 0 == 'foo' is true in PHP 7 (though fixed in PHP 8). Use strict comparison (===) for all security-sensitive comparisons, enable strict_types, and be aware of how in_array() and array_search() behave without the strict parameter.
Common Misconception
Why It Matters
Common Mistakes
- Using == with mixed types — '0e1234' == '0e5678' is true (both are 0 in scientific notation).
- in_array() without the strict third parameter — in_array(0, ['a', 'b']) returns true in PHP 7.
- switch statements coercing types — switch('0') matches case false.
- Not enabling strict_types — PHP silently coerces string '42' to int 42 without it.
Avoid When
- Avoid relying on implicit coercion for security checks — '0' == false == null under loose comparison.
- Do not use type coercion as a sanitisation strategy — cast after validating, not instead of validating.
When To Use
- Understand PHP's type coercion rules when working with legacy code that omits strict_types.
- Use explicit casting when you intentionally need a value in a specific type — (int)$_GET['page'].
Code Examples
if (md5($input) == $storedHash) { /* bypass with '0e...' hashes */ }
if (hash_equals($storedHash, md5($input))) { /* constant time + strict */ }