Warum Generics nicht nur ein modischer Trend, sondern eine Notwendigkeit sind?
\n\nWenn Sie jemals PHP-Code geschrieben haben, der mit Sammlungen, Repositories oder der Service-Schicht arbeitete, sind Sie sicherlich auf folgende Situation gestoßen: Eine Funktion akzeptiert array, aber was darin steckt, ist ein Rätsel. Vielleicht sind es Benutzer, vielleicht Katzen. Oder, noch schlimmer, beides. Genau hier kommen Generics ins Spiel – ein leistungsstarkes Typisierungswerkzeug, das Ihren Code vorhersagbar, sicher und selbstdokumentierend macht.
In diesem Artikel analysieren wir, wie Generics in PHP funktionieren, wofür sie benötigt werden und wie man sie in realen Projekten einsetzt. Sie erfahren, wie Sie typische Fehler vermeiden und Ihren Code professionell gestalten. Los geht's!
\n\nWas sind Generics einfach erklärt?
\n\nStellen Sie sich vor, Sie sind Entwickler eines Online-Shops. Sie haben eine Klasse Cart, die Artikel speichert. Ohne Generics schreiben Sie:
class Cart {\n private array $items;\n \n public function addItem($item): void {\n $this->items[] = $item;\n }\n \n public function getItems(): array {\n return $this->items;\n }\n}\n\nDas Problem? $item kann alles sein – eine Zahl, ein String, ein Product-Objekt oder sogar null. Die IDE sagt Ihnen nicht, welcher Typ von getItems() zurückgegeben wird. Sie müssen in die Dokumentation schauen oder debuggen.
Generics lösen dieses Problem. Sie ermöglichen es anzugeben: „Hey, diese Klasse arbeitet nur mit Artikeln!". Das sieht so aus:
\n\nclass Cart {\n /** @param Product[] $items */\n private array $items;\n \n public function addItem(Product $item): void {\n $this->items[] = $item;\n }\n \n /** @return Product[] */\n public function getItems(): array {\n return $this->items;\n }\n}\n\nAber das ist nur eine Nachahmung. Echte Generics in PHP kamen mit PHP 8.0, und vollständige Unterstützung gibt es ab PHP 8.1+ mit Hilfe von Attributen und statischer Analyse (z. B. PHPStan oder Psalm).
\n\nWie funktionieren Generics in PHP tatsächlich?
\n\nIm Gegensatz zu Java oder C# hat PHP (noch) keine native Unterstützung für Generics auf Syntaxebene. Dank PHPDoc-Annotationen und statischer Analyse-Tools können wir jedoch alle Vorteile der Typisierung nutzen.
\n\nVorlagentypen (Template Types)
\n\nDer gängigste Ansatz ist die Verwendung von @template in PHPDoc. Betrachten wir ein Beispiel mit einer Sammlung:
/**\n * @template T\n */\nclass Collection {\n /** @var array<int, T> */\n private array $items = [];\n \n /**\n * @param T $item\n */\n public function add(mixed $item): void {\n $this->items[] = $item;\n }\n \n /**\n * @return T\n */\n public function get(int $index): mixed {\n return $this->items[$index] ?? throw new \\OutOfBoundsException();\n }\n}\n\nJetzt geben Sie bei der Verwendung dieser Klasse den konkreten Typ an:
\n\n/** @var Collection<User> $userCollection */\n$userCollection = new Collection();\n$userCollection->add(new User('Alice'));\n$user = $userCollection->get(0); // PHPStan weiß: $user ist User\necho $user->name; // Sicher!\n\nEinschränkungen (Constraints) für Generics
\n\nManchmal muss der Typ T nicht beliebig sein, sondern ein bestimmtes Interface implementieren. Dies nennt man bound (Einschränkung):
\n\n/**\n * @template T of \\JsonSerializable\n */\nclass JsonCollection {\n /** @var array<int, T> */\n private array $items = [];\n \n public function toJson(): string {\n return json_encode($this->items);\n }\n}\n\nJetzt können Sie kein Objekt zur Sammlung hinzufügen, das JsonSerializable nicht implementiert. Die IDE und der statische Analysator geben einen Fehler aus.