__call() zur Erweiterung von Factory-Klassen nutzen
Wenn eine vorhandene Klasse erweitert werden soll, kann sie einfach vererben und die neue Klasse instanziieren.
Anders sieht es bei Factory-Klassen aus, die je nach Parameter ein anderes Objekt zurückgeben (z. B. für eine Datenbank-Klasse).
Hier müsste man dann schon in jede der Klassen die neue Funktion einfügen, um sie auch in jedem Objekt verfügbar zu haben.
Die PHP5-Funktion __call() kann hier einiges an Arbeit abnehmen. Sie fängt Aufrufe von Methoden ab, die in der Klasse selbst nicht implementiert sind und nimmt dabei zwei Parameter entgegen: den Namen der Methode und ein Array von Parametern, die an die Methode übergeben werden sollen.
Wenn man ein System wie Pear::DB oder ADOdb nutzt, wird eine Erweiterung (z. B. eine Funktion insert($table, $values)) recht schwierig. Meist hat man hier eine Factory-Funktion oder -Klasse, die jeweils unterschiedliche Objekte zurückgeben.
Mit __call() kann man (in PHP5) die entsprechende Methode in der Factory-Klasse einbauen und alle anderen Methoden (sofern existent) an die aktuelle Klasse weiterleiten.
Ein Beispiel mit ADOdb:
Die original Factory-Klasse
static function getInstance()
{
static $instance = false;
if (!$instance || !is_a($instance, 'ADOConnection') {
$instance = newADOConnection('mysql');
$instance->connect('localhost', 'user', 'geheim', 'datenbank');
}
return $instance;
}
Damit wird je nach Datenbank-Typ ein anderes Objekt zurückgegeben, aber die Erweiterung müsste in allen Klassen vorgenommen werden.
Ein Ansatz mit __call()
class DB
{
protected $conn = null;
public function __construct()
{
$this->conn = newADOConnection('mysql');
$this->conn->connect('localhost', 'user', 'geheim', 'datenbank');
}
//Die neue Funktion
public function insert($table, $values)
{
$table = trim($table); //eventuell noch überprüfen
$sql = "INSERT INTO `$table`;
$sql .= ''; //mache hier irgendwas mit den Werten
return $this->conn->execute($sql);
}
//Hier werden alle anderen Methoden an $this->conn weitergeleitet
public function __call($method, $params)
{
if (method_exists($this->conn, $method) {
return call_user_func_array(array($this->conn, $method), $params);
} else {
//Gebe Fehler aus
}
}
}