EMZETT.
Login

Kurz: class Hund extends Tier { public function laut(): string { return “Wuff”; } }

Teil des Kurses PHP

Kapitel 8 von 15 im Kurs PHP. Mit Fortschritt, Quiz und Zertifikat auf der Lernseite.

Vererbung

<?php
abstract class Tier
{
    public function __construct(protected string $name) {}
    abstract public function laut(): string;
    public function vorstellen(): string { return "{$this->name} sagt {$this->laut()}"; }
}
 
class Hund extends Tier
{
    public function laut(): string { return "Wuff"; }
}
 
class Katze extends Tier
{
    public function __construct(string $name, private int $leben = 9)
    {
        parent::__construct($name);
    }
    public function laut(): string { return "Miau"; }
    public function vorstellen(): string
    {
        return parent::vorstellen() . " ({$this->leben} Leben)";
    }
}
 
foreach ([new Hund("Rex"), new Katze("Mimi")] as $t) echo $t->vorstellen(), "\n";
var_dump(new Hund("x") instanceof Tier, get_parent_class(new Hund("x")));

Ausgabe:

Rex sagt Wuff
Mimi sagt Miau (9 Leben)
bool(true)
string(4) "Tier"
  • extends erbt; parent:: ruft die Elternversion auf
  • abstract Klassen sind nicht instanziierbar, abstrakte Methoden müssen überschrieben werden
  • final verbietet Vererbung bzw. Überschreiben
  • Bevorzuge Komposition vor tiefer Vererbung

Interfaces

Ein Interface legt Methoden fest, die implementierende Klassen anbieten müssen:

<?php
interface Formatierbar
{
    public function format(): string;
}
interface Vergleichbar
{
    public function vergleiche(self $anderer): int;
}
 
class Preis implements Formatierbar, Vergleichbar
{
    public function __construct(private float $betrag) {}
    public function format(): string { return number_format($this->betrag, 2, ",", ".") . " €"; }
    public function vergleiche(Vergleichbar $anderer): int { return $this->betrag <=> $anderer->betrag; }
}
 
function drucke(Formatierbar ...$dinge): void { foreach ($dinge as $d) echo $d->format(), "\n"; }
drucke(new Preis(1234.5), new Preis(9.99));
var_dump((new Preis(5))->vergleiche(new Preis(7)));

Ausgabe:

1.234,50 €
9,99 €
int(-1)

Eine Klasse kann mehrere Interfaces implementieren, aber nur eine Elternklasse erben. Eingebaute Interfaces: Countable, ArrayAccess, IteratorAggregate, JsonSerializable, Stringable.

<?php
class Liste implements Countable, IteratorAggregate, ArrayAccess, JsonSerializable
{
    private array $d = [];
    public function count(): int { return count($this->d); }
    public function getIterator(): Iterator { return new ArrayIterator($this->d); }
    public function offsetExists(mixed $o): bool { return isset($this->d[$o]); }
    public function offsetGet(mixed $o): mixed { return $this->d[$o] ?? null; }
    public function offsetSet(mixed $o, mixed $v): void { if ($o === null) $this->d[] = $v; else $this->d[$o] = $v; }
    public function offsetUnset(mixed $o): void { unset($this->d[$o]); }
    public function jsonSerialize(): mixed { return ["items" => $this->d]; }
}
$l = new Liste();
$l[] = "a"; $l[] = "b"; $l["x"] = "c";
echo count($l), " ", $l[0], " ", isset($l["x"]) ? "ja" : "nein", " ", json_encode($l), "\n";
foreach ($l as $k => $v) echo "$k=$v ";
echo "\n";

Ausgabe:

3 a ja {"items":{"0":"a","1":"b","x":"c"}}
0=a 1=b x=c

Traits

Traits teilen Code zwischen Klassen ohne Vererbung (“horizontale Wiederverwendung”):

<?php
trait Zeitstempel
{
    private ?string $erstellt = null;
    public function markiere(string $zeit): static { $this->erstellt = $zeit; return $this; }
    public function erstelltAm(): ?string { return $this->erstellt; }
}
trait Logger
{
    public function log(string $m): void { echo "[", static::class, "] $m\n"; }
}
 
class Artikel { use Zeitstempel, Logger; }
class Kommentar { use Zeitstempel; }
 
$a = (new Artikel())->markiere("2025-01-01");
$a->log("angelegt " . $a->erstelltAm());
echo (new Kommentar())->erstelltAm() ?? "kein Datum", "\n";

Ausgabe:

[Artikel] angelegt 2025-01-01
kein Datum

Abstrakt, Interface oder Trait?

ZielMittel
”Ist ein” mit gemeinsamem Codeabstrakte Klasse / Vererbung
Fähigkeit zusichern, Typ prüfenInterface
Code teilen ohne “ist ein”Trait

Namespaces und Autoloading

Mit namespace vermeidest du Namenskonflikte; use importiert Klassen. Composer lädt Klassen automatisch (PSR-4: Namespace = Verzeichnispfad). Mehr dazu im Kapitel über Composer.

<?php
namespace App\Modell;
 
class Nutzer {}
 
echo Nutzer::class, " ", \strlen("abc"), "\n";

Ausgabe:

App\Modell\Nutzer 3

Merke

  • extends, parent::, abstract, final
  • Interfaces garantieren Methoden; mehrere sind möglich
  • Traits teilen Code ohne Vererbung
  • Eingebaute Interfaces (Countable, ArrayAccess, IteratorAggregate, JsonSerializable) machen Objekte “native”
  • Namespaces + Composer-Autoloading organisieren Klassen

Übungsaufgabe

Definiere ein Interface Form mit flaeche(), zwei Klassen und eine Funktion, die die Gesamtfläche einer Liste berechnet.

Quiz zur Selbstkontrolle

Weiter im Kurs

Zurück: Klassen und Objekte

Weiter: Fehlerbehandlung und Exceptions

Alle Kapitel: PHP im Überblick