← Home ← Codex ← DEBT
Browse by Category
+ added · updated 7d
← Back to glossary

Type Safety in PHP (strict_types & Static Analysis)

Code Quality PHP 7.4+ Intermediate
debt(d5/e7/b7/t7)
d5 Detectability Operational debt — how invisible misuse is to your safety net

Closest to 'specialist tool catches it' (d5). The detection_hints.tools list is phpstan, psalm, and rector — all specialist static analysis tools, not default linters. The code_pattern confirms the problem is invisible without these tools: 'mixed types everywhere; type errors only caught at runtime.' Nothing in the default PHP runtime or a basic linter surfaces these issues.

e7 Effort Remediation debt — work required to fix once spotted

Closest to 'cross-cutting refactor across the codebase' (e7). The quick_fix is 'Work towards PHPStan level 8 incrementally — fix one level per sprint, starting from level 3.' This is explicitly a multi-sprint, iterative effort that touches return types, parameter types, and internal methods across the entire codebase. The common_mistakes confirm the problem decays from the inside out, affecting every method and file without return types. This is well beyond a single-file fix.

b7 Burden Structural debt — long-term weight of choosing wrong

Closest to 'strong gravitational pull' (b7). The term applies_to all three contexts (web, cli, queue-worker) and is tagged as a quality principle for php8. Absent type safety, every future change is shaped by untrusted types — callers cannot rely on contracts, internal methods silently degrade, and static analysis is blocked. This exerts a persistent pull on all work streams, not just one component.

t7 Trap Cognitive debt — how counter-intuitive correct behaviour is

Closest to 'serious trap — contradicts how a similar concept works elsewhere' (t7). The misconception is precise: developers assume declare(strict_types=1) makes the whole application type-safe, but it only affects call boundaries in that one file and has no effect on calls from other files without strict mode. This directly contradicts the intuitive expectation that a single declaration enforces a global guarantee, and it contradicts how strict typing works in languages like TypeScript or Java where type enforcement is global.

About DEBT scoring →

Also Known As

type checking strict types type enforcement

TL;DR

Combining strict_types=1, typed properties, return types, union types, and PHPStan/Psalm to catch type errors at analysis time rather than runtime.

Explanation

PHP's type system has evolved substantially: scalar type declarations (PHP 7.0), return types (7.0), nullable types (7.1), typed class properties (7.4), union types (8.0), intersection types (8.1), and DNF types (8.2). Declaring strict_types=1 at the top of a file makes PHP enforce declared types strictly — no silent coercions. But strict_types only applies to function calls within that file. The real force multiplier is static analysis: PHPStan and Psalm infer types through the code graph, catching: passing null where not nullable, incorrect return types, impossible conditions, and missing match arms. Aim for PHPStan level 6+ and full property/return type coverage. Use /** @var Type */ docblocks only where inference is insufficient.

Common Misconception

declare(strict_types=1) makes PHP fully type-safe. Strict types enforce scalar type coercion at call boundaries in that file only — it does not affect calls from other files without strict mode, and does not prevent null or wrong-class objects from passing typed parameters.

Why It Matters

Type safety enforced by PHP's type declarations and strict_types catches a whole category of bugs at development time rather than production runtime — wrong argument types become errors, not silent corruptions.

Common Mistakes

  • Not declaring return types — the method contract is implicit and callers cannot trust the type.
  • Using mixed or omitting types on internal methods — type safety degrades from the inside out.
  • Not enabling strict_types=1 — PHP silently coerces types without it, hiding mismatches.
  • Relying on PHPDoc @return annotations for type safety — they are not enforced at runtime.

Code Examples

✗ Vulnerable
// No type declarations — silent type coercion hides bugs:
function calculateDiscount($price, $percent) {
    return $price * ($percent / 100); // '10' * (0.1) = 1.0 — works, but by accident
}
calculateDiscount('ten', '20%'); // No error, returns 0

// With types:
function calculateDiscount(float $price, float $percent): float { /* ... */ }
✓ Fixed
<?php declare(strict_types=1);

class Money {
    public function __construct(
        public readonly int $amount,
        public readonly string $currency,
    ) {}

    public function add(Money $other): Money {
        if ($this->currency !== $other->currency) {
            throw new CurrencyMismatchException();
        }
        return new Money($this->amount + $other->amount, $this->currency);
    }
}

Added 15 Mar 2026
Edited 22 Mar 2026
Views 136
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings T 0 pings W 1 ping T 1 ping F 1 ping S 0 pings S 2 pings M 0 pings T 0 pings W 0 pings T 1 ping F 1 ping S 4 pings S 2 pings M 0 pings T 0 pings W 0 pings T 1 ping F 0 pings S 0 pings S 0 pings M 1 ping T 1 ping W 0 pings T 3 pings F 1 ping S 1 ping S 0 pings M 0 pings T 0 pings W
No pings yet today
No pings yesterday
Perplexity 44 ChatGPT 19 Amazonbot 15 Google 8 Scrapy 8 Ahrefs 4 Unknown AI 3 Majestic 2 Claude 2 Bing 1 Meta AI 1 SEMrush 1 PetalBot 1
crawler 105 crawler_json 4
DEV INTEL Tools & Severity
🟠 High ⚙ Fix effort: High
⚡ Quick Fix
Work towards PHPStan level 8 incrementally — fix one level per sprint, starting from level 3 which catches the most impactful bugs without too many false positives
📦 Applies To
PHP 7.4+ web cli queue-worker
🔗 Prerequisites
🔍 Detection Hints
PHPStan below level 5; mixed types everywhere; functions without return types; type errors only caught at runtime
Auto-detectable: ✓ Yes phpstan psalm rector
⚠ Related Problems
🤖 AI Agent
Confidence: High False Positives: Low ✓ Auto-fixable Fix: Low Context: File
CWE-704


✓ schema.org compliant