Aufbau: Header, Struktur und Bootstrap
Begriffe vorab
- Plugin-Header: der Kommentarblock am Anfang der Hauptdatei, an dem WordPress ein Plugin erkennt.
- Hauptdatei: die PHP-Datei mit dem Header; sie wird von WordPress direkt geladen.
- Bootstrap: der Startcode, der Konstanten setzt, Klassen lädt und Hooks registriert.
- Autoloading: Klassen werden bei Bedarf automatisch geladen, statt jede Datei von Hand einzubinden.
- Präfix: eindeutiger Namensanfang für Funktionen, Konstanten und Optionen (hier
mz_).
Das Beispiel dieses Deepdives
Wir bauen durchgängig ein kleines Plugin namens „Merkzettel“: Angemeldete Nutzer können Beiträge auf eine persönliche Merkliste setzen. Es braucht eine Einstellungsseite, ein Shortcode, eine Schnittstelle zum Speichern, eine eigene Tabelle und einen Aufräumjob – genug, um alle wichtigen Bausteine zu zeigen.
Verzeichnisstruktur
merkzettel/
├── merkzettel.php ← Hauptdatei mit Header (dünn halten)
├── uninstall.php ← Aufräumen beim Löschen
├── includes/
│ ├── class-plugin.php ← Startlogik, registriert Hooks
│ ├── class-admin.php ← Einstellungen
│ └── class-store.php ← Datenbankzugriff
├── assets/
│ └── merkzettel.js
└── languages/
Der Header
Nur Plugin Name ist Pflicht. Sinnvoll sind außerdem Angaben zu Mindestversionen, damit WordPress eine Aktivierung auf zu alten Systemen verhindert:
<?php
/**
* Plugin Name: Merkzettel
* Description: Persönliche Merkliste für Beiträge.
* Version: 1.0.0
* Requires at least: 6.5
* Requires PHP: 8.0
* Author: Beispiel GmbH
* License: GPL-2.0-or-later
* Text Domain: merkzettel
* Domain Path: /languages
*/
defined( 'ABSPATH' ) || exit;
Seit WordPress 6.5 gibt es zusätzlich den Header Requires Plugins, in dem man durch Komma getrennt die Slugs von Plugins nennt, die installiert sein müssen. Der Header Update URI verhindert, dass ein selbst verteiltes Plugin versehentlich durch ein gleichnamiges aus dem WordPress.org-Verzeichnis überschrieben wird.
Bootstrap: dünne Hauptdatei
Die Hauptdatei sollte nur Konstanten setzen und die eigentliche Arbeit anstoßen. Alles andere liegt in Klassen, die sich bei Bedarf laden:
define( 'MZ_VERSION', '1.0.0' );
define( 'MZ_FILE', __FILE__ );
define( 'MZ_PATH', plugin_dir_path( __FILE__ ) );
define( 'MZ_URL', plugin_dir_url( __FILE__ ) );
spl_autoload_register( function ( $class ) {
if ( strpos( $class, 'MZ_' ) !== 0 ) {
return;
}
$file = MZ_PATH . 'includes/class-' . strtolower( str_replace( '_', '-', substr( $class, 3 ) ) ) . '.php';
if ( is_readable( $file ) ) {
require_once $file;
}
} );
add_action( 'plugins_loaded', array( 'MZ_Plugin', 'init' ) );
Warum Präfixe und der ABSPATH-Schutz
Alle Plugins teilen sich einen globalen PHP-Namensraum. Eine Funktion save_item() ohne Präfix kollidiert früher oder später mit einem anderen Plugin und löst einen Fatal Error aus. Das ABSPATH-Prüfen am Dateianfang verhindert, dass die Datei direkt über die URL ausgeführt wird, außerhalb von WordPress.
Faustregel: Die Hauptdatei bleibt unter etwa 50 Zeilen. Wächst sie, ist Logik im falschen Ort.
Zum Selbermachen
- Lege den Ordner
merkzettel mit Hauptdatei und Header an.
- Aktiviere das Plugin im Backend und prüfe, dass es in der Liste erscheint.
- Setze testweise
Requires PHP: 99 und beobachte, dass die Aktivierung verweigert wird.
Prüfstatus: Belegt (Stand 1. Oktober 2026): Jahreszahlen, Zahlen, Namen und Quellenangaben dieser Lektion, soweit die Quellenliste sie nennt, wurden gegen Primärquellen geprüft. Nicht einzeln belegt: erklärende Darstellung nach Lehrbuchstand und Quellen, die in der Liste als „allgemeine Referenz“ markiert sind. Codebeispiele sind nur auf Syntax geprüft, nicht in WordPress ausgeführt.
Quellen
- WordPress Plugin Handbook – „Header Requirements“ (developer.wordpress.org/plugins/plugin-basics/header-requirements/) (allgemeine Referenz, nicht Zeile für Zeile geprüft)
- WordPress Plugin Handbook – „Best Practices“ (Präfixe, Dateistruktur; developer.wordpress.org/plugins/plugin-basics/best-practices/) (allgemeine Referenz, nicht Zeile für Zeile geprüft)
- PHP-Handbuch – „spl_autoload_register“ (php.net) (allgemeine Referenz, nicht Zeile für Zeile geprüft)
Lebenszyklus: Aktivierung, Deaktivierung, Deinstallation
Begriffe vorab
- Aktivierung: der Moment, in dem ein Administrator das Plugin einschaltet.
- Deaktivierung: das Ausschalten; das Plugin bleibt installiert.
- Deinstallation: das Löschen des Plugins über das Backend.
- DB-Version: eine in der Datenbank gespeicherte Zahl, mit der man erkennt, ob Struktur-Updates nötig sind.
Drei Ereignisse, drei Aufgaben
- Aktivierung: einmalige Einrichtung – Tabellen anlegen, Standardwerte setzen, Cron-Aufgaben planen.
- Deaktivierung: aufräumen, was ohne Plugin ins Leere liefe – geplante Aufgaben entfernen. Keine Nutzerdaten löschen.
- Deinstallation: endgültiges Entfernen aller Daten, die das Plugin angelegt hat.
Aktivierung und Deaktivierung
register_activation_hook( MZ_FILE, 'mz_activate' );
register_deactivation_hook( MZ_FILE, 'mz_deactivate' );
function mz_activate() {
MZ_Store::create_table(); // siehe Datenbank-Lektion
add_option( 'mz_settings', array( 'max_items' => 50 ) );
update_option( 'mz_db_version', MZ_VERSION );
if ( ! wp_next_scheduled( 'mz_cleanup' ) ) {
wp_schedule_event( time(), 'daily', 'mz_cleanup' );
}
flush_rewrite_rules(); // nur nötig, wenn Rewrite-Regeln registriert wurden
}
function mz_deactivate() {
wp_clear_scheduled_hook( 'mz_cleanup' );
flush_rewrite_rules();
}
Wichtig: Die Aktivierungs-Funktion wird nicht ausgeführt, wenn ein Plugin per Update ersetzt wird. Strukturänderungen in einer neuen Version gehören deshalb nicht allein dorthin. Üblich ist ein Versionsvergleich beim Start:
add_action( 'plugins_loaded', function () {
if ( get_option( 'mz_db_version' ) !== MZ_VERSION ) {
MZ_Store::create_table(); // dbDelta ist wiederholbar
update_option( 'mz_db_version', MZ_VERSION );
}
} );
Deinstallation: uninstall.php
Das Handbuch empfiehlt eine Datei uninstall.php im Plugin-Hauptordner. WordPress führt sie aus, wenn das Plugin im Backend gelöscht wird. Die Alternative register_uninstall_hook() speichert den Callback in einer Datenbankoption und gilt als weniger geeignet. In uninstall.php prüft man zuerst die Konstante WP_UNINSTALL_PLUGIN, damit die Datei nicht von außen aufrufbar ist:
<?php
defined( 'WP_UNINSTALL_PLUGIN' ) || exit;
delete_option( 'mz_settings' );
delete_option( 'mz_db_version' );
global $wpdb;
$wpdb->query( "DROP TABLE IF EXISTS {$wpdb->prefix}mz_items" );
Daten beim Löschen zu entfernen ist die saubere Regel – für wertvolle Nutzerdaten bietet sich aber eine Einstellung „Daten beim Löschen behalten“ an, die uninstall.php berücksichtigt. Ein versehentliches Löschen darf nicht alle Merklisten vernichten.
Zum Selbermachen
- Registriere die Hooks und schreibe bei Aktivierung eine Option.
- Aktiviere und deaktiviere das Plugin und prüfe in der Datenbank (
wp_options), was bleibt.
- Lösche das Plugin und prüfe, dass nichts zurückbleibt.
Sonderfall Multisite
In einer Multisite-Installation läuft der Aktivierungs-Hook bei einer Netzwerk-Aktivierung einmal für das Netzwerk, nicht automatisch für jede einzelne Website. Legt dein Plugin pro Website Tabellen oder Optionen an, muss der Code die Websites selbst durchlaufen oder die Einrichtung beim ersten Aufruf einer Website nachholen. Für kleine Plugins ohne eigene Tabellen ist das meist unproblematisch.
Typische Fehler
- In der Aktivierung Ausgaben erzeugen (Leerzeichen, Meldungen) – das stört das Backend und löst Hinweise aus.
- In der Deaktivierung Daten löschen, sodass ein kurzes Aus- und Wiedereinschalten alles vernichtet.
- Aufräumcode nur in der Deaktivierung, aber nicht in
uninstall.php.
Prüfstatus: Belegt (Stand 1. Oktober 2026): Jahreszahlen, Zahlen, Namen und Quellenangaben dieser Lektion, soweit die Quellenliste sie nennt, wurden gegen Primärquellen geprüft. Nicht einzeln belegt: erklärende Darstellung nach Lehrbuchstand und Quellen, die in der Liste als „allgemeine Referenz“ markiert sind. Codebeispiele sind nur auf Syntax geprüft, nicht in WordPress ausgeführt. Die Aussage, dass der Aktivierungs-Hook bei einem Plugin-Update nicht ausgeführt wird, ist Standardwissen ohne eigene Fundstelle.
Quellen
- WordPress Plugin Handbook – „Activation / Deactivation Hooks“ und „Uninstall Methods“ (developer.wordpress.org/plugins/plugin-basics/) (allgemeine Referenz, nicht Zeile für Zeile geprüft)
- WordPress Developer Resources – Funktionsreferenz zu register_uninstall_hook() (allgemeine Referenz, nicht Zeile für Zeile geprüft)
- WordPress Developer Resources – Funktionsreferenzen zu wp_schedule_event() und wp_clear_scheduled_hook() (allgemeine Referenz, nicht Zeile für Zeile geprüft)