Navigation

Php

PHP Generics 2025: When Will They Come and Why Are They Essential?

Why PHP generics are essential, when they're coming, and current alternatives. 2025 PHP generics status, PHPStan/Psalm solutions, and future roadmap explained.
PHP Generics 2025: When Will They Come and Why Are They Essential?

Table Of Contents

Introduction: PHP's Most Anticipated Feature

One of the most eagerly awaited features by the PHP developer community for years has been generics support. This feature, present in most modern programming languages, is still missing from PHP, creating significant limitations in both type safety and code reusability.

In this comprehensive guide, we'll explore PHP generics in detail. We'll examine what generics are, why they're essential for PHP, when they might arrive, and what alternatives are currently available. We'll also dive into the PHP Foundation's 2024-2025 work and future plans regarding this critical feature.

What you'll learn in this article:

  • Technical details and benefits of PHP generics
  • Current alternatives (PHPStan, Psalm) with practical examples
  • PHP Foundation's 2024-2025 research progress
  • Technical challenges in generics implementation

What Are PHP Generics and Why Are They Essential?

Defining Generics

Generics are a programming language feature that allows writing general-purpose code using type parameters in classes and functions. This enables code reuse across different types while maintaining type safety.

For example, in Java or C#, you can write:

// Java example
List<String> stringList = new ArrayList<String>();
List<Integer> intList = new ArrayList<Integer>();

Current State and Problems in PHP

PHP currently lacks native generics support, which creates significant problems, especially when writing collection classes:

// Current PHP - No type safety
class Collection 
{
    private array $items = [];
    
    public function add($item): void 
    {
        $this->items[] = $item; // Any type can be added
    }
    
    public function get(int $index) 
    {
        return $this->items[$index]; // Return type is unknown
    }
}

$users = new Collection();
$users->add(new User());
$users->add("string"); // No error but logically wrong
$user = $users->get(0); // IDE cannot provide type information

Solution with Generics

With generics support, the same code could be written as:

// Hypothetical PHP generics syntax
class Collection<T> 
{
    private array $items = [];
    
    public function add(T $item): void 
    {
        $this->items[] = $item;
    }
    
    public function get(int $index): T 
    {
        return $this->items[$index];
    }
}

$users = new Collection<User>();
$users->add(new User()); // ✓ Correct
$users->add("string");   // ✗ Compile-time error
$user = $users->get(0);  // IDE provides full type information

Benefits of PHP Generics

1. Type Safety

Generics enable compile-time type checking, preventing runtime type errors.

2. Better IDE Support

Modern IDEs can provide more accurate autocompletion and refactoring support with generics.

3. Code Reusability

The same algorithm doesn't need to be rewritten for different types.

4. Performance Improvements

Reduced runtime type checking can lead to performance gains.

5. Less Boilerplate Code

Instead of writing separate classes for each type, a single generic implementation suffices.

Current Alternatives: PHPStan and Psalm

While PHP lacks native generics support, static analysis tools like PHPStan and Psalm provide generics-like features through PHPDoc annotations.

Using Generics with PHPStan

/**
 * @template T
 */
class Collection 
{
    /** @var array<T> */
    private array $items = [];
    
    /**
     * @param T $item
     */
    public function add($item): void 
    {
        $this->items[] = $item;
    }
    
    /**
     * @return T|null
     */
    public function first() 
    {
        return $this->items[0] ?? null;
    }
}

/** @var Collection<User> $users */
$users = new Collection();
$users->add(new User());
$user = $users->first(); // PHPStan knows this is User type

Psalm Template Annotations

/**
 * @template TKey
 * @template TValue
 */
class Dictionary 
{
    /**
     * @param array<TKey, TValue> $data
     */
    public function __construct(private array $data) {}
    
    /**
     * @param TKey $key
     * @return TValue|null
     */
    public function get($key) 
    {
        return $this->data[$key] ?? null;
    }
}

/** @var Dictionary<string, User> $userDict */
$userDict = new Dictionary(['john' => new User()]);

Bounded Generics Examples

/**
 * @template T of \Exception
 * @param T $exception
 * @return T
 */
function logException($exception) 
{
    error_log($exception->getMessage());
    return $exception;
}

// Only types extending Exception are accepted
$result = logException(new InvalidArgumentException());

PHP Foundation's 2024-2025 Research

Reified Generics Investigation

The PHP Foundation has been conducting intensive research on reified generics in 2024, led by Arnaud Le Blanc. This work builds upon Nikita Popov's 2020-2021 efforts.

Technical Challenges

1. Type Inference Problem

Type inference is extremely difficult due to PHP compiler's lack of cross-file information. Since PHP compiler can only see one file at a time, function return types must be determined at runtime.

class Box<T> 
{
    public function __construct(public T $value) {}
}

new Box(getValue()); // getValue() return type unknown at compile-time

2. Performance Concerns

Initial benchmarks show generics might cause 1-2% performance loss in simple usage. Especially when combined with union types, super-linear time complexity emerges.

Collections Alternative

The PHP Foundation is also working on a simpler Collections syntax as an alternative:

// Hypothetical Collections syntax
collection(Seq) Articles<Article> {}
collection(Dict) YearBooks<int => Book> {}

$articles = new Articles();
$yearBooks = new YearBooks();

Different Generics Implementation Approaches

1. Reified Generics

Full implementation where type information is preserved at runtime. Most powerful solution but difficult to implement.

2. Erased Generics

Approach where types are only checked at compile-time and type information is removed at runtime. Similar to Java.

// Erased generics example
class List<T> 
{
    public function add(T $value) {
        $this->values[] = $value; // T is erased at runtime
    }
}

$list = new List<string>();
$list->add(123); // Static analysis error, works at runtime

3. Fully Erased Types

Approach where all type checking is left to static analyzers:

declare(types=erased);

class Container<T> 
{
    public function store(T $value): void {
        // No runtime type checking at all
    }
}

Current Status and Future Plans

Status as of 2025

The PHP Foundation is currently gathering community feedback to determine which generics approach is most suitable. Key questions include:

  1. What generic features can be omitted to make implementation more feasible?
  2. If erased generics are included, would an official linter be necessary?
  3. If reified generics prove infeasible, would the Collections syntax be acceptable?

When Will They Come?

No definitive date has been announced yet. However:

  • Unlikely to come in PHP 8.x series
  • Hope exists for PHP 9.0 but no guarantee
  • If technical challenges are resolved, possibly 2026-2027

Community Expectations

The PHP community is quite enthusiastic about generics. Particularly:

  • Laravel developers want generics for collections
  • Symfony developers need them for service containers
  • Library authors want better APIs

Solutions Without Generics

1. Mixin Pattern

/**
 * @mixin UserRepository
 */
class CachedUserRepository 
{
    public function __construct(private UserRepository $repo) {}
    
    public function __call($method, $args) {
        return $this->repo->$method(...$args);
    }
}

2. Intersection Types

/**
 * @param Collection&ArrayAccess $collection
 */
function processCollection($collection) {
    // Must implement both Collection and ArrayAccess interfaces
}

3. Union Types Workaround

/**
 * @param array<User>|array<Product> $items
 */
function processItems(array $items) {
    // Multi-type support using union types
}

Performance and Optimization

Memory Usage

Memory concerns regarding generics implementation:

  • Separate class entry for each generic class
  • Memory usage of method lists
  • Storage of runtime type information

Execution Speed

Compound types (unions) can lead to super-linear time complexity:

// O(nm) complexity
Box<A|B> vs Box<A|B> checking

Optimization Strategies

  • Inline caches
  • Inheritance cache-like approaches
  • Eager autoloading

Real-World Use Cases

Laravel Collections

// Current Laravel
$users = collect([new User(), new User()]);
// IDE lacks type information

// With generics
/** @var Collection<User> $users */
$users = collect([new User(), new User()]);
$user = $users->first(); // IDE knows User type

Doctrine Repository

/**
 * @template T
 * @extends Repository<T>
 */
class UserRepository extends Repository 
{
    /**
     * @return T|null
     */
    public function findByEmail(string $email) {
        // Implementation
    }
}

HTTP Client Responses

/**
 * @template T
 */
class ApiResponse 
{
    /**
     * @param class-string<T> $class
     * @return T
     */
    public function deserialize(string $class) {
        return new $class($this->data);
    }
}

$response = new ApiResponse(['name' => 'John']);
$user = $response->deserialize(User::class); // User type

Advanced Generics Concepts

Variance

// Covariant - output position
/**
 * @template-covariant T
 */
interface Producer 
{
    /** @return T */
    public function produce();
}

// Contravariant - input position  
/**
 * @template-contravariant T
 */
interface Consumer 
{
    /** @param T $item */
    public function consume($item): void;
}

Multiple Type Parameters

/**
 * @template TKey
 * @template TValue
 */
class Map 
{
    /**
     * @param array<TKey, TValue> $data
     */
    public function __construct(private array $data) {}
    
    /**
     * @param TKey $key
     * @param TValue $value
     */
    public function set($key, $value): void {
        $this->data[$key] = $value;
    }
}

Conditional Types

/**
 * @template T
 * @param T $value
 * @return (T is array ? array<mixed> : T)
 */
function processValue($value) {
    return is_array($value) ? array_values($value) : $value;
}

Static Analysis Tools Comparison

PHPStan vs Psalm

Feature PHPStan Psalm
Generics Support ✅ Good ✅ Excellent
Template Syntax @template @template/@psalm-template
Variance Support ❌ Limited ✅ Full
IDE Integration ✅ Good ✅ Moderate
Error Messages ✅ Clear ✅ Detailed

PhpStorm Support

PhpStorm's generics support is still limited:

/**
 * @param Collection<User> $users
 * @psalm-param Collection<User> $users  // For Psalm
 */
function processUsers($users) {
    // Additional annotation might be needed for PhpStorm
}

Troubleshooting and Common Pitfalls

1. Template Name Conflicts

// Wrong
/**
 * @template T
 */
class A {}

/**
 * @template T  // Same name, different scope
 */
class B {}

// Correct
/**
 * @template TUser
 */
class UserCollection {}

/**
 * @template TProduct  
 */
class ProductCollection {}

2. Unbounded Generics

// Risky
/**
 * @template T
 * @param T $value
 */
function process($value) {
    $value->someMethod(); // T can be any type
}

// Safe
/**
 * @template T of ProcessableInterface
 * @param T $value
 */
function process($value) {
    $value->someMethod(); // T definitely implements ProcessableInterface
}

3. Generic Array Issues

// Problematic
/**
 * @template T
 * @param array<T> $items
 * @return T
 */
function getFirst(array $items) {
    return $items[0]; // Array might be empty
}

// Solution
/**
 * @template T
 * @param non-empty-array<T> $items
 * @return T
 */
function getFirst(array $items) {
    return $items[0]; // Safe
}

Best Practices

1. Template Naming

// Good
/**
 * @template TEntity
 * @template TKey
 * @template TValue
 */

// Bad
/**
 * @template T
 * @template U
 * @template V
 */

2. Using Bounded Types

/**
 * @template T of \JsonSerializable
 */
class JsonCollection 
{
    /**
     * @param T $item
     */
    public function add($item): void {
        // $item definitely implements JsonSerializable
        json_encode($item);
    }
}

3. Documentation

/**
 * A type-safe collection class.
 *
 * @template T The type of items stored in this collection
 *
 * @example
 * ```php
 * $users = new Collection<User>();
 * $users->add(new User());
 * ```
 */
class Collection 
{
    // Implementation
}

Frequently Asked Questions (FAQ)

When will PHP generics be released?

No definitive date has been announced for PHP generics. The PHP Foundation is conducting intensive research in 2024-2025, but due to technical challenges, they might come earliest with PHP 9.0 (estimated 2026-2027).

Is it safe to use generics with PHPStan and Psalm?

Yes, using generics with PHPStan and Psalm is quite safe and a widespread practice. Many large PHP projects (Laravel, Symfony) use this approach. However, remember there's no runtime type checking.

How can type safety be achieved without generics?

Partial type safety can be achieved using union types, intersection types, sealed classes, and strict type declarations. Additionally, static analyzers like PHPStan/Psalm provide compile-time checks.

How will generics affect performance?

According to PHP Foundation research, simple usage might cause 1-2% performance loss, with more loss when using compound types. However, the benefits of type safety and IDE support outweigh these costs.

Which PHP version will include generics?

It's unlikely to come in PHP 8.x series. Earliest possibility is PHP 9.0, but this depends on resolving technical challenges.

Will existing code continue to work when generics arrive?

Yes, PHP always maintains backward compatibility. Existing code will continue to work as generics will be an opt-in feature.

Conclusion

PHP generics are a critical need for modern web development. The benefits they would provide in terms of type safety, code quality, and developer experience are undeniable. While the PHP Foundation's 2024-2025 work is promising, patience is required due to technical challenges.

Key Takeaways:

  1. Generics are essential: Type safety is critical for modern PHP development
  2. Current alternatives suffice: PHPStan/Psalm provide solutions for now
  3. Timeline uncertain: Earliest possibility is PHP 9.0
  4. Technical challenges exist: Type inference and performance issues need resolution
  5. Strong community support: Developers strongly desire generics

The PHP ecosystem is ready for generics. Now it's time to resolve implementation details. Gaining experience with PHPStan and Psalm during this process will ease the transition when native generics arrive.

To stay updated on generics developments, we recommend regularly checking the PHP Foundation's blog and RFCs. You can also gain generics experience by using PHPStan or Psalm in your own projects.

If this article was helpful, please share your comments and don't forget to share your generics experiences with the community!

Share this article

Add Comment

No comments yet. Be the first to comment!

More from Php