Plugin in PHP – Klasse in OOP

Wie ich angekündigt habe, möchte ich euch meine Pluginklasse erneut vorstellen.
Diesmal aber in OOP.

Damit jedes Plugin verbindliche Klassen zur Verfügung stellt, erstellen wir ein Interface:

Datei IgPlugin.php

<?php
	interface IgPlugin
	{

		public function getName();
		public function setName($name);

		public function getVersion();
		public function setVersion();

		public function getAutor();
		public function setAutor();

		public function install();
		public function update($version);
		public function unistall();

		public function run();
	}
?>

Das Plugin ist dann folgendermaßen aufgebaut:

</pre>
<?php

	require_once("class/IgPlugin.php");
	
	class test implements IgPlugin
	{
		private $name = "TestPlugin";
	
		public function getName() { return $this->name; }
		public function setName($name){}
		
		public function getVersion(){}
		public function setVersion(){}
		
		public function getAutor(){}
		public function setAutor(){}
		
		public function install(){}
		public function update($version){}
		public function unistall(){}
		
		public function run(){ echo "Plugin l&auml;uft"; }
	}
	
?>

Die Datei gPlugin.php ist nun die Klasse für das Laden der Plugins:


<?php
	
	/**
	 * Klasse gPlugin
	 *
	 * Klasse zum Starten, Installieren, Updaten und Deinstallieren
	 * von Plugins
	 *
	 * @author Alexander Grüßung
	 * @version 2.0
	 */
	 
	class gPlugin
	{
		/**
		 * private field
		 */
		private $pluginName;
		
		/**
		 * private field
		 */
		private $pluginInstance;
	
		/**
		 * Konstruktor lädt Plugin in den Speicher
		 */
		public function __construct($pluginName)
		{
			$this->setPluginName($pluginName);
			$this->loadPlugin();
		}
		
		//Getter und Setter für PluginName
		private function setPluginName($pN) { $this->pluginName = $pN; }
		private function getPluginName() { return $this->pluginName; }
		
		//Getter und Setter für PluginInstance
		private function setPluginInstance($instance) { $this->pluginInstance = $instance; }
		private function getPluginInstance() { return $this->pluginInstance; }
	
		/**
		 * Funktion bindet Plugindatei ein und
		 * lädt Instanz in den Speicher
		 */
		private function loadPlugin()
		{
			if (file_exists($this->getPluginName().".php"))
			{
				require_once($this->getPluginName().".php");
				$name = $this->getPluginName();
				$this->setPluginInstance(new $name());
				
				if ( ($this->getPluginInstance() instanceof IgPlugin) == false)
				{
				 trigger_error("Interner Pluginfehler: Interface wird nicht implementiert", E_USER_ERROR);
				}
				
			}
			else
			{
				trigger_error("Plugin nicht gefunden", E_USER_ERROR);
			}
		}
		
		/**
		 * Plugin starten
		 */
		public function runPlugin()
		{
			//TODO Prüfen ob Plugin installiert ist
			$this->getPluginInstance()->run();
		}
	
	}
?>

Hier eine Beispieleinbindung:

<?php
	require_once("class/gPlugin.php");
	
	$p = new gPlugin("test");
	$p->runPlugin();
	
?>

 
Damit viel Spaß mit der Klasse!

Ein Pluginsystem mit Paketserver in PHP – Teil 4

Ein Pluginsystem mit Paketserver in PHP – Teil 4

Wir haben nun gesehen:

  • wie ein Plugin aufgebaut ist
  • wie das Plugin im Frontend aufgerufen wird und
  • wie das Plugin im Backend aufgerufen und verwaltet wird.

Was jetzt noch fehlt ist die eigentliche Installation über einen Pluginserver.

Der Pluginserver

Der Pluginserver verwaltet alle Plugins und deren Datei. Jedes Plugin erhält einen Datenbankeintrag, wo folgende Daten gespeichert werden:

  • id
  • com_id
  • name
  • description
  • author
  • author-mail
  • plugin_url

Ebenfalls gibt es eine Datei, nennen wir diese get.php
Diese Datei lädt die Plugins auf dem Server aus der Datenquelle (welche ihr euch selber aussuchen könnt) und erzeugt eine XML mit folgendem Format:

<xml>
<extension type=“plugin“ xml=“app/com.gvisions.moonlight.xml“ name=“gMoon!ight“ comid=“com.gvisions.moonlight“></extension>
<extension type=“plugin“ xml=“app/com.gvisions.news.xml“ name=“gNews“ comid=“com.gvisions.news“></extension>
</xml>

 Im Parameter „xml“ ist der Speicherort einer XML angegeben, welche alle Pluginformationen enthält. Der Pfad bezieht sich auf den Server. Die restlichen Parameter sind selbsterklärend. Wie ihr die Daten auslesen werdet in der get.php überlasse ich euch.
Es sollte kein Problem sein, was kleines zu coden. Ich benutze dafür eine mySQL Datenbank. Aber auch eine rein textbasierte Lösung mit Hilfe von XML ist möglich.

Clientseitig (also im AdminPanel) geht die Installation folgendermaßen vor:

1. Der Client lädt die XML Datei (Parameter „xml“) herunter.

2. Der Client liest diese Datei aus und prüft auf Abhängigkeiten.

3. Der Client fragt den Nutzer nach der Lizens.

4. Der Client installiert.

Gehen wir die Schritte durch:

1. Datei runterladen und speichern:


<?php
//Aus dem Parameter $xmlFile (=entspricht der XML Datei auf dem Server) wird der reine Dateiname extrahiert
//zB aus http://update.gvisions.de/app/gMoonlight.xml wird gMoonlight.xml
$xmlFileName = basename($xmlFile);
//Lade Datei von Server in temp-Verzeichnis runter
copy($xmlFile,root."/temp/".$xmlFileName);
//Es wird nun versucht die Dateizugriffsrechte zu setzen
chmod(root."temp/".$xmlFileName,0777) or die('Konnte Datei nicht kopieren.<br />');

2. Der Client liest diese Datei aus und prüft auf Abhängigkeiten:

Ich habe Schritt 1 und 2 in einer Datei, für euch teile ich diese Datei hier mal, damit wir den Überblick haben.


//Nun prüfen wir die Systemvorraussetzungen
//Dazu wird die runtegeladenen XML Datei geladen
$xml = simplexml_load_file(root."temp/$xmlFileName");
//Alle Anhänigkeiten werden geladen und in ein Array gespeicchert
$req = explode(",",$xml->requirements);
//Die Anzahl wird bestimmt
$anzahl = count($req);
//Diese Variable darf nie false werden, sonst ist eine Abhänigkeit nicht verfügbar
$reqok = true;
//Schleife, welche alls Abhänigkeiten durchgeht
for ($s=0;$s < $anzahl; $s++) {
//Jede Abhänigkeit wird in comID und Version gesplittet (Zur Erinnerung, so sieht eine aus: com.gvisions.framework-0.2)
$req2 = explode("-",$req[$s]);
$comid = $req2[0];
$version = $req2[1];
//Nun wird nach der comID gesucht
$db = new db();
$sql ="SELECT * FROM ".pfw."_plugins WHERE `com_id` = '$comid' LIMIT 1;";
$db->query($sql);
$row = $db->fetch();
echo "Benötige $comid-$version";
//Wenn nun die benötige Version nicht vorhanden ist bzw. nicht installiert ist,
//so wird der entsprechenden Text gespeichert und ausgegeben
if (empty($row->version)) { $vorhanden = "nicht installiert"; } else { $vorhanden = $row->version; }
if ($version <= $vorhanden AND $vorhanden != "nicht installiert") {
echo '<font color="green"> - vorhanden: '.$vorhanden."<br></font>";
}
else
{
echo '<font color="red"> - vorhanden: '.$vorhanden."<br></font>";
//Boolean auf false setzen
$reqok=false;
}

}

//Fehlermeldung, wenn Abhänigkeiten fehlen
if (!$reqok) {
die('<br><br><font color="red"><b>Leider sind die Vorraussetzungen nicht erf&uuml;llt. Installieren Sie erst die oben genannten Pakete.</b></font>');
}
//Kein Fehler
else
{
echo "<br><b><font color=green>Alle Anforderungen sind erf&uuml;llt. Fahren Sie fort.</b></font><br><br>";
}
//Weiterlink
echo "<a href=index.php?action=download&step=2&srv=$srv&file=$xmlFileName>Weiter</a>" ;

3. License anzeigen und Nutzer fragen:


<?php
 $xml = simplexml_load_file(root."temp/$xmlFileName");
 $license = $xml->license;
?>
<textarea rows="10" cols="35" disabled><?=$license?></textarea>

Das ist einfach gewesen. Am Ende dieser Seite müsst ihr noch Links einbauen, ob derjenige Akzeptiert oder nicht und entsprechend weiter verfahren.

4. Plugin installieren

Jetzt kommt das Beste :)


<?php

//Zip Objekt erzeigen
$zip= new ZipArchive;
//XML Datei öffnen und lesen (Downloadlink speichern)
$xml = simplexml_load_file(root."temp/$xmlFileName");
$downloadFile = $xml->zipfile;
//Andere relevante Infos speichern
$comid = $xml->comid;
$desc = $xml->desc;
$version = $xml->version;
$name = $xml->name;
$fileName = basename($downloadFile);
$path = $xml->installpath;
$installPath = root.$path.$comid;
//zip Paket vom Server runterladen und kopieren, anschließend Rechte seten
copy($downloadFile,root."/temp/".$fileName);
chmod(root."temp/".$fileName,0777) or die('Konnte Datei nicht kopieren.<br />');

//Da diese Routine hier universell ist, schaue ich nach, ob es ein Plugin oder ein Style ist.
//Je nach Dem wird ein Objekt erzeugt.
//meine gPlugin.php kennt ihr bereits, die gStyle.php ist im Grunde das Selbe,
//eben für Styles.
//Wenn jmd diese haben möchte, schreibt es in die Kommentare 😉
if ($xml->type=="style") {
 $newExt = new Style();
} elseif ($xml->type=="plugin") {
 $newExt = new plugin();
} else {
 trigger_error("Typenzuordnung nicht möglich. Fehler in XML Datei.",E_USER_ERROR);
}
//Nun wird die Erweiterung registriert, dafür sind die Klassen da
if ($newExt->Registration($name,$desc,$comid,$installPath,$version,$srv,true) OR isset($_GET['forceupdate'])) {
 //Kopiere nun die Erweiterung in den Installpath
 if ($zip->open(root."/temp/".$fileName) === TRUE) {
 //Extrahiere ZIP
 $zip->extractTo($installPath);
 $zip->close();
 if (isset($_GET['forceupdate'])) {
 //Wenn es ein Update ist, un der Nutzer dies möchte, dann Version ändern
 $newExt->Update("version",$version,$comid);
 }
 }
 else
 {
 //Fehlerfall
 //Plugin aus DB löschen, Temp Daten löschen
 $newExt->Delete($comid);
 @unlink(root."temp/".$fileName);
 }
 //Falls das Plugin eine DB Änderung macht (zB Tabellen anlegen), dann werden diese hier durchgeführt
 if (!empty($xml->installsql)) {
 $db = new db();
 $data = explode(";",$xml->installsql);

 foreach ($data as $que)
 {
 //Platzhalter ersetzen
 $que = preg_replace("%AID%",$newExt->getID($comid),$que);
 if($db->query(preg_replace("%PFW%",pfw,$que))==false) {
 //fehlerfall
 //Löschen des Plugins
 $newExt->Delete($comid);
 echo $xml->typede." wurde aus der Datenbank gelöscht<br />";
 }
 else
 {
 //Alles OK
 }
 }
 }
}
else
{
 //Plugin bereits registriert, soll trotzdem ein Update durchgeführt werden??
 echo "<li><b>".$xml->typede." ist bereits registriert. Daten werden nicht entpackt</b><br>";
 $aktuelleVersion = $newExt->getVersion($comid);
 echo "<br>Wollen Sie die bereits vorhandene Version $aktuelleVersion durch die Version ".$version." ersetzen? Dabei werden die Daten im ".$xml->typede."verzeichnis &uuml;berschrieben.<br><a href=\"index.php?action=download&srv=$srv&file=$xmlFileName&step=3&forceupdate\">Ja, weiter.</a><br><a href=\"index.php?file=$xmlFileName&action=download&srv=$srv&step=0\">Nein, aufh&ouml;ren!</a></li>";
}
//Fertig

So, das war mein kleines Tutorial, wie man in PHP ein Pluginsystem mit Paketserver schreibt.
Ich hoffe euch hat es gefallen und es findet Anwendung.
Bitte lest bei der Verwendung des Quelltextes die Bedingungen im ersten Teil.

Bitte leitet die Artikel weiter.
Bei Fragen stehe ich euch in den Kommentaren unter den Artikels zur Verfügung.

Wenn ich Zeit habe werde ich auch mal ein paar Videoanleitung zum Thema produzieren, das
dauert aber noch 😉

Tschüss!

Ein Pluginsystem mit Paketserver in PHP – Teil 3

Ein Pluginsystem mit Paketserver in PHP – Teil 3

Nachdem wir nun wissen, wie ein Plugin aufgebaut ist und wir wir es im Frontend verwenden, müssen wir uns mit der Frage beschäftigen, wie wir die Plugins im Backend administrieren.

Generell ist meine Überlegung, dass das ACP eine PHP Datei hat, die je nach übergebener ID aus dem Pluginordner die Backenddateien nachlädt.

Ein Beispiel: Im ACP gibt es eine Datei plugin_list.php.
Rufen wir diese Datei auf, so erscheint eine Liste mit Plugins, wie hier:

Pluginliste

Von hier aus, kann man die Plugins administrieren.
Dazu ruft man dann plugin_verwalten.php?appid=X auf, dann erscheint, je nach Plugin, die entsprechenden ACP Seite.

Um  die plugin_list.php anzuzeigen, ist nicht viel nötig.
Der nötige SQL Befehl lautet:

SELECT * FROM plugins ORDER BY id;

Geben wir die Ergebnisse einfach als eine normale Liste aus:


$query = mysql_query("SELECT * FROM plugins ORDER BY id;");

$AppsNum = mysql_num_rows();
 $AppsTd="";
 while ($AppsArray = mysql_fetch_object($query)) {
 $color ="red";

 if ($AppsArray->activate=="1" || $AppsArray->activate=="true") { $color = "lightgreen"; }
 $AppsTd .= '<tr style="background-color:'.$color.';"><td style="width:20%;">'.$AppsArray->name.'</td><td "width:5%;">'.$AppsArray->version.'</td><td "width:45%;">'.$AppsArray->desc.'</td><td "width:35%;"><a href="plugin_verwalten.php?appid='.$AppsArray->id.'">Administrieren</a></td></tr>';
 }

 echo 'Installierte Erweiterungen:<br><div class="cel" style="height:auto;">
 <table style="width:100%;"><tr style="border: 1px black solid;"><td>Name</td><td>Version</td><td>Beschreibung</td><td>Optionen</td></tr> '.$AppsTd.'</table></div><br /><br />';

 

Die plugin_verwalten.php ist recht einfach aufgebaut, sie prüft nur, ob die angegebene ID konform und in der Datenbank existent ist und includiert dann die hinterlegten Dateien:


<?php

 /*
 Checke die Paratemter in der GET
 */

 if (!isset($_GET['appid']) OR !is_numeric($_GET['appid'])){
 trigger_error("Die Variable \"appid\" ist entweder nicht übergeben worden oder entspricht nicht den Vorraussetzungen.",E_USER_ERROR);
 }

$appid = $_GET['appid'];

 /*
 Schauen wir mal nach der und laden alle Daten in ein array() ^^
 Und natürlich wider zählen
 */

 $query = mysql_query("SELECT * FROM plugins WHERE ID = ".$appid);

 $row = mysql_fetch_object($query);

 $num = mysql_num_rows();

 /*
 Wenn jetzt $num = 0 oder > 1 ist, dan stimmt was nicht
 */

 if ($num != 1) {
 trigger_error("Die ID ist nicht vorhanden und/oder mehrmals vergeben. Dies macht eine eindeutige Identifikation unmöglich.",E_USER_ERROR);
 }

 /*
 So, dann schauen wir mal OptionFile an und laden die Datei hier rein^^
 */
 define("apppath",$row->path);
 require_once(apppath."/cp/index.php");

 /*
 Für mich ist die Sache damit erledigt :)
 */

Okay, nun wissen wir, wie wir die Backenddateien verwalten.

Im nächsten Teil installieren wir die Plugins via einem Updateserver.

Zur Hauptseite