Phantom Type¶
A phantom type is a generic type that carries a type parameter which does not appear in the runtime representation of the value. The parameter exists only at the type-checking level, adding information that guides the static analyser without any runtime cost.
The classic use case is encoding state in the type. A Connection<Closed> and a Connection<Open> have identical runtime structure but different static types, so calling query() on a closed connection becomes a compile-time error rather than a runtime exception. The phantom parameter Closed or Open holds no data; it is purely a type-level marker.
Phantom types are a lightweight alternative to dependent types for expressing invariants that can be tracked statically. They are used in Haskell, Rust (zero-sized marker types), and TypeScript (branded types via & { readonly __brand: 'USD' }).
PHP does not support phantom types natively. Generic type parameters are not a language feature. However, a partial simulation is possible using PHPStan or Psalm template annotations for static analysis purposes only, with no runtime enforcement. Branded value objects achieve a similar discrimination effect at the cost of wrapping every value in an object.
<?php
// PHP approximation using PHPStan @template (static analysis only, no runtime effect)
/**
* @template TStatus
*/
final class Connection {
public function __construct(private readonly \PDO $pdo) {}
}
/** @return Connection<'open'> */
function openConnection(string $dsn): Connection {
return new Connection(new \PDO($dsn));
}
/**
* @param Connection<'open'> $conn
*/
function query(Connection $conn, string $sql): array {
// PHPStan will warn if a Connection<'closed'> is passed
return [];
}
?>
See also Phantom type on Wikipedia and Branded types in TypeScript.
Related : Generics, Type System, Type Checking, Gradual Type Checking, Value Object, Dependent Type, Immutable