webfluence hat 5 von 5 Sternen bei 11 Bewertungen auf Google My Business.

WordPress Plugin erstellen

Wie du als Einsteiger dein erstes WordPress Plugin erstellst. Inkl. benötigter Software und Code-Beispiele

Früher oder später benötigt eine WordPress-Website kleine Anpassungen oder Funktionen, für die es kein passendes Plugin gibt.

Eine logische Option bei einem Open Source CMS: ein eigenes Plugin erstellen und das Problem selber lösen.

In diesem Artikel schauen wir uns an, was wir alles brauchen, um ein eigens Plugin zu entwickeln. Auch erstellen wir ein kleines Demo-Plugin zu Vorführungszwecken.

Robert Rosanke

Webentwickler

Ziel der Anleitung

In diesem Artikel erstellen wir ein kleines Plugin.

Ziel: einmal alle Schritte durchgehen, um ein eigenes WordPress Plugin zu erstellen.

Dieser Artikel beschäftigt sich nicht damit, wie man WordPress installiert und konfiguriert, um das geschriebene Plugin zu testen oder eine Plugin-Idee zu finden.

Wenn du bereits eine Plugin-Idee hast, dann könnte dir dieser Artikel dabei helfen, sie in die Tat umzusetzen.

Wir erstellen ein Plugin, das nur aus einer einzigen Datei besteht. Künftig wird der Artikel erweitert und soll weitere Tipps geben, mit denen auch größere Plugins umgesetzt werden können.

Benötigte Software

Zum Erstellen eines Plugins braucht es rein theoretisch keine speziellen Programme.

  • Texteditor zum Code schreiben
  • Programm zum Erstellen von zip-Dateien, um das Plugin final zu erstellen
  • einen Browser, um ggf. benötigte Funktionen zu recherchieren und Dokumentation zu lesen
  • eine WordPress-Installation zum testen des Plugins
  • wer strings in seinem WordPress-Plugin übersetzbar machen muss oder möchte, könnte WP-CLI und Poedit gut gebrauchen. Mehr dazu: WordPress übersetzten

In der Praxis würde ich statt einem einfachen Texteditor einen einfachen Code-Editor nutzen, z.B. VS Code (engl.), und dafür – falls nötig – ein paar Erweiterungen installieren.

Es gibt auch spezielle PHP-Editoren/IDEs. Kann im Rahmen von WordPress sicherlich helfen. Bis dato kam ich auch so einigermaßen zurecht, sodass ich mir den Setup-Aufwand dafür spare.

Tipp

Wer Größeres Vorhat, profitiert vermutlich von Tools wie dem Paketmanager für PHP – Composer (engl.).

Damit lassen sich Bibliotheken in das Projekt installieren, z.B. für das Erstellen von PDF-Dateien. Den Umgang mit Composer deckt diese Anleitung nicht ab. Vermutlich benötigten die meisten Menschen, die ihr erstes Plugin erstellen, das Tool nicht.

Benötigtes Vorwissen

Generell bietet es sich an, zumindest grob PHP lesen und verstehen zu können.

Eine Funktion nicht zu kennen, ist keine Schande. Dafür gibt es Google und die offiziellen Dokumentationen von WordPress & PHP.

Zudem sollte die grundsätzliche Bedienung von WordPress bekannt sein.

  • Wie installiere und aktualisiere ich ein Plugin?
  • Wie setze ich eine Testseite auf, damit ich mein ggf. Fehlerhaftes Plugin nicht direkt auf meiner Live-Seite testen muss?

Je nach Vorhabe sollte auch Grundwissen bzgl. action und filter hooks von großem Vorteil sein.

Tipp

Wenn du dir unsicher bist, was eine Funktion aus einem Code-Beispiel macht, dann suche im Netzt nach dem Namen der Funktion. In der Regel landest du dann bei der offiziellen PHP-Dokumentation, auf developer.wordpress.org oder einem Forum wie Stack Overflow.

Auch für CSS und JavaScript gibt es zahlreiche Anlaufstellen. Eine meiner häufiger genutzten Ressourcen ist z.B. MDN.

Beispiel: WordPress Plugin erstellen

Geplante Funktion des Plugins

Bevor ich mir programmieren anfange, überlege ich mir, was das Plugin machen soll.

Die Fragen beantworte ich in diesem Fall mal für das in diesem Artikel zu entwickelnde Beispiel-Plugin.

  • Welche Funktionen erfüllt es? Das Plugin soll die Performance-Metriken verbessern. Herangehensweise ist, dass kleine CSS-Dateien als inline styles im HTML-Dokument ausgeliefert und somit die Anzahl an requests reduziert werden.
  • Wer nutzt es? Das Plugin soll eher auf großen, überladenen Seiten zum Einsatz kommen. Besonders interessant, wenn die realen Verursacher der schlechten Performance-Metriken nicht zeitnah eliminiert werden können. In dem Fall ist jede noch so kleine Verbesserung willkommen.
  • Wie kommen die Nutzer an die Software? Ich installiere das Plugin manuell auf den Seiten, auf denen ich es nutzen möchte. Weder Onlineshop noch Registrierung im WordPress-Verzeichnis zum Vertrieb des Plugins notwendig.
  • Wie soll das Plugin heißen? Inline small CSS files. Klingt für mich logisch und nachvollziehbar.

Ordner und Plugin-Datei anlegen

Die Struktur für solch ein kleines Plugin ist ziemlich überschaubar. Schließlich wird es nur aus einer PHP-Datei bestehen.

Wir erstellen also einmal den Plugin-Ordner mit dem Namen inline-small-css-files und parken darin die Haupt-Plugin-Datei inline-small-css-files.php.

Die ganze Magie findet nun in der inline-small-css-files.php statt.

Der Plugin-Header

Der Plugin-Header ist ein auskommentierter Absatz mit Meta-Informationen zu unserem Plugin.

WordPress zieht sich daraus z.B. den Plugin-Namen, eine Beschreibung oder die Versionsnummer.

Der Plugin-Header wird am Anfang der Haupt-Plugin-Datei inline-small-css-files.php deklariert. Der Plugin-Header muss nicht in anderen PHP-Dateien deklariert werden, falls das Plugin aus Mehreren Dateien besteht. Einmal reicht.

<?php
/*
* Plugin Name:       Inline Small Stylesheets
* Description:       Converts stylesheet links to inline CSS when the amount of data to be loaded is less than 1KB.
* Version:           1.0.0
* Requires at least: 6.0
* Requires PHP:      7.4
* Author:            Robert Rosanke
* Author URI:        https://webfluence.de
* License:           GPL v2 or later
* License URI:       https://www.gnu.org/licenses/gpl-2.0.html
* Text Domain:       inline-small-stylesheets
* Domain Path:       /languages
*/

Hier die Erklärung zu einigen Feldern, die ggf. nicht selbsterklärend sind:

  • Version: Plugin-Versionsnummer. Falls Updates von dem Plugin an Dritte weitergegeben werden, sollte diese Nummer aktualisiert werden.
  • Requires at least: Die WordPress-Version, die mindestens benötigt wird, damit das Plugin läuft.
  • Requires PHP: Die PHP-Version, die mindestens benötigt wird, damit das Plugin läuft.
  • Text Domain: Diese Angabe wird für Übersetzungen von Texten im Plugin-Code genutzt. Mehr dazu in meinem Artikel „WordPress übersetzten„.

Weitere interessante Meta-Angaben sind z.B. Update URI oder Plugin URI.

Mehr dazu: Header Requirements (engl.)

Zu Beginn der PHP-Datei – ABSPATH-Check

Nach den auskommentierten Meta-Angaben füge ich einen Code-Schnipsel ein, den ich in der Regel zu Beginn jeder PHP-Datei in Plugins und Themes einfüge.

if (!defined('ABSPATH')) {
  exit; // Exit if accessed directly
}

Der Code bricht die Code-Ausführung ab, wenn die von WordPress üblicherweise gesetzte Konstante ABSPATH nicht definiert ist.

Damit soll verhindert werden, dass Externe einzelne PHP-Dateien direkt aufrufen können und unerwartetes Verhalten ausgelöst wird.

Der Plugin-Code – kleine CSS-Dateien inline ausliefern

add_filter('style_loader_tag', 'inline_small_stylesheets_filter_style_loader_tag', 10, 4);

function inline_small_stylesheets_filter_style_loader_tag($tag, $handle, $href, $media) {
  if (is_admin())
      return $tag;


  $home_url = trailingslashit(get_site_url());

  if (!str_contains($href, $home_url))
      return $tag;


  $file_path = inline_small_stylesheets_get_file_path_by_url($href);
  $file_size = $file_path && file_exists($file_path) ? filesize($file_path) : false;

  $kb_in_bytes = 1000;
  $is_larger_than_1_kb = $file_size !== false  && $file_size > 1 * $kb_in_bytes;


  if ($is_larger_than_1_kb || !in_array($media, ['screen', 'all', '']))
      return $tag;

  $css = file_get_contents($file_path);
  $forbidden_strings = ['</style', 'url', '@font-face', '@import'];
  foreach ($forbidden_strings as $forbidden_string) {
    if (str_contains($css, $forbidden_string))
      return $tag;
  }

  $tag = '<style type="text/css" data-src="' . esc_url($href) . '" data-handle="' . esc_attr($handle) . '" data-media="' . esc_attr($media) . '">' . $css   . '</style>';


  return $tag;
}

Was macht der Code?

  1. Prüfen, ob die CSS-Datei von der eigenen Domain ausgeliefert wird. Abbrechen und normalen <link />-Tag ausgeben, wenn nicht.
  2. Die Dateigröße abrufen und prüfen, ob sie größer als 1 KB ist.
  3. Wenn die Datei größer als 1KB ist oder ggf. nur unter bestimmten Bedingungen – z.B. print – geladen werden soll, dann abbrechen und normalen <link />-Tag ausgeben.
  4. Inhalt der Datei inline in <styles />-Tag ausgeben, wenn nicht ein verbotener string beinhaltet ist.

Mit dem Ausschließen „verbotener strings“ versuche ich das Potential für Fehler auf der Seite zu reduzieren.

Fehler könnten z.B. sein:

  • Schriftarten oder Icons werden nicht korrekt geladen, weil sich durch das inline-Ausliefern der CSS-Regeln die relativen Pfadangaben von url() und @font-face verschieben.
  • irgendwo im CSS-file steht ein schließendes style-Tag und wir haben CSS-Regeln im Hauptinhalt der Seite stehen, die dort überhaupt nicht hingehören.

Verbesserungsmöglichkeiten für den gezeigten Code

  • gzencode() und strlength() nutzen, um der tatsächlichen Übertragungsgröße der Datei näher zu kommen
  • „verbotene strings“ ggf. erweitern. Eventuell mit regulären Ausdrücken arbeiten, um Dateien, die auf absolute URLs linken, zu erlauben.
  • Schwelle – in dem Fall 1 KB – je nach eigenem Bedarf anpassen

Am Ende der Datei füge ich noch zwei „Hilfsfunktionen“ ein.

// src: https://www.php.net/manual/de/function.str-contains.php#125977
if (!function_exists('str_contains')) {
  function str_contains($haystack, $needle) {
      return $needle !== '' && mb_strpos($haystack, $needle) !== false;
  }
}

Einmal ein Fallback, für die PHP-Funktion str_contains(), die erst ab Version 8 verfügbar ist. Wir erinnern uns: im Plugin-Header wurde PHP 7.4 als Anforderung hinterlegt.

function inline_small_stylesheets_get_file_path_by_url(string $url): string {
  $parsed = parse_url($url);
  if (empty($parsed['path']))
      return '';

  $path = ltrim($parsed['path'], '/');
  $path = rtrim($parsed['path'], '/');
 
  return trailingslashit(ABSPATH)  . $path;
}

Und einmal ein Helferlein, dass mir den absoluten Pfad zu einer Datei anhand der URL errechnet.

Plugin ausprobieren

Um das Plugin auszuprobieren, muss es lediglich einmal als zip-Datei gespeichert werden.

Dazu einfach den Plugin-Ordner im Dateisystem/Explorer aufrufen, Rechtsklick und als zip komprimieren.

Diesen zip-Ordner dann im WordPress-Backend hochladen und das Plugin aktivieren.

Um die Funktion zu testen, bietet es sich an, eine Beispielseite einmal mit und ohne aktiviertes Plugin aufzurufen und in den devTools zu schauen, ob die Anzahl der Requests tatsächlich reduziert wurde.

Besonders gute Erfahrungen habe ich mit diesem Ansatz bei Seiten gemacht, die grundsätzlich überladen sind und viele Requests machen. Je nach Seite können 5 bis 10 Performance-Punkte im PageSpeed-Insights-Test gesammelt werden. Und das mit so einer kleinen Anpassung.

Code-Snippets: Typische Aktionen und Funktionen in Plugins

HTML sicher ausgeben

Achte beim zusammenstellen und ausgeben von HTML-Code darauf, dass möglicherweise schädliche oder unerwünschte Inhalte sicherheitshalber entfernt oder unschädlich gemacht werden.

Dazu gibt es in WordPress zahlreiche Hilfsfunktionen.

Mit zu den von mir am häufigsten genutzten gehören vermutlich esc_html(), esc_attr(), und esc_url(). Auch wp_kses() landet früher oder später in vielen meiner Plugins.

Ein Beispiel für den Einsatz finden wir weiter oben im Artikel – im Code vom kleinen Plugin.

$tag = '<style type="text/css" data-src="' . esc_url($href) . '" data-handle="' . esc_attr($handle) . '" data-media="' . esc_attr($media) . '">' . $css   . '</style>';
  • Die Variable $tag speichert das HTML des auszugebenden style-Tags.
  • Im HTML sind einige data-Attribute zu sehen. Die Werte dieser Attribute werden mit esc_attr() vorbereitet. Sofern der erwartete Wert eine URL ist, wird esc_url() genutzt.

Beispiele und Erklärungen für die Verwendung der Funktionen:

esc_html()

Zweck

Text sicher ausgeben. Sorgt z.B. dafür, dass innerhalb eines strings befindliches HTML als plain text ausgegeben wird statt die tatsächliche Struktur des HTML-Dokuments zu verändern.

Beispiel

In einem string befindet sich ein inline script-Tag.

Könnte aus der Datenbank kommen, könnte von einer externen API kommen, die nutzergenerierten Inhalt verwaltet und abrufbar macht. Könnte auch von einem Übersetzer kommen, der über Sprachdateien – vllt. versehentlich – HTML einschleust.

$text = "Hallo <script>alert('dieser Code sollte niemals ausgeführt werden')</script>";
Anwendung

Um in einem string möglicherweise schädlichen Inhalt, wie das script-Tag aus dem Beispiel oben, unschädlich zu machen, wird im PHP-Template esc_html() bei der Ausgabe des strings genutzt.

<p><?php echo esc_html($text); ?></p>
Ergebnis

Oben ohne esc_html(), unten mit esc_html().

Das Bild zeigt direkt, wie der mit esc_html() ausgegebene Text keinen Schaden mehr anrichten kann, weil er zu 100% als Text interpretiert wird.

Der ohne esc_html() ausgegebene string könnte Schaden anrichten, weil das über den string eingeschleuste script-Tag nicht entschärft wurde und dadurch auch im Browser ausgeführt wird. Ein Angreifer könnte nun also Code auf deiner Seite ausführen lassen.

esc_attr()

Zweck

HTML-Attribute sicher ausgeben. Sorgt z.B. dafür, dass innerhalb eines strings befindliche Anführungszeichen so ausgegeben werden, dass sie nicht unerwartet das Attribut schließen und das HTML dadurch unerwartet verändert wird.

Beispiel

In einem Attribut-Wert – string – befindet sich ein Anführungszeichen und Schadcode.

$val = '#" onclick="location.href=\'https://example.org/\'"';

Würde der Wert nun einfach ausgegeben, dann würde ggf. etwas passieren, das nicht passieren soll.

echo "<a href=\"$val\">link</a>";

// <a href="#" onclick="location.href='https://example.org/'"">link</a>
Anwendung

Um das Beispiel oben unschädlich zu machen, wird im PHP-Template esc_attr() bei der Ausgabe des strings genutzt.

echo "<a href=" . esc_attr($val) . ">link</a>";
Ergebnis

Oben ohne esc_attr(), unten mit esc_attr().

Das Bild zeigt direkt, wie der mit esc_attr() ausgegebene href-Wert keinen Schaden mehr anrichten kann, weil er zu 100% als Attribut-Wert intepretiert wird.

Der ohne esc_attr() ausgegebene string könnte Schaden anrichten, weil das über den string eingeschleuste Anführungszeichen nicht entschärft wurde und dadurch das href-Attribut geschlossen wird. onclick wird dann einfach als weitere Attribut geparst und im Zweifel auch ausgeführt.

esc_url()

Ähnlich zu esc_attr(). In Plugins, die ich schreibe, wird die Funktion hauptsächlich für das Ausgeben von URLs in <img src="#" /> oder <a href="#"> genutzt.

Laut Dokumentation sind auch andere Einsatzzwecke möglich, kann mich jedoch nicht erinnern, diese häufig zu nutzen.

Mehr dazu und weitere Funktionen zum absichern der HTML-Ausgabe in der offiziellen Entwicklerdokumentation: Escaping data (engl.)

Inline JavaScript ins Frontend einfügen

Manchmal ist es notwendig, auf einzelnen Unterseiten JavaScript hinzuzufügen.

Wenn es nur wenig Code ohne eine Abhängigkeit zu anderen JavaScript-Dateien, wie jQuery, ist, kann dafür z.B. ein <script>-Tag direkt inline genutzt werden.

Wer eine aktuelle WordPress-Version installiert hat, kann über die Wahl der hook prinzipiell zwischen drei Positionen wählen:

  • wp_head: im head-Tag
  • wp_body_open: am Anfang vom body-Tag
  • wp_footer: am Ende vom body-Tag

In der Regel fällt die Entscheidung auf das Ende vom <body>-Tag, weil das HTML an dieser Stelle bereits geparst ist und die DOM-Elemente in Skripten zur Verfügung stehen.

Im Folgenden Beispiel fügen wir ein inline script in am Ende vom <body>-Tag der Startseite ein. Wir nutzen also wp_footer.

add_action('wp_footer', 'webfluence_wp_footer_print_inline_script');

function(webfluence_wp_footer_print_inline_script){
  if( !is_front_page() )
    return;

  $title = get_the_title();

  ?>
  <script>
    const text = <?php echo esc_js( $title ); ?>
    alert(text)
  </script>
  <?php
}

Die Position des inline Skripts im jeweiligen Bereich lässt sich über einen optionalen Parameter beim Aufruf von add_action() steuern.

Skript möglichst früh ausgeben:

add_action('wp_footer', 'webfluence_wp_footer_print_inline_script', 1);

Skript möglichst spät ausgeben:

add_action('wp_footer', 'webfluence_wp_footer_print_inline_script', 9999);

Je höher die Zahl ist, um so später wird die callback-Funktion aufgerufen. Das Skript wird also weiter hinten, nach anderen Skripten, im HTML platziert.

Diesen Artikel in sozialen Netzwerken teilen: