PHP Data Types
debt(d5/e3/b5/t7)
Closest to 'specialist tool catches it' (d5). The detection_hints list phpstan, psalm, and phpcs — all specialist static analysis tools that catch type misuse (missing return types, mixed coercions, loose comparisons). These are not default linters bundled with PHP itself, so the common case is not caught automatically without configuring these tools.
Closest to 'simple parameterised fix' (e3). The quick_fix describes adding return type declarations, switching int/bool/float usage, and enabling strict_types=1 — these are mostly local, per-file changes. Some files may need touching but it does not typically require cross-cutting architectural rework. Slightly above e1 because enabling strict_types=1 must be done per file and fixing loose comparisons or float-for-currency can require reviewing multiple call sites.
Closest to 'persistent productivity tax' (b5). The applies_to covers web, cli, and queue-worker contexts — the entire PHP execution model. Without strict_types and proper type declarations, every function boundary and comparison in the codebase carries latent coercion risk. This imposes an ongoing cognitive tax on maintainers across many work streams but does not fully define the system's architectural shape.
Closest to 'serious trap' (t7). The misconception field states developers believe PHP type declarations are optional extras, not understanding that strict_types=1 makes PHP behave like a strictly typed language at call boundaries. The common_mistakes reinforce this: '0' == false == null == 0 under loose comparison directly contradicts how most typed languages behave, and silent coercion of '42' to 42 without strict_types is a well-documented gotcha that contradicts expectations from other language ecosystems.
Also Known As
TL;DR
Explanation
PHP is dynamically typed with eight primitive types: bool, int, float (double), string, array, object, callable, and null. PHP 8.0 formalised mixed (any type) and never (no return). Type coercion rules are nuanced: '1' == 1 is true, '0' == false is true, but null == false and null == 0 are also true. PHP 8.0 changed 0 == 'foo' to false (was true in PHP 7). Typed properties (PHP 7.4+), union types (8.0), and strict_types enforce types at runtime. Understanding coercion rules is critical for security (authentication bypass via type juggling) and correctness.
Common Misconception
Why It Matters
Common Mistakes
- Not enabling strict_types=1 — PHP silently coerces '42' to 42 for int parameters without it.
- Using == to compare values of different types — '0' == false == null == 0 in loose comparison.
- Forgetting that array is a first-class type in PHP, not an object — json_encode behaviour differs.
- Using float for currency — floating-point precision errors accumulate; use integer cents or bcmath.
Code Examples
// Loose comparison type confusion:
var_dump(0 == 'a'); // true in PHP 7, false in PHP 8 — changed!
var_dump('' == false); // true
var_dump('0' == false); // true
var_dump(0 == null); // true
// Use === always for predictable comparisons
// PHP 8 scalar types:
$int = 42; // PHP_INT_MAX = 9223372036854775807 on 64-bit
$float = 3.14; // IEEE 754 double — avoid for money (use int cents)
$str = 'hello'; // byte string — mb_* functions for Unicode
$bool = true;
// Compound:
$arr = [1, 'two', 3.0]; // ordered map
$assoc = ['key' => 'value'];
$obj = new stdClass();
$null = null;
// PHP 8 type system additions:
// union: int|string
// intersection: Iterator&Countable (8.1)
// never, void, mixed
// Enums with backed types (8.1)
// Type juggling changes in PHP 8:
var_dump(0 == 'foo'); // false in PHP 8 (was true in PHP 7)
var_dump(0 == ''); // false in PHP 8 (was true in PHP 7)