18. August 2010

einfaches PHP Plugin System

18. August 2010 - Geschrieben von Martin - 6 Kommentare

Plugin Systeme, sie sind viel gefragt in der heutigen Zeit, jeder will seinen Code schön erweiterbar haben, doch viele haben entweder gar keinen Schimmer wie sie das am besten machen oder sie setzten es teils auf unglaubliche Weise um. Ich habe schon die verrücktesten Kostruktionen gesehen, die Code aus Datenbanken lesen oder wie die phpBB Jungs, die einen ganzen Parser geschrieben haben, der Core Dateien Modifiziert, um an bestimmten Stellen eigenen Code ausführen zu können. Für mich ist das alles Humbug, ein Plugin System muss flexibel und performant sein und nicht zuletzt sollte das ganze so umgesetzt werden, dass vom Benutzer selbst keinerlei Änderung am Code durchgeführt werden muss um ein Plugin einzubauen/zu laden.

Dem habe ich mir angenomen, und zwar habe ein an meinem SimpleLD Framework angelehntes Plugin System (bzw. eine Klasse) geschrieben, die alle aufgezählten Punkte vereint. Folgende Funktionen hat das ganze:

  • Überall einsetzbar – Es ist lediglich eine Klasse, welche in jedem PHP Projekt verwendet werde kann
  • Sie erlaubt es Code an vorher definierten Stellen im eigenem Programm einzuhängen und auszuführen. (mittels Hooks dt. Haken)
  • Die Plugins können ohne Programmier Kentnisse vom Benutzer einfach „installiert“ werden. Denn dazu muss lediglich eine Klasse in ein vordefiniertes Verzeichnis kopiert werden.

(Für die Ungeduldigen, hier gehts zum Github Repo: PHP-Plugin-System @ Github)
Durch die geringe Anzahl an Code Zeilen (rund 100 mit Kommentaren) arbeitet das ganze System Performant. Allerdings wird es je nach Anzahl installierter Plugins langsamer, das kann aber durch einen Cache (siehe SimpleLD Framework) umgangen werden.

Wie funktioniert das ganze?
Die Funktionsweise ist ganz simpel. Plugins bestehen aus ganz normalen Klassen mit public Methoden, welche sich alle in einem von ihnen bestimmten Verzeichnis befinden. Von dort liest das Plugin System alle Klassen ein und speichert Methoden als sogenannte Hooks (dt. Haken) in einem Array. Falls im Code ein solcher Hook aufgerufen wird, werden alle Plugins mit einer passenden Methode herausgesucht und aufgerufen.

Praktisches Beispiel
Plugin Klasse (sample.php)

class sample {
  public function helloWorld() {
    echo "Hello World!";
  }
}

Wichtig: Die Klasse muss exakt so wie die Datei benannt werden! Alle Methoden ohne beginnendes _ werden als Hooks verwendet!

Datei in der das Plugin verwendet wird (index.php)

// laden des Plugin Systems
include('plugins.class.php');
// starten des Systems mit Übergabe des Plugin Verzeichnisses
plugins::start('plugin/ordner/');
// nun können wir einen Hook aufrufen
plugins::call('helloWorld');
// dann noch einen anderen 
plugins::call('foo');

Mit Parametern
Natürlich kann an Hooks auch Parameter übergeben werden. Dies geschieht in Form eines Arrays:

plugins::call('foo', array('Parameter 1', 'Parameter 2', 'Parameter 3'));

Die Methode des Plugin sieht dann folgendermaßen aus:

public function foo($parameter1, $parameter2, $parameter3) {
  // was auch immer wir tun möchten ...
}

Dabei muss allerdings beachtet werden, dass die übergeben Parameter alle von der Methode entgegen genommen werden müssen, ansonsten bricht PHP mit einem Fehler ab.

Nimmt man Änderungen an der Parametern innerhalb des Hooks vor und will diese an den Orginal Code wieder „zurückgeben“, bzw mitteilen, setzt man einfach & vor die Variable:

$var = "foo";
plugins::call('foo', array(&$var));
// Inhalt nach Änderung durch den Hook foo
echo $var;

Die Methode des Plugin sieht dann folgendermaßen aus:

public function foo(&$blub) {
  // hier können wir nun $blub ändern
}

Source Code beziehen
Der Code kann hier mit Beispielen in meinem Git Repo heruntergeladen werden:
http://github.com/LinuxDoku/PHP-Plugin-System/
oder für die faulen unter uns der Direktlink
… ich bin der Direktlink zur Zip Datei …

Tipps’n Tricks

  • Der Verzeichnis Parser liest die Plugins nach altbekannten Regeln des Alphabets (dem ABC ;-)) ein, demzufolge werden Hooks, deren Plugin Namen z.B. mit A beginnen vor Hooks, deren Plugin Namen mit M beginnen ausgeführt. Sollte dies nicht gewünscht sein, kann man sich an der Methode „load“ des System zu schaffen gemacht werden.
  • Beim Starten des System muss dem Plugin Ordnername immer ein Endendes / (Slash) mitgegeben werden!
  • Es empfiehlt sich das System in einer Global verwendeten Datei zu Starten, da ansonsten ziemlich viele Ressourcen benötigt werden. Und ein mehrfacher Start unnötig ist.

6 Antworten zu “einfaches PHP Plugin System”

  1. Hallo,

    ich habe da ein Problem mit deinem Plugin System und zwar folgendes:

    Mein Forum in dem ich dies verwenden möchte basiert hauptsächlich aus Klassen und da kommt das Problem das ich dein plugin mit den normalen Aufrufen wie:

    plugin::start

    nicht nutzen kann.
    Deshalb bin ich es umgangen und habe die Plugin Klasse mit new gestartet und mit:

    $plugin->start

    weiter gemacht. Klappt auch wunderbar und auch das einbinden klappt wunderbar. Das Problem ist nur wenn ich Parameter übermittle um eine Funktion zum laufen zu bekommen und auf das Resultat warte kommt von der Funktion nichts zurück. In der Funktion habe ich ein echo platziert und man sieht ganz deutlich das auch keine Parameter übermittelt wurden. Eine Fehlermeldung kommt auch nicht.

    Ich hoffe du kannst mir das mit den Parametern nochmal etwas genauer erklären.

    MfG.Robbyn Gerhardt

  2. Martin sagt:

    Ehrlich gesagt konnte ich dein Problem nun auf die Schnelle gar nicht Reproduzieren.

    Parameter werden wie bei normalen Funktionen, bzw. Methoden gehandelt, lediglich der Aufruf ist ein wenig anders, aber ich gehe noch ein mal drauf ein.
    Ich habe mir zum Test einmal eine Plugin Klasse angelegt:

    class blub {
    public static function paramTest($param1, $param2) {
    echo $param1.‘ ‚.$param2;
    }
    }

    diese rufe ich folgendermaßen auf (wenn ich es nicht per static machen will, was ich aber empfehle):

    $plugins = new plugins();
    $plugins->start(‚plugins/‘);
    $plugins->call(‚paramTest‘, array(‚hey‘, ‚ho‘));

    folgende Ausgabe bekomme ich dann:
    „hey ho“

    Wenn das so auch nicht funktionieren sollte, meldest du dich am besten noch mal :)

  3. Ok Danke für diese kleine Erklärrung so habe ich es auch selber gemacht. Un hier ist auch das Problem.Ich übergebe auch einen Parameter und ändere ihn darin zum Schluss schreibe ich den veränderten Parameter in eine Variable z.b. $username.

    So und diese Variable die Ich in der Funktion habe dieses $username soll dann im richtigen Script wo der Hook gesetzt ist weiterverwendet werden und das geht nicht wieso. Denn eine echo Ausgabe kann ich nicht machen da ich mein System auf eine Template Engine aufgebaut haben.

    MfG.Robbyn

  4. Martin sagt:

    Ah ok, jetzt verstehe ich das Problem ;)

    Also wenn du Variablen innherhalb der Hook Verändern willst, die per Parameter übergeben werden, musst du dies folgendermaßen machen:
    public static function hook(&$param1, &$param2) {
    $param1 = ‚hallo‘;
    $param2 = ‚hi‘;
    }

    Das & vor den Variablen bedeutet, das nach Ausführung der Methode die Werte wieder ans Ausgangsscript zurückgegeben werden. Der Aufruf muss allerdings auch etwas geändert werden:
    $lala = “;
    $blub = “;
    $plugins->call(‚hook‘, array(&$lala, &$blub));
    echo $lala;
    echo $blub;
    nun sollten die geänderten Werte in den Variablen stehen.

    Grüsse, Martin

    PS: Die Variablen müssen vorher nicht zwingend definiert werden, aber man sollte es machen, sonst weiß man später nicht wo denn Plötzlich die Variablen herkommen ;)

  5. Ah super das hat jetzt ednlich geklappt.

    Ich denke mal das Problem lag daran das ich nicht wusste wie man sowas regelt das derWert zurückgegeben wurde. Vielleicht kannste dasja nochmal für andere die auch bei sowas unwissend sind im Tutorialkurz erläutern oder eine Datei noch dazu packen beim Download.

    Ich danke dir aufjedenfall für deine Hilfe und das Pluginsystem ;-)

    MfG.Robbyn

  6. Michael sagt:

    Hallo,

    ich habe versucht einen Hook aufzurufen:

    plugins::call(’serial_register‘,array($db,$serial));

    $db ist das Datenbankobjekt, und $serial ist ein string.

    Nun bekomme ich aber ein:

    Warning: Missing argument 1 for test::serial_register() in /wsc/3/home/XXXXX-XXX/www/data/Webinterface/plugins/test.php on line 8

    Warning: Missing argument 2 for test::serial_register() in /wsc/3/home/XXXXX-XXX/www/data/Webinterface/plugins/test.php on line 8

    Die Methode ist ganz normal:

    public function serial_register($db,$serial) {
    }

    Woran liegt dass?

    danke und LG

Schreibe einen Kommentar