WordPress-Entwicklung nach aktuellem Standard: Architektur, Hooks, klassische und Block-Themes, Plugins, Datenmodell, Sicherheit, Blöcke, REST API und Best Practices.
Grundbegriffe: Was ist WordPress?
Begriffe vorab
Website: eine Sammlung von Seiten, die über eine Adresse im Internet erreichbar sind.
CMS (Content-Management-System): Software, mit der sich Inhalte ohne Programmierung anlegen und verwalten lassen.
WordPress: ein quelloffenes CMS, das 2003 von Matt Mullenweg und Mike Little als Weiterentwicklung der Blogsoftware b2/cafelog veröffentlicht wurde.
Beitrag und Seite: Beiträge sind zeitlich geordnete Inhalte (z. B. Blogartikel), Seiten sind eigenständige Inhalte (z. B. „Kontakt“).
Was WordPress ausmacht
WordPress trennt Inhalt, Darstellung und Funktion: Inhalte liegen in einer Datenbank, das Theme bestimmt, wie sie aussehen, und Plugins ergänzen Funktionen wie Kontaktformulare oder Suchmaschinenoptimierung. Dadurch lassen sich Aussehen und Funktionen unabhängig voneinander ändern.
Die wichtigsten Begriffe im Überblick
Theme: legt Layout, Farben und Schrift fest.
Plugin: Erweiterung, die Funktionen hinzufügt.
Block: Baustein des Editors, etwa Absatz, Bild oder Tabelle.
Hook: Stelle im Programmablauf, an der eigener Code eingehängt werden kann.
Benutzerrolle: legt fest, was ein Konto darf (z. B. Administrator, Redakteur, Autor).
Permalink: die dauerhafte Adresse eines Inhalts.
Verbreitung und Einordnung
WordPress ist das am weitesten verbreitete CMS: Nach den Erhebungen von W3Techs laufen etwa 40 bis 43 Prozent aller Websites mit WordPress (Stand 2026; der Wert schwankt leicht). Das bedeutet viele Erweiterungen und Hilfe, aber auch, dass die Software ein häufiges Angriffsziel ist; Updates und Sicherheitsregeln sind deshalb wichtig.
Ein Beispiel vom Anlegen bis zum Anzeigen
Eine Redakteurin legt im Editor einen Beitrag mit Überschrift, Text und Bild an.
WordPress speichert ihn in der Datenbank.
Ein Besucher ruft die Adresse auf.
WordPress lädt den Beitrag und wählt ein Template des Themes.
Das Theme formatiert den Inhalt, und die Seite erscheint im Browser.
Faustregel für den Einstieg: Inhalte gehören in Beiträge und Seiten, Aussehen ins Theme, Funktionen in Plugins. Wer diese Trennung beachtet, kann später jedes Teil austauschen, ohne alles neu zu bauen.
Zum Selbermachen
Installiere WordPress lokal oder in einer Testumgebung.
Lege eine Seite und einen Beitrag an und sieh dir den Unterschied in der Anzeige an.
Wechsle das Theme und beobachte, was gleich bleibt und was sich ändert.
Wer mit WordPress arbeitet
Typische Rollen sind Redakteurinnen, die Inhalte pflegen, Gestalter, die das Theme anpassen, und Entwicklerinnen, die Plugins und Themes programmieren. Jede Rolle braucht nur die Rechte, die sie tatsächlich benötigt – ein Grundsatz, der sich in den Benutzerrollen von WordPress widerspiegelt und später in der Sicherheitslektion wieder auftaucht.
Wie es in diesem Lernpaket weitergeht
Die nächsten Lektionen erklären die Architektur, das Hook-System, klassische und Block Themes, Plugins, das Datenmodell, Sicherheit, Blöcke und REST API sowie Übersetzung, Performance und Debugging. Jede beginnt mit den nötigen Begriffen.
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.org – „About WordPress“ und „History“ (wordpress.org/about) (allgemeine Referenz, nicht Zeile für Zeile geprüft)
W3Techs – „Usage statistics of content management systems“ (Marktanteile, laufend aktualisiert) (allgemeine Referenz, nicht Zeile für Zeile geprüft)
WordPress Developer Resources – „Glossary“ (developer.wordpress.org) (allgemeine Referenz, nicht Zeile für Zeile geprüft)
Admin-Oberfläche: Settings API und Meta-Boxen
Begriffe vorab
Admin-Menü: Einträge in der linken Backend-Leiste.
Settings API: WordPress-Schnittstelle, um Einstellungen mit Bereichen und Feldern zu registrieren und sicher zu speichern.
Meta-Box: ein Kasten im Beitragseditor für Zusatzfelder.
Capability: eine einzelne Berechtigung wie manage_options.
Eine Einstellungsseite in drei Schritten
Die Settings API übernimmt den mühsamen Teil: Nonce-Prüfung, Rechtekontrolle, Speichern und Sanitizing laufen über options.php. Du beschreibst nur, was gespeichert werden soll.
settings_fields() gibt das versteckte Nonce-Feld und die Gruppe aus; options.php prüft beides und ruft deinen sanitize_callback auf. Die Prüfung der Bereichsgrenzen dort ist wichtig, weil niemand dem Formular vertrauen darf.
Die drei Prüfungen im Speichern-Callback – Autosave überspringen, Nonce verifizieren, Recht prüfen – gehören immer zusammen. Fehlt eine, entsteht entweder ein Sicherheitsloch oder ein Fehler beim automatischen Zwischenspeichern.
Zum Selbermachen
Baue die Einstellungsseite nach und speichere einen Wert außerhalb des erlaubten Bereichs – er wird begrenzt.
Füge ein zweites Feld (Checkbox) hinzu und erweitere den Sanitizer.
Prüfe in der Datenbank, wie die Option als Array gespeichert 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 – „Settings API“ und „Administration Menus“ (developer.wordpress.org/plugins/settings/) (allgemeine Referenz, nicht Zeile für Zeile geprüft)
WordPress Developer Resources – Funktionsreferenzen zu register_setting(), add_settings_field(), add_meta_box() (allgemeine Referenz, nicht Zeile für Zeile geprüft)
WordPress Plugin Handbook – „Security: Nonces, Data Validation“ (developer.wordpress.org/plugins/security/) (allgemeine Referenz, nicht Zeile für Zeile geprüft)
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.
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:
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)
Eigene Tabellen mit dbDelta und $wpdb
Begriffe vorab
$wpdb: die globale Datenbankklasse von WordPress.
Tabellenpräfix:$wpdb->prefix, in der Konfiguration festgelegt (nicht fest wp_).
dbDelta: Funktion, die Tabellen anlegt oder an eine neue Definition anpasst.
Prepared Statement: eine Abfrage mit Platzhaltern, die Werte sicher einsetzt.
Wann eine eigene Tabelle?
Für wenige, einfache Werte genügen Optionen und Post-Meta. Eine eigene Tabelle lohnt, wenn viele Datensätze mit eigenem Aufbau entstehen oder gezielt abgefragt werden: Merklisten vieler Nutzer, Statistikeinträge, Protokolle. Post-Meta bei Millionen Zeilen wird langsam, eine passend indexierte Tabelle nicht.
Tabelle mit dbDelta anlegen
dbDelta() vergleicht die gewünschte Definition mit der vorhandenen und legt an oder ändert. Das macht es wiederholbar – ideal für Updates. Es ist aber pingelig bei der Schreibweise:
Jedes Feld in einer eigenen Zeile.
Zwei Leerzeichen zwischen PRIMARY KEY und der Definition.
Das Wort KEY statt INDEX.
Keine Apostrophe oder Backticks um Feldnamen.
class MZ_Store {
public static function table() {
global $wpdb;
return $wpdb->prefix . 'mz_items';
}
public static function create_table() {
global $wpdb;
$charset = $wpdb->get_charset_collate();
$sql = "CREATE TABLE " . self::table() . " (
id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
user_id bigint(20) unsigned NOT NULL,
post_id bigint(20) unsigned NOT NULL,
created datetime NOT NULL,
PRIMARY KEY (id),
UNIQUE KEY user_post (user_id,post_id),
KEY post_id (post_id)
) $charset;";
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
dbDelta( $sql );
}
Der eindeutige Schlüssel user_post sorgt dafür, dass derselbe Beitrag pro Nutzer nur einmal gespeichert werden kann – die Datenbank erzwingt, was die Anwendung sonst prüfen müsste.
Sicher lesen und schreiben
public static function add( $user_id, $post_id ) {
global $wpdb;
return $wpdb->insert(
self::table(),
array( 'user_id' => $user_id, 'post_id' => $post_id, 'created' => current_time( 'mysql', true ) ),
array( '%d', '%d', '%s' )
);
}
public static function for_user( $user_id, $limit = 50 ) {
global $wpdb;
return $wpdb->get_results( $wpdb->prepare(
'SELECT post_id, created FROM ' . self::table() . ' WHERE user_id = %d ORDER BY created DESC LIMIT %d',
$user_id, $limit
) );
}
}
Der Tabellenname darf nicht über einen Platzhalter eingesetzt werden, Werte schon. Deshalb kommt der Name aus $wpdb->prefix und fester Zeichenfolge, alle Nutzerwerte laufen über %d/%s. Eine Verkettung von Nutzereingaben in SQL ist die klassische Einfallstür für SQL-Injection.
Zum Selbermachen
Lege die Tabelle über die Aktivierung an und prüfe sie in einem Datenbankwerkzeug.
Füge denselben Eintrag zweimal ein und beobachte, dass der eindeutige Schlüssel den Doppeleintrag verhindert.
Ergänze eine Spalte, erhöhe die DB-Version und prüfe, dass dbDelta die Tabelle anpasst.
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 – „Creating Tables with Plugins“ (dbDelta-Regeln; developer.wordpress.org/plugins/creating-tables-with-plugins/) (allgemeine Referenz, nicht Zeile für Zeile geprüft)
WordPress Developer Resources – Klassenreferenz zu wpdb (insert, prepare, get_results, get_charset_collate) (allgemeine Referenz, nicht Zeile für Zeile geprüft)
Frontend: Shortcode, Assets, REST und AJAX
Begriffe vorab
Shortcode: ein Platzhalter wie [merkzettel], den WordPress beim Ausgeben durch Code ersetzt.
Enqueue: das geordnete Einbinden von Skripten und Styles über WordPress.
AJAX: Daten im Hintergrund an den Server senden, ohne die Seite neu zu laden.
REST-Route: eine URL der WordPress-REST-API, an die ein Plugin eigene Funktionen hängt.
Nonce: kurzlebiges Token, das die Herkunft einer Anfrage bestätigt.
Shortcode, der nur bei Bedarf Assets lädt
Ein häufiger Fehler ist, Skripte auf jeder Seite zu laden. Besser: Skript registrieren und erst im Shortcode einreihen. So wird es nur dort geladen, wo der Shortcode steht.
Wichtig: Ein Shortcode-Callback gibt zurück, er gibt nicht aus. wp_json_encode() und esc_url_raw() sorgen dafür, dass Werte sicher im Skript landen.
REST statt admin-ajax
Für neue Plugins ist die REST API der empfohlene Weg: klare URLs, Versionierung, Rechteprüfung pro Route. Die Route wird auf rest_api_init registriert; der permission_callback ist verpflichtend.
Bei Cookie-Anmeldung muss jede Anfrage das Nonce zur Aktion wp_rest mitsenden, entweder im Header X-WP-Nonce oder als Parameter _wpnonce. Fehlt es, setzt WordPress den Nutzer auf 0 – die Anfrage gilt als anonym, auch wenn man angemeldet ist.
Wenn doch admin-ajax.php
Bestehender Code nutzt oft die Hooks wp_ajax_{aktion} (angemeldet) und wp_ajax_nopriv_{aktion} (nicht angemeldet) mit admin-ajax.php. Das funktioniert weiter, bietet aber weniger Struktur als REST. Auch dort gehören Nonce-Prüfung und Rechtekontrolle in den Handler.
Zum Selbermachen
Baue den Shortcode und prüfe im Seitenquelltext, dass das Skript nur auf Seiten mit Shortcode erscheint.
Rufe die Route ohne Nonce auf und beobachte die Antwort.
Sende einen ungültigen post_id und prüfe den 404-Fehler.
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 – „Shortcodes“ (developer.wordpress.org/plugins/shortcodes/) (allgemeine Referenz, nicht Zeile für Zeile geprüft)
WordPress REST API Handbook – „Adding Custom Endpoints“ und „Authentication“ (developer.wordpress.org/rest-api/) (allgemeine Referenz, nicht Zeile für Zeile geprüft)
WordPress Developer Resources – Funktionsreferenz zu wp_register_script() (Ladestrategie ab WordPress 6.3) (allgemeine Referenz, nicht Zeile für Zeile geprüft)
Grundlagen und Architektur
Begriffe vorab
CMS (Content-Management-System): Software, mit der sich Inhalte ohne Programmierkenntnisse erstellen und verwalten lassen.
Kern (Core): der von WordPress selbst gelieferte Code.
Hook: definierte Stelle im Ablauf, an der eigener Code eingehängt werden kann.
Datenbank: der Speicher, in dem Beiträge, Einstellungen und Benutzer liegen.
WordPress ist ein PHP-basiertes Content-Management-System mit einer MySQL/MariaDB-Datenbank. Der Kern liefert die Grundfunktionen, Themes bestimmen die Darstellung und Plugins erweitern das Verhalten. Diese Trennung ist das wichtigste Architekturprinzip: Eigene Anpassungen gehören nie in den Kern.
Verzeichnisstruktur
wp-admin/ und wp-includes/: Kerncode, wird bei Updates überschrieben.
wp-content/: Alles Eigene, also themes/, plugins/, uploads/ und optional mu-plugins/.
wp-config.php: Datenbankzugang, Salts, Table-Prefix und Konstanten wie WP_DEBUG.
Must-Use-Plugins (mu-plugins/) werden automatisch geladen und lassen sich im Admin nicht deaktivieren. Sie eignen sich für zentrale Agentur- oder Infrastruktur-Logik.
Wichtige Datenbanktabellen
wp_posts: Beiträge, Seiten, Anhänge und eigene Post Types.
wp_postmeta: Zusatzfelder (Key-Value) zu Beiträgen.
wp_options: Einstellungen der Website.
wp_users und wp_usermeta: Benutzer und deren Metadaten.
wp_terms, wp_term_taxonomy, wp_term_relationships: Kategorien, Schlagwörter und eigene Taxonomien.
Der Präfix wp_ ist nur der Standard und wird in wp-config.php über $table_prefix festgelegt.
Ablauf einer Anfrage
Jede Anfrage läuft über index.php, lädt wp-load.php, dann Plugins, Theme und die Datenbank-Abfrage (Main Query). Anschließend wählt WordPress anhand der Template-Hierarchie das passende Template. Genau in diesem Ablauf setzen Hooks an, die Thema der nächsten Lektion sind.
Ändere niemals Dateien in wp-admin/ oder wp-includes/. Jede Änderung geht beim nächsten Update verloren.
Ein Beispiel für die Trennung
Soll eine Website ein Kontaktformular bekommen, installiert man ein Plugin statt den Kern zu ändern. Soll sie anders aussehen, wechselt man das Theme. Beides überlebt ein WordPress-Update, weil der Kern unberührt bleibt. Genau dafür existiert die Aufteilung: Kern aktualisieren, Eigenes bewahren.
Was bei einem Seitenaufruf geschieht
Der Webserver übergibt die Anfrage an index.php.
WordPress lädt Konfiguration, aktive Plugins und das Theme.
Aus der URL ermittelt es, welche Inhalte gemeint sind, und fragt die Datenbank.
Es wählt das passende Template und gibt das fertige HTML aus.
Häufige Anfängerfehler
Änderungen direkt im Kern oder in fremden Plugins – sie gehen beim Update verloren.
Die Datenbank von Hand ändern, ohne Sicherung.
Zu viele Plugins mit überlappender Funktion, die sich gegenseitig stören.
Vor jedem Update: Sicherung von Dateien und Datenbank anlegen und zuerst in einer Testumgebung prüfen.
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 Developer Resources – „Advanced Administration“ (Verzeichnisstruktur, wp-config.php) (allgemeine Referenz, nicht Zeile für Zeile geprüft)
WordPress Codex/Developer Resources – „Database Description“ (Tabellen wp_posts, wp_postmeta, wp_options, wp_terms) (allgemeine Referenz, nicht Zeile für Zeile geprüft)
Qualität und Veröffentlichung: Standards, Tests, Plugin Check
Begriffe vorab
WPCS: die WordPress Coding Standards, ein Regelwerk für Stil und gute Praxis.
PHPCS: PHP_CodeSniffer, das Werkzeug, das Code gegen solche Regeln prüft.
Plugin Check (PCP): ein offizielles Plugin, das ein anderes Plugin auf Verzeichnis-Anforderungen prüft.
Stable Tag: Angabe in der readme.txt, welche Version Nutzer als aktuelle sehen.
SemVer: Versionierung nach dem Schema Haupt.Neben.Fehlerkorrektur.
Automatische Prüfung als Routine
Gute Plugins prüfen sich selbst, bevor jemand anderes es tut. Drei Werkzeuge decken die wichtigsten Ebenen ab:
PHPCS mit WPCS findet Stilverstöße und typische Sicherheitsmängel (fehlendes Escaping, ungeprüfte Eingaben).
PHPUnit mit der WordPress-Testumgebung prüft Verhalten. Das Gerüst erzeugt WP-CLI mit wp scaffold plugin-tests.
Plugin Check prüft statisch (über PHP_CodeSniffer) und dynamisch (das Plugin wird aktiviert) auf Anforderungen des WordPress.org-Verzeichnisses sowie auf Hinweise zu Internationalisierung, Barrierefreiheit, Performance und Sicherheit.
# Stil und Sicherheit prüfen
composer require --dev wp-coding-standards/wpcs
vendor/bin/phpcs --standard=WordPress includes/ merkzettel.php
# Testgerüst erzeugen
wp scaffold plugin-tests merkzettel
Mindestversionen von WordPress und PHP im Header ehrlich angeben und tatsächlich testen.
Mit eingeschaltetem WP_DEBUG entwickeln, damit Hinweise zu veralteten Funktionen sichtbar werden.
Versionsnummern nach SemVer vergeben und Änderungen in einer Änderungsliste festhalten.
Veröffentlichung im WordPress.org-Verzeichnis
Die Richtlinien sind klar: Plugins müssen GPL-kompatibel lizenziert sein (empfohlen: „GPLv2 or later“), ihr Code muss weitgehend lesbar sein – Verschleierung, etwa durch Umbenennen in unlesbare Namen, ist nicht erlaubt; für minifizierte Dateien sollen lesbare Quellen mitgeliefert oder verlinkt werden – und Nutzer dürfen nicht ohne ihre Einwilligung verfolgt werden; Plugins dürfen keine externen Server ohne ausdrückliche, autorisierte Zustimmung kontaktieren, üblicherweise über ein Opt-in. Die readme.txt beschreibt das Plugin im Verzeichnis; ihr Stable tag bestimmt, welche Version als stabil ausgeliefert wird.
Ein Praxisbeispiel für das Opt-in-Prinzip ist das Tracking im Quiz-Academy-Plugin dieses Projekts: Es ist standardmäßig aus, bindet keinen eigenen Tracker ein und wird erst im Backend bewusst eingeschaltet.
Zum Selbermachen
Installiere PHPCS mit WPCS und prüfe dein Plugin. Behebe die ersten fünf Meldungen.
Installiere „Plugin Check“ und führe es für dein Plugin aus.
Schreibe eine readme.txt mit Beschreibung, Installation, Änderungsliste und Stable tag.
Eine Checkliste vor jedem Release
Versionsnummer in Header, Konstante und readme.txt angleichen.
PHPCS und Tests laufen lassen, Plugin Check ausführen.
Mit der niedrigsten unterstützten WordPress- und PHP-Version testen.
Änderungsliste ergänzen, Stable tag setzen.
Auf einer frischen Installation aktivieren, benutzen, deaktivieren und löschen – und prüfen, dass nichts zurückbleibt.
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 – „Detailed Plugin Guidelines“ (developer.wordpress.org/plugins/wordpress-org/detailed-plugin-guidelines/) (allgemeine Referenz, nicht Zeile für Zeile geprüft)
WordPress Plugin Handbook – „Plugin Readmes“ (developer.wordpress.org/plugins/wordpress-org/how-your-readme-txt-works/) (allgemeine Referenz, nicht Zeile für Zeile geprüft)
Make WordPress Plugins – „Introducing Plugin Check (PCP)“ und Plugin Check im Plugin-Verzeichnis (allgemeine Referenz, nicht Zeile für Zeile geprüft)
WordPress Coding Standards (github.com/WordPress/WordPress-Coding-Standards) (allgemeine Referenz, nicht Zeile für Zeile geprüft)
Hintergrundjobs und Zwischenspeicher: WP-Cron und Transients
Begriffe vorab
WP-Cron: der in WordPress eingebaute Zeitplan für wiederkehrende Aufgaben.
Event: eine geplante Ausführung eines Hooks.
Intervall: der Abstand zwischen zwei Ausführungen (z. B. hourly, daily).
Transient: ein zwischengespeicherter Wert mit Ablaufzeit.
Object Cache: ein Zwischenspeicher im Arbeitsspeicher, der Abfragen beschleunigt.
Wie WP-Cron arbeitet
WP-Cron ist kein echter Cronjob des Servers. Bei jedem Seitenaufruf prüft WordPress, ob geplante Aufgaben fällig sind, und stößt sie im Hintergrund über wp-cron.php an. Daraus folgt: Auf einer Seite ohne Besucher laufen Aufgaben verspätet oder gar nicht, und bei sehr viel Verkehr entstehen viele unnötige Prüfungen. Auf stark genutzten Seiten schaltet man deshalb die Seitenaufruf-Auslösung mit define( 'DISABLE_WP_CRON', true ) ab und ruft wp-cron.php stattdessen per echtem Server-Cronjob in festen Abständen auf. Wer das Abschalten ohne Ersatz tut, bekommt gar keine Ausführung mehr.
Aufgabe planen – nur einmal
Der häufigste Fehler: wp_schedule_event() bei jedem Seitenaufruf aufrufen und so Hunderte gleiche Aufgaben anlegen. Plane stattdessen bei der Aktivierung und prüfe vorher mit wp_next_scheduled():
// Aktivierung
if ( ! wp_next_scheduled( 'mz_cleanup' ) ) {
wp_schedule_event( time(), 'daily', 'mz_cleanup' );
}
// Die eigentliche Arbeit
add_action( 'mz_cleanup', function () {
global $wpdb;
$wpdb->query( $wpdb->prepare(
'DELETE i FROM ' . MZ_Store::table() . ' i LEFT JOIN ' . $wpdb->posts . ' p ON p.ID = i.post_id WHERE p.ID IS NULL AND i.created < %s',
gmdate( 'Y-m-d H:i:s', time() - DAY_IN_SECONDS )
) );
} );
// Deaktivierung
wp_clear_scheduled_hook( 'mz_cleanup' );
Die Aufgabe räumt Einträge zu inzwischen gelöschten Beiträgen weg. Eigene Intervalle ergänzt man über den Filter cron_schedules.
Transients als Zwischenspeicher
Teure Ergebnisse legt man mit einer Ablaufzeit ab, damit sie nicht bei jedem Aufruf neu berechnet werden:
function mz_popular_posts() {
$cached = get_transient( 'mz_popular' );
if ( false !== $cached ) {
return $cached;
}
global $wpdb;
$rows = $wpdb->get_results(
'SELECT post_id, COUNT(*) AS n FROM ' . MZ_Store::table() . ' GROUP BY post_id ORDER BY n DESC LIMIT 10'
);
set_transient( 'mz_popular', $rows, 15 * MINUTE_IN_SECONDS );
return $rows;
}
Ohne persistenten Object Cache liegen Transients in der Tabelle wp_options; mit einem Object Cache (z. B. Redis) liegen sie im Arbeitsspeicher. Ablaufzeiten sind Obergrenzen: Ein Transient kann früher verschwinden, deshalb muss der Code immer mit einem Fehlen rechnen.
Transients sind Zwischenspeicher, keine Datenbank. Alles, was nicht neu berechnet werden kann, gehört in Optionen oder eine Tabelle.
Zum Selbermachen
Plane die Aufgabe bei Aktivierung und prüfe sie mit dem Plugin „WP Crontrol“ oder wp cron event list.
Löse sie manuell aus und beobachte das Ergebnis.
Messe die Laufzeit von mz_popular_posts() mit und ohne Transient.
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 – „Cron“ (developer.wordpress.org/plugins/cron/), einschließlich „Hooking WP-Cron Into the System Task Scheduler“ (allgemeine Referenz, nicht Zeile für Zeile geprüft)
WordPress Developer Resources – Funktionsreferenzen zu wp_schedule_event(), wp_next_scheduled(), set_transient() (allgemeine Referenz, nicht Zeile für Zeile geprüft)
WP-CLI Handbuch – „wp cron“ (developer.wordpress.org/cli/commands/cron/) (allgemeine Referenz, nicht Zeile für Zeile geprüft)
Hooks: Actions und Filter
Begriffe vorab
Callback: eine Funktion, die WordPress bei einem Hook aufruft.
Priorität: Zahl, die die Reihenfolge mehrerer Callbacks am selben Hook festlegt.
Erweiterungspunkt: ein Hook, den Kern, Theme oder Plugin bewusst bereitstellt.
Hooks sind das zentrale Erweiterungssystem von WordPress. Sie erlauben es, Code an definierten Stellen einzuhängen, ohne Kerndateien zu ändern. Es gibt zwei Arten: Actions und Filter.
Actions
Eine Action führt Code zu einem bestimmten Zeitpunkt aus, liefert aber keinen Rückgabewert. Der Kern löst sie mit do_action() aus, du registrierst dich mit add_action().
add_action( 'save_post', 'meinpraefix_nach_speichern', 20, 2 );
function meinpraefix_nach_speichern( $post_id, $post ) {
// Aktion nach dem Speichern eines Beitrags
}
Die Signatur lautet add_action( $hook, $callback, $priority = 10, $accepted_args = 1 ). Niedrigere Prioritäten laufen zuerst. $accepted_args legt fest, wie viele Argumente der Callback erhält.
Filter
Ein Filter verändert einen Wert und muss ihn immer zurückgeben. Der Kern ruft apply_filters() auf, du nutzt add_filter().
Vergisst ein Filter das return, verschwindet der Wert, etwa der gesamte Beitragsinhalt.
Wichtige Hooks in der Reihenfolge
plugins_loaded: alle aktiven Plugins sind geladen.
after_setup_theme: Theme-Features registrieren.
init: Post Types, Taxonomien und Shortcodes registrieren.
wp_enqueue_scripts: Skripte und Styles im Frontend einbinden.
admin_menu und admin_init: Backend-Menüs und Einstellungen.
template_redirect: letzte Chance vor der Template-Ausgabe.
Hooks entfernen und prüfen
Mit remove_action() und remove_filter() löst du Callbacks wieder. Hook-Name, Callback und Priorität müssen exakt mit der Registrierung übereinstimmen. Mit has_action() und did_action() prüfst du den Zustand.
Eigene Hooks für andere Entwickler erstellst du mit do_action( 'meinpraefix_vor_ausgabe' ) oder apply_filters( 'meinpraefix_titel', $titel ).
Ein Praxisbeispiel
Du möchtest nach jedem Speichern eines Beitrags eine Benachrichtigung auslösen. Dafür reicht eine Action auf save_post. Möchtest du dagegen jeden Titel um ein Präfix ergänzen, nutzt du einen Filter auf the_title und gibst den geänderten Titel zurück. Faustregel: etwas tun = Action, etwas verändern = Filter.
Typische Stolperfallen
Ein Filter ohne return lässt den Wert verschwinden.
Zu spät oder zu früh registrierte Hooks laufen ins Leere – der Zeitpunkt muss zum Ablauf passen.
Dieselbe Funktion mehrfach zu registrieren führt zu doppelter Ausführung.
Zum Entfernen müssen Hook, Callback und Priorität exakt stimmen.
Eigene Hooks anbieten
Wer ein Plugin oder Theme für andere baut, schafft mit do_action() und apply_filters() eigene Erweiterungspunkte. So können Dritte Verhalten anpassen, ohne deinen Code zu verändern. Präfixe für Hook-Namen verhindern Kollisionen.
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 – „Hooks: Actions and Filters“ (developer.wordpress.org/plugins/hooks/) (allgemeine Referenz, nicht Zeile für Zeile geprüft)
WordPress Developer Resources – Funktionsreferenzen zu add_action(), add_filter(), do_action(), apply_filters() (allgemeine Referenz, nicht Zeile für Zeile geprüft)
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:
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)
Klassische Themes und Template-Hierarchie
Begriffe vorab
Template: eine Datei, die festlegt, wie ein bestimmter Seitentyp ausgegeben wird.
Template-Hierarchie: die Suchreihenfolge, nach der WordPress das passende Template wählt.
Child Theme: ein Theme, das von einem Parent Theme erbt und nur Abweichungen enthält.
Ein klassisches Theme besteht aus PHP-Templates. Mindestens nötig sind style.css mit dem Theme-Header und index.php. Die functions.php ist die Stelle für Theme-Setup, Hooks und Enqueues.
WordPress sucht für jede Anfrage das spezifischste vorhandene Template und fällt schrittweise zurück. Beispiel für den Einzelbeitrag eines Post Types book mit dem Slug dune:
single-book-dune.php
single-book.php
single.php
singular.php
index.php
Weitere typische Templates: page.php, front-page.php (Startseite), home.php (Beitragsübersicht), archive.php, category.php, search.php und 404.php.
Ein Child Theme erbt vom Parent, erkennbar am Header Template: parent-ordner. Seine functions.php wird vor der des Parents geladen. Für Pfade des Child Themes nutzt du get_stylesheet_directory_uri(), für den Parent get_template_directory_uri().
Zusätzliche Abfragen in Templates immer mit wp_reset_postdata() abschließen, sonst zeigen spätere Funktionen den falschen Beitrag.
Ein Beispiel für die Suche
Ruft jemand eine Kategorieseite „Rezepte“ auf, sucht WordPress nacheinander category-rezepte.php, category-{ID}.php, category.php, archive.php und zuletzt index.php. Existiert nur index.php, wird diese genutzt. So kann man ein Theme schrittweise verfeinern, ohne alles auf einmal anzulegen.
Warum ein Child Theme
Änderungen direkt am Parent Theme gehen bei dessen Update verloren. Ein Child Theme enthält nur eigene Styles und abweichende Templates, der Rest kommt vom Parent. Es ist der übliche Weg, ein fertiges Theme sicher anzupassen.
Gute Gewohnheiten
Wiederverwendbare Teile mit get_template_part() auslagern.
Skripte und Styles nur über wp_enqueue_* einbinden, nie fest im Header.
Nach eigenen Abfragen wp_reset_postdata() aufrufen.
Zum Selbermachen: die Hierarchie erkunden
Lege in einem Testtheme nur index.php an und rufe verschiedene Seitentypen auf.
Ergänze single.php und beobachte, was sich ändert.
Ergänze category-{slug}.php für eine Kategorie und prüfe, welche Datei genutzt wird.
Notiere die Reihenfolge, in der WordPress sucht.
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 Theme Handbook – „The WordPress Template Hierarchy“ (developer.wordpress.org/themes/basics/template-hierarchy/) (allgemeine Referenz, nicht Zeile für Zeile geprüft)
WordPress Developer Resources – Funktionsreferenz zu wp_reset_postdata() (allgemeine Referenz, nicht Zeile für Zeile geprüft)
Block Themes, theme.json und Site Editor
Begriffe vorab
Block: ein Baustein des Editors, etwa Absatz, Bild oder Überschrift.
Site Editor: Oberfläche, in der man Templates und Gestaltung eines Block Themes visuell bearbeitet.
Preset: eine benannte Vorgabe, zum Beispiel eine Farbe oder Schriftgröße.
Global Styles: Gestaltungseinstellungen, die für die ganze Website gelten.
Block Themes sind der aktuelle Standard. Templates bestehen aus HTML mit Block-Markup und werden im Site Editor bearbeitet. Mindestens nötig sind style.css (Header) und templates/index.html.
Verzeichnisstruktur
theme.json: zentrale Konfiguration für Einstellungen und Styles
templates/: Seitentemplates wie index.html, single.html, page.html, 404.html
parts/: Template Parts wie header.html und footer.html
patterns/: PHP-Dateien mit Header-Kommentar, werden automatisch als Block Patterns registriert
styles/: Stilvariationen als JSON-Dateien
functions.php: optional, für Theme-Support und eigene Registrierungen
Die Template-Hierarchie gilt auch hier, nur mit .html-Dateien statt PHP.
settings legt fest, was im Editor verfügbar ist (Paletten, Schriftgrößen, Layout). styles definiert das Standard-Aussehen. Aus Presets erzeugt WordPress CSS-Variablen nach dem Muster --wp--preset--color--primary. Version 3 von theme.json wird empfohlen, wenn die Mindestversion WordPress 6.6 ist. Ältere Versionen werden weiterhin unterstützt.
Seit WordPress 7.0 lassen sich Pseudo-Klassen wie :hover und :focus für Blöcke wie den Button direkt in theme.json definieren.
Patterns und Block Styles
Patterns sind vorgefertigte Block-Anordnungen. Block Styles (per register_block_style() oder JSON) bieten Variationen eines Blocks, zum Beispiel einen abgerundeten Button.
Halte Designentscheidungen in theme.json statt in eigenem CSS. Nutzer können sie dann im Site Editor über Global Styles anpassen.
Ein Beispiel für den Nutzen von theme.json
Legst du in theme.json eine Palette mit den Hausfarben fest, erscheinen genau diese Farben im Editor, und WordPress erzeugt daraus CSS-Variablen. Ändert jemand später die Primärfarbe in den Global Styles, passt sich jede Stelle an, die darauf verweist. Bei eigenem CSS müsste man jede Stelle einzeln anfassen.
Klassisch oder Block Theme?
Klassische Themes bestehen aus PHP-Templates und bieten viel Programmierfreiheit. Block Themes bestehen aus HTML-Templates und sind für Redakteure im Editor bearbeitbar. Für neue Projekte ist das Block Theme der aktuelle Standard; klassische Themes werden weiter unterstützt und sind bei bestehenden Projekten verbreitet.
Typische Fehler
Designentscheidungen doppelt in theme.json und in eigenem CSS zu pflegen.
Zu viele Farben und Größen freizugeben, sodass das Design uneinheitlich wird.
Änderungen im Site Editor nicht mit der Versionskontrolle abzugleichen – sie liegen in der Datenbank, nicht in den Dateien.
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 Theme Handbook – „Global Settings & Styles (theme.json)“ (developer.wordpress.org/themes/global-settings-and-styles/) (allgemeine Referenz, nicht Zeile für Zeile geprüft)
Make WordPress Core – „Pseudo-element support for blocks and their variations in theme.json“ (Core-Changelog zu WordPress 7.0) (allgemeine Referenz, nicht Zeile für Zeile geprüft)
Plugin-Entwicklung
Begriffe vorab
Plugin: ein Erweiterungspaket, das Funktionen hinzufügt, unabhängig vom Theme.
Präfix: ein eindeutiger Namensanfang für Funktionen und Optionen zur Vermeidung von Konflikten.
Autoload: Optionen, die bei jedem Seitenaufruf automatisch geladen werden.
Ein Plugin ist eine PHP-Datei (oder ein Ordner) in wp-content/plugins/ mit einem Header-Kommentar. Pflichtangabe ist der Plugin Name.
Die ABSPATH-Prüfung verhindert den direkten Aufruf der Datei außerhalb von WordPress.
Aktivierung, Deaktivierung, Deinstallation
register_activation_hook( __FILE__, 'callback' ): einmalige Einrichtung, etwa Tabellen anlegen.
register_deactivation_hook(): Aufräumen, zum Beispiel geplante Events entfernen.
uninstall.php oder register_uninstall_hook(): Daten beim Löschen entfernen.
Präfixe und Struktur
Funktionen, Klassen, Optionen und Hooks bekommen ein eindeutiges Präfix oder einen Namespace, um Konflikte mit anderen Plugins zu vermeiden. Bei größeren Plugins bewähren sich Klassen, eine Ordnerstruktur wie includes/, admin/, assets/ und Autoloading.
Ein Shortcode-Callback muss den Inhalt zurückgeben und darf nicht direkt ausgeben.
Optionen und Settings API
Einstellungen speicherst du mit der Options API (get_option(), update_option()). Für Admin-Seiten nutzt du die Settings API: register_setting(), add_settings_section() und add_settings_field(). Sie übernimmt Nonce-Prüfung, Speicherung und Sanitizing über einen Callback.
Menüseiten legst du mit add_menu_page() oder add_options_page() auf dem Hook admin_menu an.
Bei add_option() und update_option() ist Autoload standardmäßig aktiv. Große Datenmengen sollten mit Autoload false gespeichert werden, sonst wird jede Seite langsamer.
Ein Beispiel: das kleinste sinnvolle Plugin
Eine Datei mit Header-Kommentar, die einen Shortcode registriert, ist bereits ein vollständiges Plugin. Sie liegt in wp-content/plugins/, lässt sich im Backend aktivieren und überlebt Theme-Wechsel. Genau deshalb gehört Funktionalität, die unabhängig vom Aussehen gebraucht wird, in ein Plugin und nicht in die functions.php eines Themes.
Wachsende Plugins strukturieren
Code in Klassen und Ordner wie includes/ und admin/ aufteilen.
Übersetzbare Texte mit Text Domain versehen.
Beim Deinstallieren angelegte Daten aufräumen (uninstall.php).
Sicherheit gleich mitdenken
Jede Eingabe bereinigen, jede Ausgabe escapen, Formulare mit Nonce absichern und Rechte prüfen. Diese Regeln sind in der Sicherheitslektion erklärt und gehören von Anfang an in jedes Plugin, nicht erst nachträglich.
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 – „Plugin Basics“ und „Settings API“ (developer.wordpress.org/plugins/) (allgemeine Referenz, nicht Zeile für Zeile geprüft)
WordPress Developer Resources – Funktionsreferenz zu register_activation_hook() (allgemeine Referenz, nicht Zeile für Zeile geprüft)
Datenmodell: Post Types, Taxonomien, Meta und WP_Query
Begriffe vorab
Post Type: Inhaltstyp, etwa Beitrag, Seite oder ein eigener Typ.
Taxonomie: Ordnungssystem für Inhalte, z. B. Kategorien oder Schlagwörter.
Term: ein einzelner Eintrag einer Taxonomie.
Meta: zusätzliche Felder zu einem Beitrag.
Inhalte in WordPress sind Posts eines bestimmten Post Types. Beiträge, Seiten und Anhänge sind eingebaut, eigene Typen ergänzt du mit register_post_type().
Die Registrierung erfolgt auf init. Der Slug darf höchstens 20 Zeichen lang sein – eine feste Grenze durch die Spalte post_type in der Tabelle wp_posts, die als VARCHAR(20) angelegt ist; eine Überschreitung bricht die Registrierung mit einem Fehler ab. show_in_rest ist nötig, damit der Typ im Block-Editor und in der REST API verfügbar ist. Permalinks aktualisierst du nur einmalig bei Aktivierung mit flush_rewrite_rules(), nicht bei jedem Seitenaufruf.
Taxonomien
Mit register_taxonomy() ordnest du Inhalte. Hierarchische Taxonomien verhalten sich wie Kategorien, nicht hierarchische wie Schlagwörter.
Der dritte Parameter true liefert einen einzelnen Wert statt eines Arrays. Mit register_post_meta() und show_in_rest machst du Meta-Felder für Block-Editor und REST API sichtbar.
Nie query_posts() verwenden, es überschreibt die Main Query.
Die Main Query änderst du mit dem Hook pre_get_posts, wobei $query->is_main_query() geprüft wird.
no_found_rows => true spart die Zählabfrage, wenn keine Paginierung nötig ist.
Für Abfragen mit vielen Meta-Bedingungen lohnt sich oft eine Taxonomie, da diese deutlich besser indexiert wird.
Ein durchgängiges Beispiel
Für ein Rezeptportal legst du den Post Type recipe an, dazu eine hierarchische Taxonomie „Gericht“ (Vorspeise, Hauptgericht …) und Meta-Felder wie Kochzeit. Eine Abfrage mit WP_Query holt dann zum Beispiel alle Hauptgerichte unter 30 Minuten. Die Datenstruktur bleibt dabei sauber getrennt von der Darstellung im Theme.
Wann Meta, wann Taxonomie?
Meta eignet sich für einzelne Werte pro Beitrag (Preis, Kochzeit). Eine Taxonomie lohnt sich, wenn Inhalte nach einem Merkmal gruppiert, gefiltert und aufgelistet werden sollen. Taxonomien sind dafür besser indexiert; Abfragen über viele Meta-Felder werden bei großen Datenmengen langsam.
Gute Praxis bei Abfragen
Die Hauptabfrage über pre_get_posts ändern, nicht mit query_posts().
Nur benötigte Felder und Mengen laden.
Ergebnisse, die selten wechseln, zwischenspeichern.
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 Developer Resources – Funktionsreferenz zu register_post_type() (Longenscheidung des 20-Zeichen-Limits durch die Spalte post_type in wp_posts) (allgemeine Referenz, nicht Zeile für Zeile geprüft)
WordPress Developer Resources – Klassenreferenz zu WP_Query und der Hook pre_get_posts (allgemeine Referenz, nicht Zeile für Zeile geprüft)
Sicherheit: Sanitizing, Escaping, Nonces und Capabilities
Begriffe vorab
Sanitizing: Eingaben bereinigen, bevor sie verarbeitet oder gespeichert werden.
Escaping: Ausgaben so kennzeichnen, dass sie nicht als Code gelesen werden.
XSS: Einschleusen von Skript in Seiten.
CSRF: untergeschobene Anfragen im Namen angemeldeter Nutzer.
SQL-Injection: Einschleusen von Datenbankbefehlen.
Sicherheit folgt in WordPress einem festen Muster: Eingaben validieren und bereinigen, Ausgaben escapen, Aktionen per Nonce absichern und Berechtigungen prüfen.
Sanitizing und Validierung (Eingabe)
Bereinige Daten, bevor du sie speicherst oder verwendest.
sanitize_text_field(): einfacher Text
sanitize_email(), esc_url_raw(), absint()
wp_kses_post(): erlaubt nur sicheres HTML wie in Beiträgen
Escaping (Ausgabe)
Escape so spät wie möglich, direkt bei der Ausgabe, passend zum Kontext.
Typische Capabilities sind manage_options (Einstellungen), edit_posts und edit_post (mit Post-ID, für einen konkreten Beitrag).
Datenbank
Eigene SQL-Abfragen laufen immer über $wpdb->prepare() mit Platzhaltern %s (String), %d (Integer) und %f (Float).
global $wpdb;
$row = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->posts} WHERE ID = %d", $id ) );
Vertraue nie $_GET, $_POST oder $_REQUEST. Bereinige mit wp_unslash() und den passenden Sanitize-Funktionen.
Welche Regel gegen welchen Angriff?
Escaping schützt vor XSS: Ein eingegebenes Skript wird als Text angezeigt statt ausgeführt.
Nonce schützt vor CSRF: Eine Anfrage ohne gültiges Token wird abgewiesen.
Prepared Statements ($wpdb->prepare()) schützen vor SQL-Injection.
Capability-Prüfung verhindert, dass Nutzer Aktionen ausführen, für die ihnen das Recht fehlt.
Ein Beispiel
Ein Formular speichert einen Namen. Ohne Escaping könnte jemand als Namen ein Skript eintragen, das später jedem Besucher ausgeführt wird. Mit esc_html() an der Ausgabestelle wird daraus harmloser Text. Zusätzlich sorgt die Nonce-Prüfung dafür, dass das Formular nicht von einer fremden Seite abgeschickt werden kann.
Nie darauf verlassen, dass Eingaben „schon stimmen“. Alles, was von außen kommt – auch aus Cookies, URLs und REST-Anfragen – gilt als nicht vertrauenswürdig.
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 Developer Resources – „Security“ (Data Validation, Sanitizing, Escaping; developer.wordpress.org/apis/security/) (allgemeine Referenz, nicht Zeile für Zeile geprüft)
WordPress Developer Resources – „Nonces“ (developer.wordpress.org/apis/security/nonces/) (allgemeine Referenz, nicht Zeile für Zeile geprüft)
Gutenberg-Blöcke und REST API
Begriffe vorab
REST API: Schnittstelle, über die Programme Daten per HTTP abrufen und ändern.
Endpunkt (Route): eine konkrete URL der Schnittstelle.
Attribut: ein gespeicherter Wert eines Blocks.
Dynamischer Block: Block, der beim Aufruf serverseitig gerendert wird.
Der Block-Editor (Gutenberg) baut Inhalte aus Blöcken. Eigene Blöcke werden mit JavaScript (React) und PHP entwickelt. Die Entwicklungsumgebung liefert das Paket @wordpress/scripts, gestartet über npm start (Entwicklung) und npm run build (Produktion).
block.json
Die Datei block.json beschreibt einen Block zentral: Name, Titel, Kategorie, Attribute und Assets.
Statische Blöcke speichern ihr fertiges HTML in der Datenbank (Funktion save).
Dynamische Blöcke rendern bei jedem Aufruf serverseitig, per render.php oder render_callback. Ideal für Inhalte, die sich ändern, etwa eine Liste neuer Beiträge.
Interactivity API
Für Frontend-Interaktivität ohne eigenes Framework gibt es die Interactivity API. Sie nutzt HTML-Direktiven wie data-wp-interactive und data-wp-on--click und einen Store im Modul-JavaScript.
REST API
WordPress stellt Inhalte unter /wp-json/ bereit, zum Beispiel /wp-json/wp/v2/posts. Eigene Endpunkte registrierst du auf rest_api_init.
Der permission_callback ist seit WordPress 5.5 (August 2020) Pflicht; fehlt er, meldet WordPress einen _doing_it_wrong()-Hinweis. Für öffentliche Endpunkte gibst du __return_true an, für geschützte prüfst du eine Capability mit current_user_can(). Ohne ihn erzeugt WordPress einen Hinweis.
Definiere Namespace und Version (mein-plugin/v1). So bleibst du bei späteren API-Änderungen abwärtskompatibel.
Statisch oder dynamisch?
Ein statischer Block speichert sein fertiges HTML im Beitrag; er ist schnell, ändert sich aber nicht automatisch. Ein dynamischer Block speichert nur seine Einstellungen und rendert bei jedem Aufruf neu – richtig für Inhalte wie „neueste Beiträge“, die sich ständig ändern.
Ein Beispiel für einen eigenen Endpunkt
Eine mobile App soll eine Liste von Rezepten abrufen. Du registrierst eine Route unter einem eigenen Namespace mit Version, etwa mein-plugin/v1/rezepte, gibst eine Callback-Funktion an und legst fest, wer zugreifen darf. Der Namespace mit Version erlaubt es später, die Schnittstelle zu ändern, ohne bestehende Clients zu brechen.
Gute Praxis
Eingaben einer Route validieren und bereinigen.
Rückgaben schlank halten und nur nötige Felder liefern.
Geschützte Daten nur nach Capability-Prüfung ausgeben.
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 Developer Resources – Funktionsreferenz zu register_rest_route() (permission_callback seit WordPress 5.5 verpflichtend) (allgemeine Referenz, nicht Zeile für Zeile geprüft)
WordPress Block Editor Handbook – „Block API: block.json“ (allgemeine Referenz, nicht Zeile für Zeile geprüft)
Übersetzung, Performance und Debugging
Begriffe vorab
i18n: Internationalisierung – Vorbereitung auf Übersetzung.
Text Domain: Kennung, die Übersetzungen einem Plugin oder Theme zuordnet.
Object Cache: Zwischenspeicher für Abfrageergebnisse im Arbeitsspeicher.
Debug-Log: Datei, in der Fehler protokolliert werden.
Professionelle WordPress-Entwicklung berücksichtigt Mehrsprachigkeit, Geschwindigkeit und saubere Fehlersuche von Anfang an.
Internationalisierung (i18n)
__( 'Text', 'textdomain' ) gibt die Übersetzung zurück.
_e() gibt sie direkt aus.
esc_html__() und esc_attr__() übersetzen und escapen in einem Schritt.
Die Text Domain muss ein fester String sein, keine Variable, und mit dem Slug des Plugins oder Themes übereinstimmen. Für JavaScript nutzt du wp_set_script_translations(). Bei Plugins und Themes aus dem WordPress.org-Verzeichnis werden Übersetzungen automatisch geladen.
Performance
Skripte und Styles nur dort laden, wo sie gebraucht werden, mit Bedingungen wie is_singular( 'book' ).
Teure Ergebnisse mit Transients zwischenspeichern:
Diese Konstanten in wp-config.php schreiben Fehler nach wp-content/debug.log, ohne sie Besuchern anzuzeigen. Nützliche Werkzeuge sind das Plugin Query Monitor, SCRIPT_DEBUG für unminifizierte Core-Skripte und WP-CLI für Automatisierung.
Coding Standards
Die WordPress Coding Standards (WPCS) definieren Einrückung mit Tabs, Yoda-Bedingungen und Namensregeln. Mit PHP_CodeSniffer lassen sie sich automatisch prüfen.
Lasse WP_DEBUG_DISPLAY auf Live-Seiten niemals aktiv. Fehlermeldungen können Pfade und sensible Informationen preisgeben.
Ein Beispiel: langsame Seite analysieren
Eine Seite lädt träge. Mit einem Werkzeug wie Query Monitor siehst du, welche Datenbankabfragen und Hooks lange dauern. Häufig findet man eine Abfrage innerhalb einer Schleife, die hundertfach wiederholt wird. Sie einmalig zu bündeln, kann mehr bringen als jede Serveroptimierung.
Reihenfolge der Maßnahmen
Messen, bevor man optimiert.
Teure Abfragen bündeln oder zwischenspeichern.
Assets nur dort laden, wo sie gebraucht werden.
Bilder passend skalieren und Caching nutzen.
Debugging ohne Risiko
Fehler werden in die Logdatei geschrieben, aber nicht im Frontend angezeigt. Auf Live-Seiten die Anzeige abschalten, weil Fehlermeldungen Pfade und interne Details verraten können.
Zum Selbermachen: eine Seite messen
Installiere Query Monitor in einer Testumgebung.
Rufe eine Seite auf und notiere die Anzahl und Dauer der Datenbankabfragen.
Suche die langsamste Abfrage und finde heraus, welcher Code sie auslöst.
Beseitige eine Ursache und miss erneut.
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 Developer Resources – „Internationalization“ (developer.wordpress.org/apis/internationalization/) (allgemeine Referenz, nicht Zeile für Zeile geprüft)
WordPress Developer Resources – „Debugging in WordPress“ (WP_DEBUG, WP_DEBUG_LOG, WP_DEBUG_DISPLAY) (allgemeine Referenz, nicht Zeile für Zeile geprüft)