Websiteentwicklung: PHP: Magische Methoden

Aus Wikibooks

Magische Methoden[Bearbeiten]

Es gibt einige Methoden, die von PHP in speziellen Fällen aufgerufen werden. Jede Klasse besitzt diese automatisch, sie können aber bei der Deklaration auch mit einer neuen Funktionsweise und anderen Parametern überschrieben werden. Diese Methoden werden Magic Methods genannt.

__construct[Bearbeiten]

Wann immer man ein neues Objekt erzeugt, wird der entsprechende Konstruktor der Klasse aufgerufen. Er wird in der Regel dafür verwendet, Eigenschaften des neuen Objektes festzulegen.

Nehmen wir zum Beispiel unsere Welt-Klasse aus dem vorangegangenen Kapitel: Wir haben eine Methode setzeBaumhoehe implementiert, die nur richtig funktionieren kann, wenn wir vorher einen Baum gepflanzt haben. Deshalb müsste nun vorher überprüft werden, dass sich hinter der Eigenschaft $baum auch eine Instanz der Klasse Baum versteckt. Da alle Methoden der Klasse Welt mit einem Objekt $baum arbeiten, wollen wir der Welt schon beim Entstehungszeitpunkt sagen, welchen Baum sie verwenden soll. Dazu ändern wir die Deklaration zu folgender um:

<?php
class Welt {
  protected $baum;
 
  public function __construct(Baum $baum) {
    $this->baum = $baum;
  }
 
  public function setzeBaumhoehe() {
    $this->baum->hoehe = 10;
  }
}
?>

Wie du siehst, haben wir einfach die Methode pflanzeBaum in __construct umbenannt. Um nun eine Welt zu erzeugen, verwenden wir folgenden Code:

<?php
require_once 'Baum.php';
require_once 'Welt.php';
 
$bar = new Baum();
$welt = new Welt($bar);
?>

Wir übergeben unserer Welt also mit der Erzeugung des Objekts den Baum. Natürlich können wir den Baum auch weiterhin optional machen, dazu setzen wir den Parameter $baum im Konstruktor auf den Standardwert null. Allerdings müssten wir nun wieder eine Prüfmethode implementieren, die sicherstellt, dass ein Baum existiert, wenn wir mit ihm arbeiten wollen.

Vielleicht wunderst du dich, warum du einen Konstruktor als public definieren musst, schließlich wird der Konstruktor immer bei der Erzeugung des Objekts aufgerufen. Es gibt allerdings einige Spezialfälle, in denen ein Objekt aus der eigenen Klasse heraus erzeugt wird – zum Beispiel im Singleton-Pattern.

__destruct[Bearbeiten]

Der Gegenspieler vom Konstruktor ist der Destruktor. Er wird aufgerufen, wenn das Objekt aus dem globalen Speicher gelöscht wird. Erweitern wir die Welt-Klasse um folgende Methode:

public function __destruct() {
  echo 'Ein(e) '.__CLASS__.' wurde zerstört!';
}

Wenn du jetzt deinen Code von oben aufrufst, wird ausgegeben:

Ein(e) Welt wurde zerstört!

Das liegt daran, dass bei Beendigung des Skriptes immer die Destruktoren der Objekte aufgerufen werden. Man kann allerdings auch den Destruktor auslösen, indem man das Objekt mittels der Funktion unset zerstört. Und als dritte Möglichkeit steht der direkte Aufruf der Funktion __destruct zur Verfügung, bei der allerdings das Objekt nicht gelöscht, sondern lediglich der Code des Destruktors ausgeführt wird.

__call und __callStatic[Bearbeiten]

Methoden müssen noch nicht einmal definiert sein, um aufgerufen zu werden, sie können auch dynamisch zur Laufzeit erzeugt werden (sog. Overloading). Nehmen wir an, wir rufen in unserem Skript die Methode drehe_um_winkel auf:

<?php
require_once 'Baum.php';
require_once 'Welt.php';

$ber = new Baum();
$welt = new Welt($ber); 
$welt->drehe_um_winkel(50);
?>

Selbstverständlich erzeugt das eine Fehlermeldung:

 Fatal error: Call to undefined method Welt::drehe_um_winkel()

Allerdings können wir die Magische Methode __call in die Klasse Welt einfügen, um auf alle Methodenaufrufe zu reagieren, wenn die Methode nicht erreichbar ist:

public function __call($name, $arguments) {
  if (strtolower($name) == 'drehe_um_winkel')
  {
    echo 'Und sie dreht sich doch, um den Winkel '. $arguments[0].'!';
  } else {
    throw new Exception('Funktion '.$name.' ist nicht definiert in Klasse '.__CLASS__);
  }
}

Was macht diese Funktion? Nun, wird eine Methode aufgerufen, die nicht erreichbar ist (also z.B. eine als protected deklarierte Methode von außerhalb aufgerufen, oder eine, die in der Klasse nicht existiert), wird die Magische Methode __call aufgerufen, mit dem Namen dieser nicht erreichbaren Methode $name sowie deren Parameter als Array $arguments. In diesem Beispiel wird, sofern man versucht, die Methode drehe_um_winkel aufzurufen, eine Nachricht inklusive des ersten Arguments ausgegeben, ansonsten eine Exception geworfen (mehr zu Exceptions im entsprechenden Kapitel).

Als Pendant zu Objektmethoden können auch statische Methoden dynamisch erzeugt werden, dazu wird die Magische Methode __callStatic aufgerufen, diese muss logischerweise auch als statisch deklariert werden. Mehr zu statischen Methoden findest du im Kapitel Statische Eigenschaften und Methoden.

Dynamisch erzeugte Methoden solltest du nur in ganz speziellen Bedarfsfällen erzeugen. Es ist immer besser, wirkliche Methoden zu implementieren, anstatt sich über Kontrollstrukturen zu behelfen. So muss man bei dynamischen Methoden Elemente wie Sichtbarkeit, Type Hinting oder Überprüfung auf richtige Anzahl der Parameter selbst programmieren.

__get, __set, __isset und __unset[Bearbeiten]

Es gibt entsprechende Magische Methoden zum Overloading auch für nicht erreichbare Klasseneigenschaften. So kann man z.B. eine Eigenschaft als array deklarieren und mit den magischen Methoden $objekt->eigenschaft in $objekt->array[$eigenschaft] übersetzen.

Eine Übersicht über diese Methoden liefert die folgende Tabelle:

Methodensignatur wird aufgerufen, wenn eine nicht erreichbare Eigenschaft... Parameter und Hinweise
void __set (string $name, mixed $value) ...auf einen Wert gesetzt werden soll $name = Name der Eigenschaft, $value = zu setzender Wert
mixed __get (string $name) ...gelesen werden soll $name = Name der Eigenschaft
bool __isset (string $name) ...als Parameter der Funktionen isset oder unset verwendet wird $name = Name der Eigenschaft; Methode muss true oder false zurückgeben
__unset (string $name) ...als Parameter der Funktion unset verwendet wird $name = Name der Eigenschaft

__sleep und __wakeup[Bearbeiten]

Objekte (und auch andere Daten) können, z.B. um sie zu speichern, mittels der Funktion serialize in einen besonderen String umgewandelt werden, der mit der Funktion unserialize wieder in die Ursprungsform verwandelt wird. Damit man vorher nochmal „aufräumen“ kann, gibt es die Magische Methode __sleep. Sie muss ein Array zurückgeben, das die Namen der Eigenschaften des Objekts beinhaltet, die von serialize gespeichert werden sollen.

Unser Baum etwa soll sich immer wieder regenerieren, wenn er zu Bett geht (also serialisiert wird). Daher brauchen wir seine Gesundheit gar nicht speichern:

public function __sleep() {
  return array('hoehe');
}

Jetzt setzen wir noch die Gesundheit nach dem Aufwachen auf 100:

public function __wakeup() {
  $this->gesundheit = 100;
}

Und schon haben wir einen erholten Baum:

<?php
require_once 'Baum.php';

$ber = new Baum();
$speicher = serialize($ber);
$ber = unserialize($speicher);
echo $ber->sage_mir_dein_wohlbefinden();
?>
Mir geht es super.

__toString[Bearbeiten]

Mit der Magischen Methode __toString können wir kontrollieren, wie sich ein Objekt verhält, das in den Datentyp String konvertiert werden soll, beispielsweise bei Verwendung der Funktion strlen:

class Welt {
  protected $name;
  protected $baum;

  public function __construct(Baum $baum, $name = 'Erde') {
    $this->baum = $baum;
    $this->name = $name;
  }

  public function __toString() {
    return $this->name;
  }

// Restliche Methoden

Nach Erzeugung eines entsprechenden Objektes ohne Angabe eines Namens wird uns die Funktion strlen($welt) die Zeichenanzahl 4 zurückgeben, da sie auf den String $name verwiesen wird, der "Erde" enthält.

__set_state[Bearbeiten]

__clone[Bearbeiten]

Nun genügt uns eine Welt nicht, wir wollen noch eine zweite Welt erschaffen, die genauso wie die erste aussieht. Dazu können wir, wie im Kapitel Methoden gezeigt, das Schlüsselwort clone verwenden:

<?php
require_once 'Baum.php';
require_once 'Welt.php';

$ber = new Baum();
$welt = new Welt($ber);
$welt2 = clone $welt;
$welt2->setzeBaumhoehe(10);
echo $ber->hoehe;
?>

Als Ausgabe erhalten wir

10

denn durch clone wird eine sogenannte flache Kopie erzeugt, d.h. wir haben zwar zwei Objekte der Klasse Welt, die teilen sich aber per Referenz dieselbe Instanz von Baum. Der Baum steht also zur gleichen Zeit auf zwei verschiedenen Welten. Da wir nicht Quantenphysik betreiben wollen, ändern wir dieses Verhalten:

// Vorhergehender Code der Klasse Welt

  public function __clone() {
    $this->baum = clone $this->baum;
  }

Nun wird nach dem Klonen die Funktion __clone aufgerufen und das Objekt $baum ebenfalls kopiert, somit haben wir eine tiefe Kopie erzeugt.

__invoke (PHP 5.3)[Bearbeiten]

Die Magische Methode __invoke ist neu mit der PHP-Version 5.3 hinzugekommen. Hier kann man das Verhalten eines Objektes bestimmen, das wie eine Funktion aufgerufen wird:

class Ausgabe {

  public function __invoke($text) {
    echo $text;
  }

$ausgabe = new Ausgabe();
$ausgabe('Hallo Welt!');

Ausgabe:

Hallo Welt!

Aufrufbare Objekte geben true zurück, wenn man auf sie die Funktion is_callable anwendet.

PHP-Dokumentation[Bearbeiten]

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