Websiteentwicklung: PHP: Vererbung

Aus Wikibooks

Vererbung[Bearbeiten]

Eine der wichtigsten Möglichkeiten von Klassen (wenn nicht DIE wichtigste) ist die Vererbung. Damit können Klassen auf anderen Klassen aufbauen, sie erben alle Eigenschaften und Methoden der Elternklasse. Schauen wir uns dazu noch einmal unsere bisherige Klasse „Baum“ an:

class Baum {
 
  public $hoehe;
  protected $gesundheit;
 
  const  KANN_REDEN = true;
 
  public function sagHallo() {
 
    if (self::KANN_REDEN == true) { 
    echo 'Hallo Welt!';
    return true; // zurückgeben, dass der Sprechversuch geklappt hat
    } else {
    return false; // Der Baum kann gar nicht reden
    }
 
  }
 
  public function sage_mir_dein_wohlbefinden() {
    if ( $this->gesundheit > 80 ) return 'Mir geht es super.';
    if ( $this->gesundheit > 40 ) return 'Ich hatte schon bessere Tage.';
    if ( $this->gesundheit > 0 )  return 'Mir geht es echt mies.';
    return 'Ich bin ein Tisch.';                   /*  tritt nur ein wenn $this->gesundheit
                                                      *  kleiner oder gleich 0 ist. */
  }
 
}

Das Schlüsselwort extends[Bearbeiten]

Vererbung geschieht in PHP mittels des Schlüsselworts extends:

class Kastanie extends Baum {
}

$kastanie = new Kastanie();
echo $kastanie->sage_mir_dein_wohlbefinden();

Ausgabe:

Ich bin ein Tisch.

Wie du siehst, hat die Kastanie alle Methoden der Klasse Baum geerbt.

Überschreiben von Methoden und Eigenschaften[Bearbeiten]

Eigenschaften können von der Kindklasse auch überschrieben und so mit neuen Werten belegt werden:

class Kastanie extends Baum {

protected $gesundheit = 100;

}

$kastanie = new Kastanie();
echo $kastanie->sage_mir_dein_wohlbefinden();
Mir geht es super.

Genau so können Methoden ebenfalls überschrieben werden, beispielsweise, weil unsere Kastanie unhöflich ist und niemals Hallo sagt:

class Kastanie extends Baum {

  protected $gesundheit = 100;

  public function sagHallo($sender) {
    echo 'Erzähler: ' . $sender . ' erhält keine Antwort von ' . get_class($this);
  }
}

$kastanie = new Kastanie();
$kastanie->sagHallo('Foo');

So können auch neue Parameter der Methode hinzugefügt werden, in diesem Falle ist es der Name desjenigen, der die Kastanie begrüßt.

Form der Vererbung[Bearbeiten]

PHP kann keine Mehrfachvererbung, d.h., eine Klasse kann immer nur von einer einzigen Klasse abgeleitet werden. Allerdings kannst du Vererbungen aneinander ketten:

class Rosskastanie extends Kastanie {
}

Somit ist die Vererbungssequenz: Baum => Kastanie => Rosskastanie.

Selbstverständlich kann aber eine Elternklasse mehrere Kinder haben:

class Buche extends Baum {};
class Esche extends Baum {};
// ...

Type Hinting und Vererbung[Bearbeiten]

Abgeleitete Klassen erfüllen Type Hints der Elternklasse. Die Methode

public function __construct(Baum $baum) {
  $this->baum = $baum;
}

in der Klasse Welt kann auch mit einer Rosskastanie gültig aufgerufen werden:

$kastanie = new Rosskastanie();
$welt = new Welt($kastanie);

Auf überschriebene Methoden zugreifen[Bearbeiten]

Überschriebene Methoden werden zwar vollständig von der neuen Methode ersetzt, allerdings kann noch auf sie zugegriffen werden. Nehmen wir an, unser neuester Zuwachs bei den Bäumen ist ein Spiegelbaum, der außer "Hallo" alles rückwärts sagt. Natürlich muss er dann auch die Sätze der Methode sage_mir_dein_wohlbefinden() rückwärts aussprechen. Wir könnten jetzt hingehen und in eine überschreibende Methode diese Funktion (mittels strrev()) einbauen:

class Spiegelbaum extends Baum {
public function sage_mir_dein_wohlbefinden() {
    if ( $this->gesundheit > 80 ) return strrev('Mir geht es super.');
    if ( $this->gesundheit > 40 ) return strrev('Ich hatte schon bessere Tage.');
    if ( $this->gesundheit > 0 )  return strrev('Mir geht es echt mies.');
    return strrev('Ich bin ein Tisch.');                   /*  tritt nur ein wenn $this->gesundheit
                                                      *  kleiner oder gleich 0 ist. */
  }
}

Das widerspricht aber guter Klassenmodellierung, denn wir mussten die komplette Logik kopieren. Was ist denn, wenn jetzt noch ein neuer Satz eingefügt werden soll, z.B. bei der Bedingung "Gesundheit > 100"? Wir müssten sowohl die Klasse Baum als auch die Klasse Spiegelbaum anpassen. Da hätten wir uns die Vererbung auch gleich ganz sparen können!

Zum Glück kennt PHP das Schlüsselwort parent. Damit wird ermöglicht, auf Methoden der Elternklasse zuzugreifen. Gefolgt wird parent vom Scope Resolution Operator, weil die Methode nicht wirklich dem Objekt angehört, sondern sich nur in der Klasseninformation der Elternklasse befindet. So sieht dann die Klasse Spiegelbaum aus:

class Spiegelbaum extends Baum {
  public function sage_mir_dein_wohlbefinden() {
    return strrev(parent::sage_mir_dein_wohlbefinden());
  }
}


Tipp:

Besonders hilfreich ist diese Möglichkeit bei Objekten, die andere Objekte per Konstruktor in sich kapseln (z.B. der Controller im MVC-Modell), aber davon abgeleitete Klassen auch mehr Objekte haben können:

class Controller {
  protected $model;
  protected $view;

  public function __construct(Model $model, View $view) {
    $this->model = $model;
    $this->view  = $view;
  }
}

class WithloggerController extends Controller {
  protected $logger;

  public function __construct(Model $model, View $view, Logger $logger) {
    $this->logger = $logger;
    parent::__construct($model, $view);
  }
}


Abstrakte Klassen und Methoden[Bearbeiten]

Unsere Klasse Baum ist eigentlich nicht dafür gedacht, einfach so verwendet zu werden. Schließlich gibt es nicht den Baum, sondern nur die verschiedenen Unterklassen wie Kastanien, Buchen, etc. Der Begriff "Baum" ist doch viel zu abstrakt! Genau das können wir PHP auch sagen, mit dem Schlüsselwort abstract:

abstract class Baum {
// vorhergehender Code
}

Jetzt ist es unmöglich, ein Objekt der Klasse Baum zu erzeugen:

  Fatal error: Cannot instantiate abstract class Baum

Zuvor müssen wir also z.B. die Klasse Buche von Baum ableiten:

class Buche extends Baum {
}

Besonders viel haben wir dadurch allerdings nicht gewonnen. Die Klasse Buche ist im Grunde das gleiche wie die Klasse Baum - alle Methoden und Eigenschaften wurden übernommen. Aber angenommen, die Methode sagHallo soll zwingend überschrieben werden? Dann geht das auch:

abstract class Baum {

  abstract public function sagHallo();

// restlicher Code
}

Abstrakte Methoden enthalten nur den Methodenkopf, abgeschlossen von einem Semikolon, können also selbst keinen Code enthalten. Klassen, die auch nur eine abstrakte Methode enthalten, müssen ebenfalls als abstrakt definiert werden.

Bei Erzeugung einer neuen Buche gibt es nun eine Fehlermeldung:

 Fatal error: Class Buche contains 1 abstract method and must therefore be declared abstract
 or implement the remaining methods (Baum::sagHallo)

PHP sagt dir, welches deine Optionen sind: Entwender du deklarierst die Klasse Buche ebenfalls als abstrakt (könntest dann aber wiederum kein Objekt davon erzeugen), oder du erweiterst diese um die Methode sagHallo(). Diese Methode muss exakt den gleichen Methodenkopf haben:

class Buche extends Baum {
  public function sagHallo() {
    echo '"Hallo", sagte die Buche.';
  }
}

Finale Klassen und Methoden[Bearbeiten]

Genau so wie festgelegt werden kann, dass Klassen und Methoden überschrieben werden müssen, kann man mit dem Schlüsselwort final bestimmen, dass die Klasse oder Methode nicht überschrieben werden darf:

class Baum {
  final public function sage_mir_dein_wohlbefinden()
  {
    // Inhalt der Methode
  }
}

class Buche extends Baum {
  public function sage_mir_dein_wohlbefinden()
  {
    return false;
  }
}

Das führt zu einer Fehlermeldung:

 Fatal error: Cannot override final method Baum::sage_mir_dein_wohlbefinden()

Das geht auf die gleiche Weise für Klassen:

final class Baum {
}

class Kastanie extends Baum {
}

Auch das ist ein Fehler:

 Fatal error: Class Kastanie may not inherit from final class (Baum)

PHP-Dokumentation[Bearbeiten]

Folgende Abschnitte der PHP-Dokumentation wurden in diesem Kapitel behandelt: