Phan v6 provides full support for all PHP 8.4 and PHP 8.5 language features with complete type checking and error detection.
New feature: Methods to intercept property access with custom get/set logic.
class User {
public string $name {
set {
$this->name = trim($value);
}
}
}Phan validates:
- Hook parameter types match property type
- Hook return type matches expected value
- Readonly properties with set hooks (
PhanReadonlyPropertyHasSetHook)
class Product {
public int $price {
set {
if ($value < 0) {
throw new ValueError("Price cannot be negative");
}
$this->price = $value; // Type-checked
}
get {
return $this->price; // Return type verified
}
}
}Detected errors:
PhanPropertyHookIncompatibleParamType- Parameter type mismatchPhanPropertyHookIncompatibleReturnType- Return type mismatchPhanPropertyHookWithDefaultValue- Invalid default valuesPhanPropertyHookFinalOverride- Illegal override of final hook
class Temperature {
private float $celsius;
public float $fahrenheit {
get {
return ($this->celsius * 9/5) + 32;
}
set {
$this->celsius = ($value - 32) * 5/9;
}
}
public function __construct(float $celsius) {
$this->celsius = $celsius;
}
}
$temp = new Temperature(20);
echo $temp->fahrenheit; // 68
$temp->fahrenheit = 86;
echo $temp->celsius; // ~30New feature: Mark deprecated functions, methods, and constants.
#[Deprecated(message: "Use newMethod() instead", since: "2.0.0")]
public function oldMethod(): void {
$this->newMethod();
}
#[Deprecated]
class LegacyClass {
#[Deprecated(message: "Use NEW_VALUE instead")]
public const OLD_VALUE = 1;
}Detection:
PhanDeprecatedFunction- Using deprecated functionsPhanDeprecatedMethod- Using deprecated methodsPhanDeprecatedClassConstant- Using deprecated constants
// Example detection
oldMethod(); // Warning: PhanDeprecatedFunction
LegacyClass::OLD_VALUE; // Warning: PhanDeprecatedClassConstantNew feature: Declare types for class constants with inheritance checking.
interface DatabaseConfig {
public const int PORT = 5432;
public const string HOST = 'localhost';
}
class PostgresConfig implements DatabaseConfig {
public const int PORT = 5432; // OK
public const string HOST = '127.0.0.1'; // OK
}
class MySQLConfig implements DatabaseConfig {
public const string PORT = '3306'; // Error: type mismatch!
public const string HOST = 'localhost'; // OK
}Detected errors:
PhanTypeMismatchDeclaredConstant- Value doesn't match declared typePhanConstantTypeMismatchInheritance- Type doesn't match parent constantPhanTypeMismatchDeclaredConstantNever- Value contradicts never type
Type narrowing in conditions:
if (MyEnum::STATUS === 'ACTIVE') {
// Phan narrows MyEnum::STATUS to literal 'ACTIVE' here
}Phan understands all PHP 8.4 new functions with proper type signatures:
// array_find - find first element matching condition
$found = array_find([1, 2, 3, 4], fn($v) => $v > 2); // int|null
// array_find_key - find first key matching condition
$key = array_find_key(['a' => 1, 'b' => 2], fn($v) => $v > 1); // string|int|null
// array_any - check if any element matches
$hasEven = array_any([1, 2, 3], fn($v) => $v % 2 === 0); // true
// array_all - check if all elements match
$allPositive = array_all([1, 2, 3], fn($v) => $v > 0); // true
// String trimming functions with locale support
$trimmed = mb_trim(" hello "); // "hello"
$left = mb_ltrim(" hello"); // "hello "
$right = mb_rtrim("hello "); // " hello"
$ucfirst = mb_ucfirst("hello"); // "Hello"
$lcfirst = mb_lcfirst("HELLO"); // "hELLO"Type checking examples:
// Type-safe usage
$numbers = [1, 2, 3, 4, 5];
// Phan knows the return types
$doubled = array_map(fn($n) => $n * 2, $numbers); // array<int|string|mixed>
$even = array_filter($numbers, fn($n) => $n % 2 === 0); // array
$first = array_find($numbers, fn($n) => $n > 3); // int|null
if ($first !== null) {
echo $first; // Phan knows it's int here
}New feature: First-class pipe syntax for function chaining.
// Traditional chaining
$result = strtoupper(trim($input));
// With pipe operator
$result = $input |> trim(...) |> strtoupper(...);Phan provides full type inference through pipe chains:
/**
* @param int $value
* @return int
*/
function double($value): int {
return $value * 2;
}
/**
* @param int $value
* @return string
*/
function stringify($value): string {
return (string) $value;
}
// Type flows correctly through the pipe
$result = 5 |> double(...) |> stringify(...);
// Phan infers: $result is stringError detection:
function expectsString(string $s): void {}
// Type error caught at pipe level
5 |> double(...) |> expectsString(...); // Error: int not assignable to stringNew feature: Mark return values that shouldn't be ignored.
#[NoDiscard]
function generateId(): string {
return uniqid('id_');
}
#[NoDiscard(message: "Session was not stored")]
function createSession(): Session {
return new Session();
}
// Errors
generateId(); // Error: PhanNoDiscardReturnValueIgnored
// OK
$id = generateId(); // Value is used
(void) generateId(); // Explicit discard with castPhan detections:
PhanNoDiscardReturnValueIgnored- Return value was ignored
New feature: Explicitly suppress #[NoDiscard] warnings.
#[NoDiscard]
function getValue(): int {
return 42;
}
// Error
getValue(); // PhanNoDiscardReturnValueIgnored
// OK
(void) getValue(); // Explicit cast suppresses warningThis is clearer than assigning to unused variables:
// Old approach (less clear)
$_ = getValue();
// New approach (explicit intent)
(void) getValue();Phan v6 includes updated function signatures for all PHP 8.5 standard library changes:
// New DateTime methods
$timestamp = DateTime::createFromTimestamp(time());
$immutable = DateTimeImmutable::createFromTimestamp(time());
// DOMXPath enhancements
$xpath->registerPHPFunctionNS('ns', 'localName', callable);// Find first matching element
array_find([1, 2, 3, 4], fn($v) => $v > 2); // Returns first element or null
// Find first matching key
array_find_key(['a' => 1, 'b' => 2], fn($v) => $v > 1); // Returns first key or null
// Check if any element matches predicate
array_any([1, 2, 3], fn($v) => $v > 2); // bool
// Check if all elements match predicate
array_all([1, 2, 3], fn($v) => $v > 0); // bool// Multibyte string trimming
mb_trim(" hello "); // "hello"
mb_ltrim(" hello"); // "hello "
mb_rtrim("hello "); // " hello"
// Case operations
mb_ucfirst("hello"); // "Hello"
mb_lcfirst("HELLO"); // "hELLO"$items = [1, 2, 3, 4, 5];
// Phan understands return types
if (array_any($items, fn($i) => $i > 3)) {
// We know at least one item > 3
$first = array_find($items, fn($i) => $i > 3);
// Phan knows $first is int (even though it could be null)
}class BankAccount {
private float $balance = 0;
public float $availableBalance {
get {
return $this->balance;
}
set {
if ($value < 0) {
throw new ValueError("Balance cannot be negative");
}
$this->balance = $value;
}
}
public function __construct(float $initialBalance) {
if ($initialBalance < 0) {
throw new ValueError("Initial balance cannot be negative");
}
$this->balance = $initialBalance;
}
}
// Type-safe usage
$account = new BankAccount(1000);
$account->availableBalance = 500; // Type checked
echo $account->availableBalance; // float
$account->availableBalance = "invalid"; // Error: string not assignable to float#[Deprecated(message: "Use TokenFactory::create() instead")]
function legacyCreateToken(): Token {
return TokenFactory::create();
}
class Authentication {
public function login(string $user): void {
$token = legacyCreateToken(); // Warning: deprecated function
$this->validateToken($token);
}
}function parseJson(string $json): mixed {
return json_decode($json, true);
}
function validateData(array $data): array {
return $data; // type check
}
function formatOutput(array $data): string {
return json_encode($data);
}
// Full type-safe pipe
$result = '{"key":"value"}'
|> parseJson(...)
|> validateData(...)
|> formatOutput(...);
// Phan infers: $result is stringTo analyze PHP 8.4/8.5 code, ensure:
- Phan v6.0+ installed
- PHP 8.1+ to run Phan (target version can be different)
- php-ast 1.1.3+ for PHP 8.4+ syntax support
# Check requirements
phan --version
php --version
php -r "echo phpversion('ast');"
# Configure for PHP 8.5 analysis
// .phan/config.php
return [
'target_php_version' => '8.5',
// ... other settings
];- [[Annotating-Your-Source-Code-V6]] - Full annotation guide with examples
- [[Migrating-to-Phan-V6]] - Migration from older Phan versions
- PHP 8.4 Announcement
- PHP 8.5 Announcement