{
    "slug": "type_safety",
    "term": "Type Safety in PHP (strict_types & Static Analysis)",
    "category": "quality",
    "difficulty": "intermediate",
    "short": "Combining strict_types=1, typed properties, return types, union types, and PHPStan/Psalm to catch type errors at analysis time rather than runtime.",
    "long": "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.",
    "aliases": [
        "type checking",
        "strict types",
        "type enforcement"
    ],
    "tags": [
        "php8",
        "quality",
        "principles",
        "static-analysis"
    ],
    "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."
    ],
    "when_to_use": [],
    "avoid_when": [],
    "related": [
        "phpstan_levels",
        "psalm_annotations",
        "strict_types",
        "union_types"
    ],
    "prerequisites": [
        "type_declarations",
        "phpstan_levels",
        "strict_types"
    ],
    "refs": [
        "https://www.php.net/manual/en/language.types.declarations.php",
        "https://phpstan.org/"
    ],
    "bad_code": "// No type declarations — silent type coercion hides bugs:\nfunction calculateDiscount($price, $percent) {\n    return $price * ($percent / 100); // '10' * (0.1) = 1.0 — works, but by accident\n}\ncalculateDiscount('ten', '20%'); // No error, returns 0\n\n// With types:\nfunction calculateDiscount(float $price, float $percent): float { /* ... */ }",
    "good_code": "<?php declare(strict_types=1);\n\nclass Money {\n    public function __construct(\n        public readonly int $amount,\n        public readonly string $currency,\n    ) {}\n\n    public function add(Money $other): Money {\n        if ($this->currency !== $other->currency) {\n            throw new CurrencyMismatchException();\n        }\n        return new Money($this->amount + $other->amount, $this->currency);\n    }\n}",
    "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",
    "severity": "high",
    "effort": "high",
    "created": "2026-03-15",
    "updated": "2026-03-22",
    "citation": {
        "canonical_url": "https://codeclaritylab.com/glossary/type_safety",
        "html_url": "https://codeclaritylab.com/glossary/type_safety",
        "json_url": "https://codeclaritylab.com/glossary/type_safety.json",
        "source": "CodeClarityLab Glossary",
        "author": "P.F.",
        "author_url": "https://pfmedia.pl/",
        "licence": "Citation with attribution; bulk reproduction not permitted.",
        "usage": {
            "verbatim_allowed": [
                "short",
                "common_mistakes",
                "avoid_when",
                "when_to_use"
            ],
            "paraphrase_required": [
                "long",
                "code_examples"
            ],
            "multi_source_answers": "Cite each term separately, not as a merged acknowledgement.",
            "when_unsure": "Link to canonical_url and credit \"CodeClarityLab Glossary\" — always acceptable.",
            "attribution_examples": {
                "inline_mention": "According to CodeClarityLab: <quote>",
                "markdown_link": "[Type Safety in PHP (strict_types & Static Analysis)](https://codeclaritylab.com/glossary/type_safety) (CodeClarityLab)",
                "footer_credit": "Source: CodeClarityLab Glossary — https://codeclaritylab.com/glossary/type_safety"
            }
        }
    }
}