# The Type System and Static Analysis — PHP

Source: https://www.skillbyai.com/en/php/e-types

> Use union, intersection, nullable and special types, and catch bugs with PHPStan or Psalm.

## Types that document and protect

PHP's type system has grown considerably. **Nullable** types (`?string`), **union** types (`int|string`, PHP 8.0), **intersection** types (`Countable&Traversable`, 8.1) and **DNF** types combining both (`(A&B)|null`, 8.2) describe precise contracts. Special types: **`mixed`** (anything), **`void`**, **`never`** (always throws or exits), **`static`** (the called class, useful in fluent factories), **`iterable`**, and standalone `null`, `true` and `false` (8.2). PHP 8.3 added **typed class constants**. Still, runtime types cannot express everything, such as the shape of an array or generics like "a collection of Orders". **Static analysers** fill the gap: **PHPStan** and **Psalm** read your code without running it, understand docblock types such as `array<string, Order>`, `list<int>` and `Collection<Order>`, and report type errors, undefined methods, unreachable code and possible null dereferences. Start at a low strictness level and raise it over time, use a baseline file for legacy code, and run analysis in CI on every pull request.

## Precise types plus docblock generics for static analysis

Runtime types where PHP supports them; docblocks where it does not.

```php
<?php
declare(strict_types=1);

final class OrderRepository
{
    /** @var array<string, Order> */
    private array $orders = [];

    public function find(string $id): ?Order
    {
        return $this->orders[$id] ?? null;
    }

    /** @return list<Order> */
    public function byStatus(OrderStatus $status): array
    {
        return array_values(array_filter($this->orders, fn (Order $o): bool => $o->status === $status));
    }

    public function idOf(Order|string $orderOrId): string     // union type
    {
        return $orderOrId instanceof Order ? $orderOrId->id : $orderOrId;
    }

    public function fail(string $reason): never               // always throws
    {
        throw new RuntimeException($reason);
    }
}

// vendor/bin/phpstan analyse src --level=8
// PHPStan would flag: $repo->find('x')->total   (possible null dereference)
```

## A building inspector before move-in

Runtime type checks are like discovering a missing step when you fall on the stairs. Static analysis is the inspector who walks through before anyone moves in and marks every missing step on the plan.

**Quiz:** What does PHPStan or Psalm add on top of PHP's runtime type declarations?

- [ ] A faster interpreter
- [x] Static checking of code, including docblock types such as array shapes and generics, without running it
- [ ] A web server
- [ ] Automatic database migrations

*Answer:* Static checking of code, including docblock types such as array shapes and generics, without running it. Static analysers find type errors and bugs before the code runs.
