Interfaces und abstrakte Klassen sind zwei fundamentale Konzepte in der objektorientierten Programmierung, die in PHP vollständig unterstützt werden. Beide bieten Mechanismen zur Definition von Vertragsbedingungen für Klassen, unterscheiden sich jedoch in ihrer Anwendung und ihren Möglichkeiten.
Eine abstrakte Klasse dient als Basis für andere Klassen, kann aber
selbst nicht instanziiert werden. Sie wird mit dem Schlüsselwort
abstract definiert und kann sowohl abstrakte als auch
konkrete Methoden und Eigenschaften enthalten.
abstract class Dokument {
protected string $titel;
protected string $autor;
protected DateTime $erstelltAm;
public function __construct(string $titel, string $autor) {
$this->titel = $titel;
$this->autor = $autor;
$this->erstelltAm = new DateTime();
}
// Konkrete Methode
public function getMetadaten(): string {
return "Titel: {$this->titel}, Autor: {$this->autor}, Erstellt: {$this->erstelltAm->format('d.m.Y')}";
}
// Abstrakte Methode - muss von Kindklassen implementiert werden
abstract public function anzeigen(): string;
// Abstrakte Methode mit Parametern
abstract public function exportieren(string $format): string;
}Abstrakte Klassen müssen von konkreten Klassen erweitert werden, die alle abstrakten Methoden implementieren:
class PDFDokument extends Dokument {
private string $pfad;
public function __construct(string $titel, string $autor, string $pfad) {
parent::__construct($titel, $autor);
$this->pfad = $pfad;
}
public function anzeigen(): string {
return "PDF wird angezeigt: {$this->titel} ({$this->pfad})";
}
public function exportieren(string $format): string {
return "PDF wird nach {$format} exportiert...";
}
}
class TextDokument extends Dokument {
private string $inhalt;
public function __construct(string $titel, string $autor, string $inhalt) {
parent::__construct($titel, $autor);
$this->inhalt = $inhalt;
}
public function anzeigen(): string {
return "Textdokument: {$this->titel}\n{$this->inhalt}";
}
public function exportieren(string $format): string {
switch ($format) {
case 'html':
return "<html><body><h1>{$this->titel}</h1><p>{$this->inhalt}</p></body></html>";
case 'json':
return json_encode([
'titel' => $this->titel,
'autor' => $this->autor,
'inhalt' => $this->inhalt
]);
default:
return $this->inhalt;
}
}
}Eine abstrakte Klasse kann nicht direkt instanziiert werden:
$dokument = new Dokument("Titel", "Autor"); // Fehler!Abstrakte Methoden haben keine Implementierung in der abstrakten Klasse.
Jede Klasse, die eine abstrakte Klasse erweitert, muss alle abstrakten Methoden implementieren oder selbst abstrakt sein.
Abstrakte Klassen können konkrete Methoden und Eigenschaften enthalten, die von Kindklassen geerbt werden.
Abstrakte Klassen können protected und private Eigenschaften und Methoden haben, was bei Interfaces nicht möglich ist.
Ein Interface definiert einen Vertrag, den implementierende Klassen einhalten müssen. Es spezifiziert, welche Methoden eine Klasse haben muss, ohne deren Implementierung vorzugeben.
interface Druckbar {
public function drucken(): string;
public function seitenanzahlBerechnen(): int;
}
interface Exportierbar {
public function alsPDF(): string;
public function alsWord(): string;
public function alsPlaintext(): string;
}Eine Klasse kann ein oder mehrere Interfaces implementieren:
class Bericht implements Druckbar, Exportierbar {
private string $titel;
private array $abschnitte;
public function __construct(string $titel, array $abschnitte) {
$this->titel = $titel;
$this->abschnitte = $abschnitte;
}
public function drucken(): string {
return "Drucke Bericht: {$this->titel}";
}
public function seitenanzahlBerechnen(): int {
// Einfache Berechnung: Ein Abschnitt pro Seite
return count($this->abschnitte);
}
public function alsPDF(): string {
return "Exportiere als PDF: {$this->titel}";
}
public function alsWord(): string {
return "Exportiere als Word: {$this->titel}";
}
public function alsPlaintext(): string {
return "Titel: {$this->titel}\n" . implode("\n\n", $this->abschnitte);
}
}Alle Methoden in einem Interface sind implizit abstrakt und public.
Interfaces können keine Implementierungen enthalten (vor PHP 8.0).
Eine Klasse kann mehrere Interfaces implementieren, aber nur von einer Klasse erben.
Interfaces können andere Interfaces erweitern.
Interfaces können Konstanten definieren, aber keine Eigenschaften (Variablen).
Interfaces können Konstanten definieren, die von implementierenden Klassen verwendet werden können:
interface Zahlungsstatus {
const AUSSTEHEND = 'ausstehend';
const VERARBEITET = 'verarbeitet';
const STORNIERT = 'storniert';
const FEHLGESCHLAGEN = 'fehlgeschlagen';
}
class Bestellung implements Zahlungsstatus {
private string $status;
public function __construct() {
$this->status = self::AUSSTEHEND;
}
public function verarbeiteZahlung(): void {
// Zahlungslogik...
$this->status = self::VERARBEITET;
}
public function getStatus(): string {
return $this->status;
}
}
$bestellung = new Bestellung();
echo $bestellung->getStatus(); // Ausgabe: ausstehend
$bestellung->verarbeiteZahlung();
echo $bestellung->getStatus(); // Ausgabe: verarbeitet
// Direkter Zugriff auf Interface-Konstanten
echo Zahlungsstatus::STORNIERT; // Ausgabe: storniertInterfaces können andere Interfaces erweitern:
interface Basis {
public function methodA(): void;
}
interface Erweitert extends Basis {
public function methodB(): void;
}
// Diese Klasse muss sowohl methodA als auch methodB implementieren
class KonkreteKlasse implements Erweitert {
public function methodA(): void {
echo "Methode A";
}
public function methodB(): void {
echo "Methode B";
}
}Interfaces sind besonders nützlich für Typ-Deklarationen und ermöglichen polymorphes Verhalten:
function druckeElement(Druckbar $element): void {
echo $element->drucken();
echo "Anzahl der Seiten: " . $element->seitenanzahlBerechnen();
}
// Kann mit jeder Klasse verwendet werden, die Druckbar implementiert
$bericht = new Bericht("Quartalsbericht", ["Einleitung", "Finanzen", "Ausblick"]);
druckeElement($bericht);Ab PHP 8.0 unterstützen Interfaces zusätzliche Features:
interface Konvertierbar {
public function konvertieren(string|array $daten): string|array;
}#[Attribute]
interface ValidierungsRegel {
public function validieren($wert): bool;
}
#[ValidierungsRegel]
class EmailValidierung implements ValidierungsRegel {
public function validieren($wert): bool {
return filter_var($wert, FILTER_VALIDATE_EMAIL) !== false;
}
}| Abstrakte Klassen | Interfaces | |
|---|---|---|
| Instanziierung | Kann nicht instanziiert werden | Kann nicht instanziiert werden |
| Methoden | Konkrete und abstrakte Methoden | Nur abstrakte Methoden (bis PHP 8.0) |
| Eigenschaften | Kann Eigenschaften haben | Keine Eigenschaften, nur Konstanten |
| Vererbung | Einfachvererbung | Mehrfachimplementierung möglich |
| Zugriffsmodifikatoren | public, protected, private | Nur public |
| Verwendungszweck | Basisimplementierung und Vertragserzwingung | Vertragsdefinition |
Betrachten wir ein praktisches Beispiel für ein einfaches Content-Management-System, das sowohl abstrakte Klassen als auch Interfaces verwendet:
// Interface für alle Entitäten, die in einer Datenbank gespeichert werden können
interface Persistierbar {
public function speichern(): bool;
public function laden(int $id): bool;
public function löschen(): bool;
}
// Interface für Elemente, die im Frontend angezeigt werden können
interface Darstellbar {
public function alsHTML(): string;
public function alsText(): string;
}
// Abstrakte Basisklasse für alle Content-Typen
abstract class Content implements Persistierbar {
protected ?int $id = null;
protected string $titel;
protected string $autor;
protected DateTime $erstelltAm;
protected DateTime $aktualisiertAm;
public function __construct(string $titel, string $autor) {
$this->titel = $titel;
$this->autor = $autor;
$this->erstelltAm = new DateTime();
$this->aktualisiertAm = new DateTime();
}
// Gemeinsame Implementierung für alle Content-Typen
public function speichern(): bool {
// Speichert in der Datenbank, setzt ID wenn erfolgreich
$this->aktualisiertAm = new DateTime();
echo "Content '{$this->titel}' wurde gespeichert.\n";
$this->id = $this->id ?? random_int(1, 1000); // Simuliert Datenbankzuweisung
return true;
}
public function laden(int $id): bool {
// Lädt aus der Datenbank
$this->id = $id;
echo "Content mit ID {$id} wurde geladen.\n";
return true;
}
public function löschen(): bool {
// Löscht aus der Datenbank
echo "Content '{$this->titel}' wurde gelöscht.\n";
$this->id = null;
return true;
}
// Abstrakte Methoden, die von allen Content-Typen implementiert werden müssen
abstract public function getContentTyp(): string;
abstract public function validieren(): bool;
}
// Konkrete Content-Typen
class Artikel extends Content implements Darstellbar {
private string $inhalt;
private array $tags;
public function __construct(string $titel, string $autor, string $inhalt, array $tags = []) {
parent::__construct($titel, $autor);
$this->inhalt = $inhalt;
$this->tags = $tags;
}
public function getContentTyp(): string {
return 'Artikel';
}
public function validieren(): bool {
return strlen($this->titel) > 0 && strlen($this->inhalt) > 50;
}
public function alsHTML(): string {
$tagStr = implode(', ', $this->tags);
return "<article>
<h1>{$this->titel}</h1>
<p class='metadata'>Von {$this->autor} am {$this->erstelltAm->format('d.m.Y')}</p>
<div class='content'>{$this->inhalt}</div>
<div class='tags'>Tags: {$tagStr}</div>
</article>";
}
public function alsText(): string {
$tagStr = implode(', ', $this->tags);
return "{$this->titel}\n" .
"Von {$this->autor} am {$this->erstelltAm->format('d.m.Y')}\n\n" .
"{$this->inhalt}\n\n" .
"Tags: {$tagStr}";
}
}
class Video extends Content implements Darstellbar {
private string $url;
private int $dauer; // in Sekunden
private string $beschreibung;
public function __construct(string $titel, string $autor, string $url, int $dauer, string $beschreibung = '') {
parent::__construct($titel, $autor);
$this->url = $url;
$this->dauer = $dauer;
$this->beschreibung = $beschreibung;
}
public function getContentTyp(): string {
return 'Video';
}
public function validieren(): bool {
return filter_var($this->url, FILTER_VALIDATE_URL) !== false && $this->dauer > 0;
}
public function alsHTML(): string {
$dauerFormatiert = sprintf('%02d:%02d', floor($this->dauer / 60), $this->dauer % 60);
return "<div class='video'>
<h2>{$this->titel}</h2>
<p>Dauer: {$dauerFormatiert}</p>
<iframe src='{$this->url}' width='560' height='315' frameborder='0'></iframe>
<p>{$this->beschreibung}</p>
</div>";
}
public function alsText(): string {
$dauerFormatiert = sprintf('%02d:%02d', floor($this->dauer / 60), $this->dauer % 60);
return "{$this->titel} [VIDEO - {$dauerFormatiert}]\n" .
"URL: {$this->url}\n" .
"{$this->beschreibung}";
}
}
// Verwendung des CMS
$artikel = new Artikel(
"PHP 8 Features",
"Max Mustermann",
"PHP 8 bringt viele neue Features wie Union Types, Attribute und mehr...",
["PHP", "Programmierung", "Web-Entwicklung"]
);
$video = new Video(
"PHP-Tutorial: OOP Grundlagen",
"Erika Musterfrau",
"https://example.com/videos/php-oop-tutorial",
1250, // 20:50 Minuten
"In diesem Video lernen Sie die Grundlagen der objektorientierten Programmierung in PHP."
);
// Content-Management-Funktionen
function veröffentlicheContent(Content $content): void {
if ($content->validieren()) {
$content->speichern();
echo "{$content->getContentTyp()} wurde veröffentlicht!\n";
} else {
echo "Validierungsfehler: {$content->getContentTyp()} kann nicht veröffentlicht werden.\n";
}
}
function renderContent(Darstellbar $content, string $format = 'html'): string {
return $format === 'html' ? $content->alsHTML() : $content->alsText();
}
// Demo
veröffentlicheContent($artikel);
veröffentlicheContent($video);
echo "\nArtikel als HTML:\n";
echo renderContent($artikel);
echo "\nVideo als Text:\n";
echo renderContent($video, 'text');In diesem Beispiel:
Persistierbar und Darstellbar sind
Interfaces, die verschiedene Fähigkeiten definierenContent ist eine abstrakte Klasse, die grundlegende
Implementierungen bereitstellt und weitere Anforderungen definiertArtikel und Video sind konkrete Klassen,
die sowohl von Content erben als auch
Darstellbar implementierenveröffentlicheContent und
renderContent nutzen Polymorphismus durch
Typ-Deklarationen