Über den Author / Internet / WordPress / Plugin / Eigenes WordPress Plugin schreiben

Eigenes WordPress Plugin schreiben

~10 Min. Lesezeit

Um ein eigenes WordPress Plugin zu schreiben braucht es nur ein paar Dinge.

Programmieren ist Fleissarbeit welche Zeit benötigt und daher sollte man damit immer reichlich planen.
Weiterhin sollte jedem Programmierer von WordPress Plugins bewusst sein, dass es nicht mit der Erstellung des Plugins endet.

Diese Plugins sollten jederzeit auf die neueste Möglichkeiten und Techniken angepasst werden sowie den Usern eine kleine Hilfe geboten werden, wenn das Plugin nicht selbsterklärend ist.

Was man ausser Zeit noch braucht, ist selbstverständlich PHP Kenntnisse!

Ich werde hier nun schnell und einfach ein sauberes Template erklären, mit welchem man das Grundgerüst für jedes WordPress Plugin schnell und einfach zur Verfügung hat.

Plugin

Was benötigt aber ein WordPress Plugin?

Plugin Template

Als Vorbereitung überlegt man sich einen Klassen Namen, welcher nur aus Buchstaben zusammengesetzt wird, ohne Leerzeichen, (z.B. MeinPlugin), welchen wir überall wo TEMPLATE steht, per Suchen und Ersetzen im hier gezeigten und später heruntergeladenen Template austauschen.

Ein Plugin benötigt als Grundriss ein Header, woran WordPress das Plugin erkennt und auch weiss was dies machen sollte, welche Version es hat etc. Daher sollte man keine Zeilen weglassen oder Neue erfinden:

<?php
/*
 Plugin Name: some name here
 Plugin URI: http://www.yourdomain.com/plugin_webpage/ifanyexists/
 Description: Enter here a short text, what your plugin does
 Version: 1.0
 Author: your name
 Author URI: http://www.yourdomain.com

 This program is distributed under the GNU General Public License, Version 2,
 June 1991. Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin
 St, Fifth Floor, Boston, MA 02110, USA

 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

Wie man sieht, ist auch gleich das Plugin dann unter GNU v2 veröffentlicht, was ich normalerweise bevorzuge für die Veröffentlichung. Selbstverständlich kann man auch das Plugin unter einer höheren Version oder der MIS Lizenz veröffentlichen, wenn man dies möchte, aber Open Source ist nötig, um das Plugin bei WordPress.org zu veröffentlichen.

Weiter geht es mit der Abfrage, ob die Klasse nicht schon zufällig existiert und wenn nicht, wird diese erstellt:

if (!class_exists('TEMPLATE')) {
	class TEMPLATE {
		var $settings;

Gleich mit in der Klasseninitialisierung wurde eine Variable $settings erstellt, in welcher die Einstellungen vom Plugin zugegriffen werden können.

Die nächste Funktion ist die Grundfunktion, welche das Plugin initialisiert und die benötigten Funktionalitäten bereitstellt, wobei ich ins Detail gehen werde:

		function TEMPLATE() {
			$this->getOptions();
			if (function_exists('load_plugin_textdomain')) load_plugin_textdomain('TEMPLATE', PLUGINDIR.'/'.dirname(plugin_basename(__FILE__)).'/languages', dirname(plugin_basename( __FILE__ )).'/languages');

			// Example Shortcode [date] add with: add_shortcode('date', array(&$this,'show_date'));

			if(is_admin()) {
				add_action('admin_menu', array(&$this, 'add_menupages'));
			}

			// Example Filter add_filter( 'user_can_richedit', array(&$this, 'disable_wysiwyg') );
		}

Zeile 2 ruft die Funktion getOptions() auf, welche im späteren Teil erklärt wird. Kurz gesagt, diese Funktion ladet alle Plugin spezifischen Einstellungen und speichert diese dann in der $settings Variable die wir vorhin definiert haben.

Mit der Zeile 3 überprüfen wir, ob die installierte WordPress Version bereits Mehrsprachigkeit unterstützt und gegebenenfalls dann nachschaut, ob im Pluginverzeichnis ein Verzeichnis ‚languages‘ existiert und darin wiederum eine Sprachdatei zur aktuellen Sprache vorliegt. Falls dies der Fall ist, wird die Ausgabe dann in der aktuellen Sprache erfolgen und nicht in der „programmierten“ Sprache.

Selbstverständlich möchte man auch dem User die Möglichkeit geben, innerhalb von WordPress Einstellungen zum Plugin vornehmen. Dies soll jedoch nur auf der Adminansicht von WordPress sein und daher muss dieses Modul auch nur dann geladen werden. Falls der Benutzer Admin ist, wird das Admin Menü geladen und angezeigt (Zeile 7-9).

Weiterhin hat man natürlich die Möglichkeit hier nun die Hooks und Actions vom eigenen Plugin zu initialisieren, wie die 2 Beispiele auf Zeile 5 und 11.

Die nächste Funktion ist bereits die Versprochene, wo die Einstellungen ladet:

		function getOptions() {
			$this->settings = get_option('TEMPLATE');
			if (!array_key_exists('irgendwas',$this->settings)) {
				$this->settings['irgendwas'] = 'wert';
				update_option('TEMPLATE', $this->settings);
			}
		}

Diese holt nun die Einstellungen zum Plugin mit get_option() auf Zeile 2 und speichert das Resultat denn in der $settings ein. Auf Zeile 3-6 schaue ich, ob das Setting ‚irgendwas‘ bereits gesetzt ist, falls nicht, setze ich das Setting auf ‚wert‘ und speichere dies gleich wieder ein.

Genau auf die gleiche Art kann man nun ein Formular programmieren, wo man die Einstellungen einstellen kann und auf die gleiche Weise ein speichern. Man muss dazu nicht seine eigene Datenbank oder Konfigurationsdateien anlegen, alles ist aus einem Guss fertig verfügbar.

Die nächste Funktion ist sehr einfach erklärt:

		function activate() {
			// Create Tables if needed or generate whatever on installation
		}

Diese Funktion wird aufgerufen wenn das Plugin installiert wird. Ist es beispielsweise nötig das eine Datenbank erstellt wird oder Standard Werte geladen werden, kann man dies in diese Funktion einbinden und diese wird dann EINMALIG bei der Installation oder Pluginupdate aufgerufen.

Diese Funktion macht das umgekehrte, bei der Deinstallation wird diese Funktion aufgerufen (nicht bereits Deaktivieren):

		function uninstall() {
			// Delete Tables or settings if needed be deinstallation
		}

Ihr könnt euch nun denken was da rein müsste. Falls ihr irgendwas spezielles installiert habt, wieder hier entfernen, damit die Datenbank von WordPress anschliessend wieder sauber ist.

Die nächste Funktion ist sehr wichtig, denn ohne diese ist es zwar ein Plugin, aber der Benutzer kann nichts einstellen oder verändern.

		function add_menupages() {
			// For Option Pages, see WordPress function: add_options_page()
			// For own Menu Pages, see WordPress function: add_menu_page() and add_submenu_page()
		}

Diese Funktion fügt innerhalb vom WordPress Admin Panel neu Links hinzu. Die erste Option auf Zeile 2 fügt einen Link zu den Einstellungen mit dem angegebenen Namen dazu (Bild Links).
Wobei die Funktionen auf Zeile 3 komplett eigenständige Menüstrukturen mit Unterpunkten erstellen (Bild Rechts).

Menü unter Einstellungen
Menü unter Einstellungen
Eigenes Menü mit Submenü
Eigenes Menü mit Submenü


Kommt man mit einer Einstellungsseite zurecht ist die Einstellungsseite unter Optionen sicher zu bevorzugen. Bei grossen Plugins mit vielen Einstellungsmöglichkeiten oder unterschiedlichen Seiten, ist es besser in ein separates Menü auszulagern.

Das war es, nun muss nur noch die Klasse geladen werden und die Aktivierungs- und Deinstallierungshooks installieren mit dem Code:

	}
	add_action('init', 'TEMPLATE1');
	function TEMPLATE1() {
		global $TEMPLATE;
		$TEMPLATE = new TEMPLATE();
	}
}

if (function_exists('register_activation_hook')) { register_activation_hook(__FILE__, array('TEMPLATE', 'activate')); }
if (function_exists('register_uninstall_hook')) { register_uninstall_hook(__FILE__, array('TEMPLATE', 'uninstall')); }

?>

Wichtig ist hierbei, die Aktivierungs- und Deinstallierungshooks dürfen NICHT innerhalb der Klasse initialisiert werden, da diese anderenfalls nicht ausgeführt werden.

Das war eigentlich schon das ganze Template was es hier zum Download gibt, dennoch gebe ich euch noch gerne weitere sehr wichtige Tipps zur Programmierung mit auf den Weg, die nicht im Template ersichtlich sind:

Übersetzungen

Sagen wir, ihr habt eine Textausgabe die wie folgt aussieht:

$ip = getenv('REMOTE_ADDR');
echo "Deine IP Adresse lautet: ".$ip;

Ist die Ausgabe „Deine IP Adresse lautet: 123.123.123.123“. Was funktioniert, aber unschön ist, denn diese Ausgabe ist nur in einer Sprache verfügbar.

Also sollten wir dies Multisprachig programmieren was wir im Plugin ermöglicht haben. Man programmiert es wie folgt:

$ip = getenv('REMOTE_ADDR');
echo __("Deine IP Adresse lautet: ","TEMPLATE").$ip;

Diese Funktion __() schaut nun ob der Text „Deine IP Adresse lautet“ in einer anderen Sprache verfügbar ist.

Dies funktioniert soweit gut, bis die Variable mitten im Text ist.

$ip = getenv('REMOTE_ADDR');
echo "Deine IP Adresse ".$." war schon mal hier ";

Dann haben wir ein Problem, warum wir wie folgt solche Texte einbinden:

$ip = getenv('REMOTE_ADDR');
print_f( __("Deine IP Adresse %s war schon mal hier", "TEMPLATE"), $ip);

Hier wird der Text „Deine IP Adresse %s war schon mal hier“ in der Übersetzung gesucht und anschliessend das %s mit der Variable ersetzt.

Caching

Grössere Webseiten, welche immer dynamisch erstellt werden brauchen Serverleistung. Da sich aber Webseiten nicht jede Sekunde ändern, erstellt man nur in gewissen Abschnitten eine Passage neu und falls diese noch gültig ist, greifft man auf ein bereits vorher erstellten Text zurück und verwendet diesen.

Hat man nun eine Ausgabe, z.B. man generiert einen Text oder Artikel der auf die Seite ausgegeben wird, dann baut man Cachefunktionalität ein.

Als Beispiel, hier laden wir ein Artikel Archiv und geben dies aus:

$qry = $wpdb->get_results("SELECT DISTINCT YEAR( post_date ) AS year, MONTH( post_date ) AS month, post_title,guid FROM `".$wpdb->prefix."posts` WHERE post_type='post' AND  post_status='publish' ORDER BY year DESC, month DESC;");
foreach ($qry as $row) {
	if ($last != $row->year.$row->month) {
		if ($last != '') { echo "</UL>"; }
		echo "<h4 class=\"wctarchiveheader\" id=\"a_".$row->year."_".$row->month."\">".date("F Y",mktime(0, 0, 0, $row->month, '5', $row->year))."</h4><UL class=\"wctarchiveul\">";
		$last = $row->year.$row->month;
	}
	echo "<li class=\"wctarchiveli\"><a class=\"wctarchivea\" href=\"".$row->guid."\">".$row->post_title."</a></li>";
}

Nun würde bei jedem Aufruf des Artikelarchives die Datenbank durchgegangen und die Seite genieriert werden. Wir können nur den Code in folgendes ändern:

$out = wp_cache_get( 'archive' , 'TEMPLATE');
if ($out == false) {
	$qry = $wpdb->get_results("SELECT DISTINCT YEAR( post_date ) AS year, MONTH( post_date ) AS month, post_title,guid FROM `".$wpdb->prefix."posts` WHERE post_type='post' AND  post_status='publish' ORDER BY year DESC, month DESC;");
	foreach ($qry as $row) {
		if ($last != $row->year.$row->month) {
			if ($last != '') { $out .= "</UL>"; }
			$out .= "<h4 class=\"wctarchiveheader\" id=\"a_".$row->year."_".$row->month."\">".date("F Y",mktime(0, 0, 0, $row->month, '5', $row->year))."</h4><UL class=\"wctarchiveul\">";
			$last = $row->year.$row->month;
		}
		$out .= "<li class=\"wctarchiveli\"><a class=\"wctarchivea\" href=\"".$row->guid."\">".$row->post_title."</a></li>";
	}
	wp_cache_set( 'archive', $out, 'TEMPLATE', '86400');
}
echo $out;

Diese Funktion prüft auf Zeile 1 ob im Chache mit dem Namen ‚archive‘ noch was gespeichert ist. Falls Nein, wird die Seite erzeugt und auf Ziele 12 gespeichert. Wie man sieht, bleibt diese Ausgabe nun in der Definition genau 84600 Sekunden gültig was einem Tag entspricht. Nach einem Tag wird der Inhalt neu generiert, innerhalb dieses Tages aber auf den erstellen Text zurückgegriffen. So spart man sich Datenbankzugriffe und CPU Zeit diese Daten zu erstellen.

Alle Texte / Codes welche im WordPress Frontend ausgegeben werden, sollten über diese Möglichkeit eingebunden werden.
Dies ist zwar kein Must-Have wenn man kein Chaching Plugin hat, aber man sollte immer bedenken das in der WordPress Welt viele andere Blogs sind und es wäre schade, wenn ein grosse Seite das Plugin nicht verwenden würde, weil diese Option fehlt, wo für diese unverzichtbar ist.

Veröffentlichung auf WordPress.org

Um das Plugin auf WordPress.org zu veröffentlichen, muss man zuerst ein paar Richtlinien erfüllen. Das Wichtigste ist aber unter GNU oder höheren Lizenz zu veröffentlichen und weiterhin ein Readme zum Plugin zu erstellen.
WordPress stellt ein Standard Readme zur Verfügung und auch ein Readme Validator wo man das eigene Readme überprüfen kann.
Veröffentlichen kann man es unter dem folgenden Link: WordPress.org

Fragen und Probleme

Selbstverständlich bin ich gespannt, welche Plugins alles mit meinem Template umgesetzt werden, oder wen ich Lust auf WordPress Plugin Programmierung gemacht habe.
Ich stehe in den Kommentaren gerne für Fragen zu Hooks, Actions und generell zu WordPress Programmierung zur Verfügung.

About Stefan

avatar

Ein männlicher IT Nerd, durchstöbert das Web nach speziellen Gadgets, unentbehrlicher Software und Alles was man im IT Sektor nicht verpassen darf.

Immer hilfsbereit wenn Probleme zu lösen sind oder das Unmögliche umgesetzt werden sollte.

Weitere interessante Artikel

Tabelle vertical Scrollbar machen (Responsive Table)

~0 Min. LesezeitHTML Tabellen sind ein gern eingesetztes Mittel auf Webseiten. Leider eigenen sich solche …

YubiKey – OTP und U2F Authentifizierung

~1 Min. LesezeitFast bei allen Diensten benötigt man Heute ein Benutzername und Passwort. Dies zu …

5 Kommentare

  1. avatar

    Hallo,

    das Tutorial ist wirklich klasse, zumal es auf die randbereiche wie mehrsprachigkeit und co eingeht. Leider bleiben am ende trotzdem noch ein paar fragen offen für leute die sich noch nicht so gut mit php auskennen. schade auch das hier noch niemand ein Kommentar gepostet hat, die Anleitung haben sicher schon viele gelesen!
    Zur Frage, wird die function activate() automatisch ausgeführt beim Aktivieren oder gibt es dazu auch ein action hook, den hättest du dann z.B. unterschlagen in deiner erklärung, selbiges gilt beim uninstall und co ;-). Noch dazu könntest du auf das speichern der Optionen kommen, das würde einem auch viel recherche abnehmen.

    Grüße, Sebastian

  2. avatar

    Hallo Sebastian

    Auf die beiden Routinen gehe ich auch ein, nicht all zu explizit, sondern eher unsichtbarer.
    Im Code Fenster 5 und 6 findest du die beiden Funktiionen wo du dein Code einfügen kannst.

    Im Fenster 8 auf Zeile 9 und 10 siehst du die Hooks welche diese Funktionalitäten automatisch beim Aktivieren und Deinstallieren aufrufen.
    Es gäbe auch noch den „Deaktivierungshook“, welcher aber nie genutzt wird. Will man ein Plugin kurzzeitig deaktivieren wären sonst alle Einstellungen und Daten weg. Daher nimmt man den Deinstallierungshook. Ein Installierungshook gibt es nicht.

    Auch müssen die Hooks wie beschreiben eingebunden sein und nicht in der Classe selbst, anderenfalls werden diese nicht aufgerufen.

    Auf das Speichern gehe ich nciht ein, sieht man aber in der GetSettings Funktion kurz. Da prüfe ich ob ‚irgendwas‘ gesetzt ist, wenn nein, setzen.
    Damit lässt sich dies auch innert Minuten finden, wenn man weiss wo.

    Aber dazu sind doch schon viel PHP Wissen nötig, da hast du recht.

    Bei Fragen nur zu.

    Gruss Stefan

  3. avatar

    Hi Stefan,
    Dein Tut hat mir viel weiter geholfen.
    Eine kleine Sache habe ich aber bis jetzt noch nicht herausgefunden.
    Eigene Plugins haben als Pictogram immer das Zahnrad.
    Wie bekomme ich da eigene Pictogramme angezeigt?

    Gruß
    Thomas

  4. avatar

    Hallo Thomas

    Schau dir diese Seite an:
    http://codex.wordpress.org/Function_Reference/add_menu_page
    add_menu_page( $page_title, $menu_title, $capability, $menu_slug, $function, $icon_url, $position );

    Du kannst einfach die URL zum Icon angeben.

    Gruss
    Stefan

  5. avatar

    Danke

    Gruss
    Thomas

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

eMail-Benachrichtigung bei weiteren Kommentaren.
Auch möglich: Abo ohne Kommentar.