einefaches Plugin-System mit Abhängigkeiten (rekursion)
reiner hohn
- php
0 dedlfix
Hallo Forum,
ich versuche gerade ein sehr einfaches Plugin-System zu schreiben, welches auf Abhängigkeiten achtet und diese ggf. nach läd. Das Probelm ist, dass wenn ein Plugin vom dem anderen abhängig ist und das andere von dem einen, läuft alles out of memory. Zur Veranschaulichung:
class app {
protected static $loaded_plugins = array();
public function load ($str) {
if(!isset(self::$loaded_plugins[$str])) {
$class = 'plugin_'.$str;
self::$loaded_plugins[$str] = new $class();
}
}
}
class plugin extends app {
protected $required_plugins = array();
public function __construct () {
foreach($this->required_plugins as $plugin)
$this->load($plugin);
}
public function __destruct () {
}
}
class plugin_foo extends plugin {
protected $required_plugins = array('bar');
}
class plugin_bar extends plugin {
protected $required_plugins = array('abc');
}
class plugin_abc extends plugin {
protected $required_plugins = array('foo');
}
$app = new app();
$app->load('foo');
Wenn versucht wird das Plugin "foo" zu laden, wird festgestellt das selbiges das Plugin "bar" benötigt. Es wird also versucht zuerst "bar" zu initialisieren. Dabei wird widerum festgestellt das dafür "abc" benötigt wird. Beim Versuch dieses zu initialisieren, wird abermals festgestellt das dieses auch von "foo" abhängig (was wir unsprünglich laden wollten) ist, dies aber wegen den Abhängigkeiten noch nicht initialisiert werden konnte -> wir drehen uns im Kreis -> Memory Limit erreicht.
Ist ja gut das mir das soweit schon mal bewusst ist, aber mir fehlt hier eine clevere Idee das Problem zu lösen. Ich hoffe mit kann jemand auf die Spünge helfen.
Gruss Reiner
Tach!
public function load ($str) {
if(!isset(self::$loaded_plugins[$str])) {
$class = 'plugin_'.$str;
self::$loaded_plugins[$str] = new $class();
}
}
}
Du hast hier bereits eine Prüfung auf Vorhandensein der Klasse eingebaut. Nur kommt diese bei deinem zirkulären Bezug nicht zum Einsatz. Erst wird die Konstruktorfunktion ausgeführt, die ruft load() auf, woraufhin die Konstruktorfunktion ... Eine Lösung kann sein, erst das Objekt zu erzeugen und in den loaded_plugins ablegen, dann load() aufrufen. Die Konstruktoren dürfen dann aber keinerlei Funktionalität der noch nicht geladenen anderen Plugins verwenden.
Nach dem Clean-Code-Grundsatz "Don't look for things!" wäre jedoch eine andere Vorgehensweise besser. Wenn es Abhängigkeiten gibt, fordert sie das Plugin über Pflichtparameter ein. Das heißt, der Verwender hat dafür zu sorgen, dass alles benötigte erstellt und übergeben wird. Dann kann solch eine Rekursion nicht entstehen, weil er schon beim Code erstellen feststellt, dass er das so nicht hinbekommt. Das Übergabe-Prinzip hört auf den Namen Dependency Injection (DI). Wenn du allerdings ein DI-Frameworks mit automatischer Abhängigkeitsauflösung einzusetzen gedenkst, kann das Problem wieder auftreten, denn dann rennt der Auflösungsmechanismus in die Schleife rein, wenn er ungünstig implementiert ist.
dedlfix.
Dank. Ich hab das jetzt wie folgt gelöst:
class app {
protected static $loaded_plugins = array();
protected static $queue = array();
public function load ($str) {
if(!isset(self::$loaded_plugins[$str]) && !in_array($str,self::$queue)) {
$class = 'plugin_'.$str;
self::$loaded_plugins[$str] = new $class();
}
}
public function loaded () {
return self::$loaded_plugins;
}
}
class plugin extends app {
protected $required_plugins = array();
public function __construct () {
parent::$queue[] = substr(get_class($this), strripos(get_class($this), '_') + 1); // name des plugins welches unprügnlich geladen werden sollte
foreach($this->required_plugins as $plugin)
$this->load($plugin);
parent::$queue = array();
}
public function __destruct () {
}
}