In der objektorientierten Programmierung mit PHP sorgen Interfaces für klare Strukturen und verbindliche Regeln. Ein Interface legt fest, welche Methoden eine Klasse bereitstellen muss, ohne dabei die konkrete Umsetzung vorzugeben. Dadurch wird der Code flexibler, testbarer und besser wartbar. In diesem Tutorial lernst du, wie du Interfaces definierst, implementierst und wann ihr Einsatz sinnvoller ist als eine Vererbung über Basisklassen.

Los geht es mit der Frage, was ein Interface überhaupt ist und welche Rolle es in der objektorientierten Architektur spielt.
Was ist ein Interface in PHP?
Ein Interface ist ein Vertrag (Contract) für Klassen. Es definiert eine Reihe von Methoden-Signaturen, die jede implementierende Klasse bereitstellen muss. Das Interface selbst enthält keinen ausführbaren Code. Es beschreibt nur das „Was“, nicht das „Wie“.
Interfaces sind ein zentrales Werkzeug der Polymorphie: Verschiedene Klassen können dasselbe Interface implementieren und werden dadurch austauschbar. Eine Funktion, die ein Interface als Typ erwartet, akzeptiert jedes Objekt, das dieses Interface implementiert.
Ein Interface definieren
Ein Interface wird mit dem Schlüsselwort interface definiert. Es enthält Methoden-Signaturen, die implizit public sind.
Methoden im Interface
Methoden in einem Interface haben keinen Funktionskörper. Sie bestehen nur aus dem Methodennamen, den Parametern und dem Rückgabetyp.
<?php
interface Loggable
{
public function log(string $nachricht): void;
}
Jede Klasse, die Loggable implementiert, muss die Methode log() mit genau dieser Signatur bereitstellen.
Konstanten im Interface
Interfaces können auch Konstanten enthalten. Diese werden von den implementierenden Klassen geerbt und können dort direkt verwendet werden. Das ist nützlich, um feste Werte zu definieren, die zum Vertrag des Interfaces gehören.
<?php
interface HttpStatus
{
const OK = 200;
const NOT_FOUND = 404;
const SERVER_ERROR = 500;
}
class ApiResponse implements HttpStatus
{
public function statusText(int $code): string
{
return match($code) {
self::OK => 'Erfolg',
self::NOT_FOUND => 'Nicht gefunden',
self::SERVER_ERROR => 'Serverfehler',
default => 'Unbekannt'
};
}
}
Bis PHP 8.1 konnten Interface-Konstanten nicht überschrieben werden. Seit PHP 8.1 ist es möglich, Konstanten in Interfaces als public zu deklarieren, sodass sie in implementierenden Klassen überschrieben werden können.
Properties im Interface (ab PHP 8.4)
Seit PHP 8.4 ist es möglich, auch Properties in Interfaces zu deklarieren. Damit lassen sich Anforderungen an die Datenstruktur einer Klasse direkt im Interface festlegen. Diese Neuerung ergänzt den Vertrag um strukturelle Anforderungen, die über reine Methoden-Signaturen hinausgehen. In älteren PHP-Versionen war es notwendig, Getter-Methoden im Interface zu definieren, um den Zugriff auf bestimmte Daten zu erzwingen.
Ein Interface implementieren
Eine Klasse implementiert ein Interface mit dem Schlüsselwort implements. Sie muss dann alle im Interface definierten Methoden konkret umsetzen.
<?php
interface Loggable
{
public function log(string $nachricht): void;
}
class DateiLogger implements Loggable
{
public function log(string $nachricht): void
{
file_put_contents(
'app.log',
date('Y-m-d H:i:s') . ' ' . $nachricht . PHP_EOL,
FILE_APPEND
);
}
}
Fehlt eine Methode oder stimmt die Signatur nicht überein, wirft PHP einen Fatal Error. Der Vertrag des Interfaces ist verbindlich.
Die Signatur der implementierten Methode muss exakt mit der im Interface definierten übereinstimmen. Das betrifft den Methodennamen, die Parameter-Typen und den Rückgabetyp. Abweichungen führen ebenfalls zu einem Fatal Error. Diese Strenge ist beabsichtigt: Sie garantiert, dass jeder Code, der das Interface als Typ verwendet, sich auf die vorhandenen Methoden verlassen kann.
Mehrere Interfaces implementieren
Anders als bei der Vererbung, wo PHP nur eine einzige Elternklasse erlaubt, kann eine Klasse beliebig viele Interfaces implementieren. Das ist die Lösung für die fehlende Mehrfachvererbung in PHP.
<?php
interface Lesbar
{
public function lesen(): string;
}
interface Schreibbar
{
public function schreiben(string $inhalt): void;
}
class Datei implements Lesbar, Schreibbar
{
public function __construct(
private string $pfad
) {}
public function lesen(): string
{
return file_get_contents($this->pfad);
}
public function schreiben(string $inhalt): void
{
file_put_contents($this->pfad, $inhalt);
}
}
Die Klasse Datei erfüllt beide Verträge gleichzeitig. Sie kann überall eingesetzt werden, wo entweder Lesbar oder Schreibbar erwartet wird. Das ist ein wesentlicher Vorteil gegenüber der Vererbung: Während eine Klasse nur von einer einzigen Elternklasse erben kann, gibt es bei Interfaces keine solche Beschränkung. Dadurch lassen sich flexible Architekturen aufbauen, in denen Klassen verschiedene Rollen gleichzeitig erfüllen können.
Interfaces und Vererbung kombinieren
Interfaces können auch untereinander vererbt werden. Dafür wird das Schlüsselwort extends verwendet. Eine Klasse kann gleichzeitig eine Elternklasse haben und Interfaces implementieren.
<?php
interface Speicherbar extends Lesbar, Schreibbar
{
public function loeschen(): bool;
}
Eine Klasse, die Speicherbar implementiert, muss alle Methoden aus Lesbar, Schreibbar und Speicherbar bereitstellen. Wichtig: extends wird für die Vererbung zwischen Interfaces verwendet, implements für die Umsetzung durch eine Klasse.
Die Kombination von Vererbung und Interfaces ist in der Praxis weit verbreitet. Eine typische Architektur sieht so aus: Ein Interface definiert den Vertrag, eine abstrakte Basisklasse liefert gemeinsame Funktionalität, und konkrete Klassen implementieren die spezifische Logik. Diese Schichtung sorgt für maximale Flexibilität bei gleichzeitiger Code-Wiederverwendung.
<?php
interface CacheInterface
{
public function get(string $key): mixed;
public function set(string $key, mixed $value, int $ttl): void;
public function delete(string $key): void;
}
class FileCache implements CacheInterface
{
public function get(string $key): mixed
{
/* Liest aus Datei */
return null;
}
public function set(string $key, mixed $value, int $ttl): void
{
/* Schreibt in Datei */
}
public function delete(string $key): void
{
/* Loescht Datei */
}
}
In diesem Beispiel könnte später eine RedisCache-Klasse hinzugefügt werden, die dasselbe Interface implementiert. Jeder Code, der CacheInterface als Typ erwartet, funktioniert automatisch mit beiden Implementierungen.
Interface vs. abstrakte Klasse
Interfaces und abstrakte Klassen haben auf den ersten Blick Ähnlichkeiten, unterscheiden sich aber in wesentlichen Punkten.
Ein Interface enthält ausschließlich Methoden-Signaturen und Konstanten. Es kann keinen ausführbaren Code enthalten. Eine abstrakte Klasse hingegen kann sowohl abstrakte (nicht implementierte) als auch konkrete (implementierte) Methoden haben. Weitere Unterschiede betreffen die Vererbung und die Sichtbarkeit.
<?php
/* Interface: nur Vertrag, kein Code */
interface Formatierer
{
public function formatieren(string $text): string;
}
/* Abstrakte Klasse: Vertrag und gemeinsamer Code */
abstract class BasisFormatierer
{
abstract public function formatieren(string $text): string;
public function bereinigen(string $text): string
{
return trim($text);
}
}
Die wichtigsten Unterschiede auf einen Blick:
- Eine Klasse kann nur von einer einzigen abstrakten Klasse erben, aber beliebig viele Interfaces implementieren.
- Abstrakte Klassen können Eigenschaften mit Werten, konkrete Methoden und Konstruktoren enthalten. Interfaces beschränken sich auf Signaturen und Konstanten.
- Methoden in Interfaces sind immer
public. In abstrakten Klassen sind auch protected und private Methoden möglich. - Abstrakte Klassen eignen sich, wenn mehrere verwandte Klassen gemeinsamen Code teilen sollen. Interfaces eignen sich, wenn nicht verwandte Klassen eine gemeinsame Schnittstelle benötigen.
Die Faustregel lautet: Verwende ein Interface, wenn du nur einen Vertrag definieren willst. Verwende eine abstrakte Klasse, wenn du zusätzlich gemeinsamen Code für Unterklassen bereitstellen möchtest. In der Praxis werden beide Konzepte oft kombiniert: Ein Interface definiert den Vertrag, und eine abstrakte Klasse liefert eine Basis-Implementierung, die von konkreten Klassen erweitert wird.
Warum Interfaces verwenden?
Interfaces bieten zwei zentrale Vorteile, die den Code einer Anwendung deutlich verbessern.
Polymorphie
Polymorphie bedeutet, dass verschiedene Klassen über dasselbe Interface angesprochen werden können. Eine Funktion arbeitet mit dem Interface-Typ und muss die konkreten Klassen nicht kennen.
<?php
interface Bezahlbar
{
public function bezahlen(float $betrag): bool;
}
class Kreditkarte implements Bezahlbar
{
public function bezahlen(float $betrag): bool
{
/* Kreditkarten-Logik */
return true;
}
}
class PayPal implements Bezahlbar
{
public function bezahlen(float $betrag): bool
{
/* PayPal-Logik */
return true;
}
}
function bestellungAbschliessen(Bezahlbar $methode, float $betrag): void
{
$methode->bezahlen($betrag);
}
Neue Zahlungsmethoden können jederzeit hinzugefügt werden, ohne die Funktion bestellungAbschliessen() zu ändern. Die Funktion verlässt sich ausschließlich auf den Vertrag des Interfaces.
Dependency Injection
Beim Prinzip der Dependency Injection wird gegen Interfaces programmiert, nicht gegen konkrete Klassen. Das macht den Code testbar, weil sich Abhängigkeiten in Tests durch Mock-Objekte ersetzen lassen. In modernen Frameworks wie Laravel und Symfony ist dieses Muster allgegenwärtig.
<?php
interface MailerInterface
{
public function senden(string $an, string $betreff, string $text): bool;
}
class SmtpMailer implements MailerInterface
{
public function senden(string $an, string $betreff, string $text): bool
{
/* Echte SMTP-Logik */
return true;
}
}
class FakeMailer implements MailerInterface
{
public array $gesendet = [];
public function senden(string $an, string $betreff, string $text): bool
{
$this->gesendet[] = ['an' => $an, 'betreff' => $betreff];
return true;
}
}
class Newsletter
{
public function __construct(
private MailerInterface $mailer
) {}
public function versenden(array $empfaenger, string $betreff, string $text): int
{
$gesendet = 0;
foreach ($empfaenger as $email) {
if ($this->mailer->senden($email, $betreff, $text)) {
$gesendet++;
}
}
return $gesendet;
}
}
Die Klasse Newsletter erwartet im Konstruktor ein Objekt vom Typ MailerInterface. In der Produktion wird SmtpMailer übergeben, in Tests FakeMailer. Die Klasse Newsletter muss dafür nicht verändert werden. Dieses Prinzip wird als lose Kopplung bezeichnet: Die Klasse hängt nicht von einer konkreten Implementierung ab, sondern nur vom Interface.
Typ-Prüfung mit instanceof
Mit dem Operator instanceof lässt sich zur Laufzeit prüfen, ob ein Objekt ein bestimmtes Interface implementiert. Das ist nützlich, wenn eine Funktion mit verschiedenen Objekttypen arbeiten muss und nur bei bestimmten Interfaces zusätzliche Aktionen ausführen soll.
<?php
function verarbeiten(object $obj): void
{
if ($obj instanceof Loggable) {
$obj->log('Verarbeitung gestartet');
}
}
Diese Prüfung ist besonders in Kombination mit Polymorphie wertvoll, wenn verschiedene Klassen unterschiedliche Interfaces implementieren und der Code darauf reagieren soll.
Namenskonventionen für Interfaces
Für die Benennung von Interfaces haben sich in der PHP-Community zwei Konventionen etabliert. Viele Entwickler verwenden ein Suffix wie Interface (z.B. LoggerInterface, CacheInterface). Andere bevorzugen beschreibende Adjektive (z.B. Loggable, Cacheable, Serializable). Beide Ansätze sind verbreitet. Entscheidend ist, dass die Benennung innerhalb eines Projekts konsistent bleibt. Das PSR-Framework von PHP-FIG verwendet durchgehend das Suffix Interface.
Typische Fehler und Stolperfallen
Beim Arbeiten mit Interfaces gibt es einige häufige Fehlerquellen:
- Alle Methoden eines Interfaces müssen in der implementierenden Klasse vorhanden sein. Fehlt eine Methode, wirft PHP einen Fatal Error.
- Methoden in Interfaces können nur
public sein. Andere Sichtbarkeiten sind nicht erlaubt. - Das Schlüsselwort
extends wird für Interfaces untereinander verwendet, implements für die Umsetzung durch Klassen. Eine Verwechslung führt zu einem Syntaxfehler. - Interface-Konstanten konnten bis PHP 8.1 nicht überschrieben werden.
- Properties in Interfaces sind erst ab PHP 8.4 möglich. In älteren PHP-Versionen führt der Versuch zu einem Fehler.
classDiagram
class Bezahlbar {
<<interface>>
+bezahlen(float betrag) bool
}
class Kreditkarte {
+bezahlen(float betrag) bool
}
class PayPal {
+bezahlen(float betrag) bool
}
class Rechnung {
+bezahlen(float betrag) bool
}
Bezahlbar <|.. Kreditkarte
Bezahlbar <|.. PayPal
Bezahlbar <|.. Rechnung
Fazit
Interfaces sind ein mächtiges Werkzeug der objektorientierten Programmierung in PHP. Sie definieren klare Verträge für Klassen, ermöglichen Polymorphie und bilden die Grundlage für lose Kopplung und Dependency Injection. Im Unterschied zu abstrakten Klassen enthalten sie keinen eigenen Code, sondern beschreiben ausschließlich die Schnittstelle. Wer sauberen, erweiterbaren und testbaren PHP-Code schreiben möchte, kommt an Interfaces nicht vorbei. Zusammen mit Vererbung und Traits gehören sie zu den vier Säulen der objektorientierten Programmierung in PHP: Kapselung, Vererbung, Polymorphie und Abstraktion. Interfaces decken dabei die Bereiche Polymorphie und Abstraktion ab und sind damit unverzichtbar für professionelle PHP-Anwendungen.