Type Safety in PHP (strict_types & Static Analysis)
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);
}
}
Tags
🤝 Adopt this term
£79/year · your link shown here
Added
15 Mar 2026
Edited
22 Mar 2026
Views
99
🤖 AI Guestbook educational data only
|
|
Last 30 days
Agents 1
Perplexity 1
No pings yesterday
Perplexity 41
ChatGPT 15
Amazonbot 14
Google 6
Unknown AI 3
Majestic 2
Ahrefs 1
Also referenced
How they use it
crawler 81
crawler_json 1
Related categories
⚡
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
🔍 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