Generics in a docblock are checked by nothing at runtime

PHP has no generics, so @return list<Order> is a promise to the analyser and a comment to the engine.

/**
 * @template T of Model
 */
final class Repository
{
    /** @param class-string<T> $class */
    public function __construct(private string $class) {}

    /** @return T|null */
    public function find(int $id) { /* ... */ }
}

// $repo = new Repository(Order::class);
// $repo->find(1) is Order|null, to the analyser.

The annotations are genuinely useful and are genuinely unenforced: a function returning the wrong type produces no runtime error and an analyser finding only where the analyser can see both ends. class-string<T> is the piece that makes a generic factory work and is the least obvious part of the syntax. The whole scheme depends on the analyser running in CI, because a docblock nobody checks is worse than none — it is a claim people trust.