Abschnitt 6 · Lektion 7
Klasse und Objekt
Du hast in Abschnitt 5 gelernt, wie man mit Arrays Daten zusammenhält, und in diesem Abschnitt, wie man Funktionen schreibt. Beides zusammen reicht erstaunlich weit. Es hat nur eine Schwachstelle, und die zeigt sich erst, wenn ein Programm größer wird.
Das Problem: die Funktionen wissen zu viel und PHP weiß zu wenig
<?php
// So haettest du es bis hierher gebaut: die Daten in einem Array,
// die Funktionen daneben.
function notizZeile(array $notiz): string
{
return $notiz["titel"] . " (" . ($notiz["erledigt"] ? "erledigt" : "offen") . ")";
}
function notizErledigen(array $notiz): array
{
$notiz["erledigt"] = true;
return $notiz;
}
$milch = ["titel" => "Milch", "erledigt" => false];
$brot = ["titel" => "Brot", "erledigt" => true];
echo notizZeile($milch), "\n";
echo notizZeile($brot), "\n";
$milch = notizErledigen($milch);
echo notizZeile($milch), "\n"; Sieh dir an, was notizZeile() alles voraussetzt. Sie verlangt ein Array, in dem ein Schlüssel
titel steht und ein Schlüssel erledigt. Steht dort etwas anderes, merkt PHP das erst in dem
Moment, in dem die Funktion darauf zugreift, und dann ist es eine Warnung mitten im Lauf.
Die Typangabe array hilft dabei kein bisschen. Sie sagt „hier kommt ein Array”, nicht „hier kommt
ein Array mit genau diesen zwei Schlüsseln”. Aus PHPs Sicht ist ein Einkaufszettel mit dreißig
Einträgen genauso ein Array wie deine Notiz.
Dazu kommt die Sache mit notizErledigen(). Ein Array geht als Kopie in eine Funktion, wie du
in Lektion 6.4 gesehen hast. Die Funktion kann die Notiz draußen also gar nicht ändern, sie muss
eine neue zurückgeben, und der Aufrufer muss daran denken, sie wieder zuzuweisen. Vergisst er es,
passiert nichts Schlimmes, nur eben auch nichts.
Die Klasse ist der Bauplan, das Objekt ist das Gebaute
<?php
class Notiz
{
public string $titel;
public bool $erledigt;
public function __construct(string $titel, bool $erledigt)
{
$this->titel = $titel;
$this->erledigt = $erledigt;
}
public function zeile(): string
{
return $this->titel . " (" . ($this->erledigt ? "erledigt" : "offen") . ")";
}
public function erledigen(): void
{
$this->erledigt = true;
}
}
$milch = new Notiz("Milch", false);
$brot = new Notiz("Brot", true);
echo $milch->zeile(), "\n";
echo $brot->zeile(), "\n";
$milch->erledigen();
echo $milch->zeile(), "\n"; Eine Klasse beschreibt, wie etwas aussieht und was es kann. Sie ist ein Bauplan, kein Ding:
Vom Bauplan eines Hauses kann niemand einziehen. Das Ding entsteht mit new, und es heißt
Objekt.
In der Klasse stehen zwei Sorten Zeilen:
- Eigenschaften sind die Daten.
public string $titel;sagt: Jede Notiz hat einen Titel, und der ist eine Zeichenkette. Das ist genau die Zusage, die dem Array gefehlt hat. - Methoden sind die Funktionen, die zu diesen Daten gehören. Sie werden mit dem Pfeil
aufgerufen:
$milch->zeile().
Der Pfeil -> ist dabei kein neues Konzept, sondern nur eine andere Schreibweise für „da drin”.
Wo beim Array $notiz["titel"] stand, steht jetzt $notiz->titel, ohne Anführungszeichen und ohne
Klammern.
Und $this? Das ist das Objekt selbst, von innen gesehen. In zeile() weiß PHP nicht von sich aus,
welche Notiz gemeint ist, es könnten ja tausend geben. $this->titel heißt deshalb: der Titel von
genau der Notiz, an der diese Methode gerade aufgerufen wurde.
__construct ist eine Methode mit besonderem Namen. PHP ruft sie beim new von selbst auf, und
sie ist der Ort, an dem ein Objekt vollständig wird. Mehr dazu in Lektion 13.1, wenn es mit den
Objekten richtig losgeht.
Jedes Objekt hat seinen eigenen Zustand
<?php
class Zaehler
{
public int $stand = 0;
public function hoch(): void
{
$this->stand = $this->stand + 1;
}
}
$links = new Zaehler();
$mitte = new Zaehler();
$rechts = new Zaehler();
$links->hoch();
$links->hoch();
$mitte->hoch();
echo "links: ", $links->stand, "\n";
echo "mitte: ", $mitte->stand, "\n";
echo "rechts: ", $rechts->stand, "\n"; Drei Objekte aus derselben Klasse, und jedes hat seinen eigenen $stand. Das ist der Unterschied
zwischen einer Klasse und einer Funktion mit static aus Lektion 6.4: Dort gibt es einen Behälter
für alle Aufrufe, hier bekommt jedes Objekt seinen eigenen.
Beachte auch, dass hoch() nichts zurückgibt. Sie muss nicht: Das Objekt geht nicht als Kopie
irgendwohin, $links ist ein Verweis auf das Objekt. Wer die Methode aufruft, ändert das
Original. Genau die Umständlichkeit von notizErledigen() fällt damit weg.
Was ein Tippfehler kostet
<?php
// Derselbe Tippfehler zweimal. Sieh dir im Terminal beide Kanaele an:
// die Ausgabe und den Fehlerkanal.
$notiz = ["titel" => "Milch", "erledigt" => false];
$notiz["erldigt"] = true;
echo "Schluessel im Array: ", implode(", ", array_keys($notiz)), "\n";
echo "Und erledigt ist: ";
var_dump($notiz["erledigt"]);
class Notiz
{
public string $titel = "Milch";
public bool $erledigt = false;
}
$objekt = new Notiz();
$objekt->erldigt = true;
echo "Und erledigt ist: ";
var_dump($objekt->erledigt); Beide Male steht erldigt statt erledigt, beide Male bleibt der richtige Wert unverändert. Der
Unterschied steht auf dem Fehlerkanal.
Beim Array passiert gar nichts. $notiz["erldigt"] = true; legt einen neuen Schlüssel an, das
ist eine völlig normale Sache, und PHP hat keine Möglichkeit zu wissen, dass du das nicht wolltest.
Der Array hat danach drei Schlüssel, und der Fehler wandert weiter, bis irgendwo eine Zahl falsch
ist. Im Reiter Debug kannst du ihn dabei beobachten: Nach Aufzeichnen steht ab der Zeile
mit dem Tippfehler erldigt: true als dritter Eintrag neben dem unveränderten erledigt: false,
beim Array wie beim Objekt.
Beim Objekt meldet sich PHP:
Deprecated: Creation of dynamic property Notiz::$erldigt is deprecated. Seit PHP 8.2 ist das
Anlegen einer Eigenschaft, die in der Klasse nicht steht, verwarnt, und in PHP 9 soll ein Fehler
daraus werden. Die Meldung nennt dabei beides beim Namen: die Klasse und die Eigenschaft.
Sei fair zu dir selbst: Das ist eine Warnung und kein Absturz, und das Programm läuft weiter. Aber eine Warnung ist unendlich viel mehr als gar nichts. Der nächste Abschnitt handelt von nichts anderem, und sein Kernsatz gilt schon hier: Ein Fehler, der sich meldet, ist der billigste Fehler, den es gibt.
Wann sich eine Klasse lohnt
Nicht immer. Ein Array mit drei Werten, das an einer einzigen Stelle benutzt wird, bleibt ein Array, und eine Funktion, die aus zwei Zahlen eine dritte macht, bleibt eine Funktion.
Die Klasse lohnt sich ab dem Moment, in dem dieselben Daten an mehreren Stellen auftauchen und mehrere Funktionen sich darauf verlassen, wie sie aussehen. Dann ist die Klasse der Ort, an dem diese Absprache einmal aufgeschrieben steht, statt in fünf Funktionsköpfen zu stecken.
Zum Mitnehmen
Bis hierher waren deine Daten Arrays, und die Funktionen darauf standen daneben und hofften das Beste. Eine Klasse legt beides zusammen. Der Gewinn zeigt sich nicht am schönen Code, sondern am ersten Tippfehler.
Jetzt du
Basis Konto, kostenlosZu dieser Lektion gehört eine Aufgabe. Du schreibst den Code selbst, und nach jedem Lauf sagt dir eine Prüfliste, was schon stimmt.
Dafür brauchst du das Basis Konto. Es kostet nichts, und ein Passwort gibt es auch nicht.
In diesem Kurs läuft dein Code auf einem Server. Dafür hat das Basis Konto 1 Stunde im Monat, mehr Zeit gibt es mit dem Premium Konto.
Was in dieser Lektion steckt
-
Artikel mit 4 Beispielen zum Ausprobieren
Steht hier, ohne Konto lesbar.
-
Aufgabe, dein Code läuft auf einem Server
Öffnet sich mit dem Basis Konto.