Webentwicklung

Akademie

Webentwicklung

Websites bauen und für alle nutzbar machen: WordPress-Entwicklung und digitale Barrierefreiheit.

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

  1. Eine Redakteurin legt im Editor einen Beitrag mit Überschrift, Text und Bild an.
  2. WordPress speichert ihn in der Datenbank.
  3. Ein Besucher ruft die Adresse auf.
  4. WordPress lädt den Beitrag und wählt ein Template des Themes.
  5. 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

  1. Installiere WordPress lokal oder in einer Testumgebung.
  2. Lege eine Seite und einen Beitrag an und sieh dir den Unterschied in der Anzeige an.
  3. 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.

Schritt 1: Menü anlegen

add_action( 'admin_menu', function () {
    add_options_page(
        'Merkzettel', 'Merkzettel',
        'manage_options', 'merkzettel',
        'mz_render_settings'
    );
} );

Schritt 2: Einstellung registrieren

add_action( 'admin_init', function () {
    register_setting( 'mz_group', 'mz_settings', array(
        'type'              => 'array',
        'sanitize_callback' => 'mz_sanitize_settings',
        'default'           => array( 'max_items' => 50 ),
    ) );
    add_settings_section( 'mz_main', 'Allgemein', '__return_false', 'merkzettel' );
    add_settings_field( 'max_items', 'Maximale Einträge', 'mz_field_max', 'merkzettel', 'mz_main' );
} );

function mz_sanitize_settings( $in ) {
    return array( 'max_items' => max( 1, min( 500, absint( $in['max_items'] ?? 50 ) ) ) );
}

function mz_field_max() {
    $o = get_option( 'mz_settings', array( 'max_items' => 50 ) );
    printf( '<input type="number" name="mz_settings[max_items]" value="%d" min="1" max="500">', (int) $o['max_items'] );
}

Schritt 3: Seite ausgeben

function mz_render_settings() {
    if ( ! current_user_can( 'manage_options' ) ) { return; }
    echo '<div class="wrap"><h1>Merkzettel</h1><form method="post" action="options.php">';
    settings_fields( 'mz_group' );
    do_settings_sections( 'merkzettel' );
    submit_button();
    echo '</form></div>';
}

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.

Meta-Box mit sicherem Speichern

add_action( 'add_meta_boxes', function () {
    add_meta_box( 'mz_box', 'Merkzettel', 'mz_box_html', 'post', 'side' );
} );

function mz_box_html( $post ) {
    wp_nonce_field( 'mz_save', 'mz_nonce' );
    $v = get_post_meta( $post->ID, '_mz_note', true );
    printf( '<textarea name="mz_note">%s</textarea>', esc_textarea( $v ) );
}

add_action( 'save_post', function ( $post_id ) {
    if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) { return; }
    if ( ! isset( $_POST['mz_nonce'] ) || ! wp_verify_nonce( $_POST['mz_nonce'], 'mz_save' ) ) { return; }
    if ( ! current_user_can( 'edit_post', $post_id ) ) { return; }
    update_post_meta( $post_id, '_mz_note', sanitize_textarea_field( wp_unslash( $_POST['mz_note'] ?? '' ) ) );
} );

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

  1. Baue die Einstellungsseite nach und speichere einen Wert außerhalb des erlaubten Bereichs – er wird begrenzt.
  2. Füge ein zweites Feld (Checkbox) hinzu und erweitere den Sanitizer.
  3. 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.

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

  1. Lege den Ordner merkzettel mit Hauptdatei und Header an.
  2. Aktiviere das Plugin im Backend und prüfe, dass es in der Liste erscheint.
  3. 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

  1. Lege die Tabelle über die Aktivierung an und prüfe sie in einem Datenbankwerkzeug.
  2. Füge denselben Eintrag zweimal ein und beobachte, dass der eindeutige Schlüssel den Doppeleintrag verhindert.
  3. 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.

add_action( 'wp_enqueue_scripts', function () {
    wp_register_script( 'mz-js', MZ_URL . 'assets/merkzettel.js', array(), MZ_VERSION,
        array( 'in_footer' => true, 'strategy' => 'defer' ) );
} );

add_shortcode( 'merkzettel', function ( $atts ) {
    $atts = shortcode_atts( array( 'titel' => 'Meine Merkliste' ), $atts, 'merkzettel' );
    if ( ! is_user_logged_in() ) {
        return '<p>Bitte anmelden.</p>';
    }
    wp_enqueue_script( 'mz-js' );
    wp_add_inline_script( 'mz-js', 'window.MZ=' . wp_json_encode( array(
        'rest'  => esc_url_raw( rest_url( 'merkzettel/v1/items' ) ),
        'nonce' => wp_create_nonce( 'wp_rest' ),
    ) ) . ';', 'before' );
    return '<div class="mz"><h3>' . esc_html( $atts['titel'] ) . '</h3><ul id="mz-list"></ul></div>';
} );

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.

add_action( 'rest_api_init', function () {
    register_rest_route( 'merkzettel/v1', '/items', array(
        array(
            'methods'             => 'POST',
            'callback'            => 'mz_add_item',
            'permission_callback' => fn() => is_user_logged_in(),
            'args'                => array(
                'post_id' => array( 'required' => true, 'sanitize_callback' => 'absint' ),
            ),
        ),
    ) );
} );

function mz_add_item( WP_REST_Request $req ) {
    $post_id = (int) $req['post_id'];
    if ( ! get_post_status( $post_id ) ) {
        return new WP_Error( 'mz_not_found', 'Beitrag nicht gefunden.', array( 'status' => 404 ) );
    }
    MZ_Store::add( get_current_user_id(), $post_id );
    return rest_ensure_response( array( 'ok' => true ) );
}

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

  1. Baue den Shortcode und prüfe im Seitenquelltext, dass das Skript nur auf Seiten mit Shortcode erscheint.
  2. Rufe die Route ohne Nonce auf und beobachte die Antwort.
  3. 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

  1. Der Webserver übergibt die Anfrage an index.php.
  2. WordPress lädt Konfiguration, aktive Plugins und das Theme.
  3. Aus der URL ermittelt es, welche Inhalte gemeint sind, und fragt die Datenbank.
  4. 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:

  1. PHPCS mit WPCS findet Stilverstöße und typische Sicherheitsmängel (fehlendes Escaping, ungeprüfte Eingaben).
  2. PHPUnit mit der WordPress-Testumgebung prüft Verhalten. Das Gerüst erzeugt WP-CLI mit wp scaffold plugin-tests.
  3. 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

Ein einfacher Test

class Test_Store extends WP_UnitTestCase {
    public function test_add_is_unique_per_user_and_post() {
        $user = self::factory()->user->create();
        $post = self::factory()->post->create();

        $this->assertNotFalse( MZ_Store::add( $user, $post ) );
        $this->assertFalse( @MZ_Store::add( $user, $post ) ); // eindeutiger Schlüssel greift
    }
}

Kompatibilität pflegen

  • 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

  1. Installiere PHPCS mit WPCS und prüfe dein Plugin. Behebe die ersten fünf Meldungen.
  2. Installiere „Plugin Check“ und führe es für dein Plugin aus.
  3. Schreibe eine readme.txt mit Beschreibung, Installation, Änderungsliste und Stable tag.

Eine Checkliste vor jedem Release

  1. Versionsnummer in Header, Konstante und readme.txt angleichen.
  2. PHPCS und Tests laufen lassen, Plugin Check ausführen.
  3. Mit der niedrigsten unterstützten WordPress- und PHP-Version testen.
  4. Änderungsliste ergänzen, Stable tag setzen.
  5. 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)

Warum Barrierefreiheit? Zahlen, Nutzen, Prinzip

Begriffe vorab

  • Barrierefreiheit: die Nutzbarkeit von Angeboten für möglichst alle Menschen, unabhängig von Einschränkungen.
  • Assistive Technologie: Hilfsmittel wie Screenreader oder Spracheingabe.
  • Inklusion: gleichberechtigte Teilhabe aller Menschen.
  • Barriere: alles, was die Nutzung erschwert oder verhindert.

Barrierefreiheit (Accessibility, kurz a11y – 11 Buchstaben zwischen a und y) bedeutet, digitale Angebote so zu gestalten, dass Menschen mit Behinderungen sie genauso nutzen können wie alle anderen.

Wie viele Menschen sind betroffen?

Die Weltgesundheitsorganisation (WHO) geht davon aus, dass weltweit rund 1,3 Milliarden Menschen, also etwa 16 % der Weltbevölkerung, eine signifikante Behinderung haben (WHO, Fact Sheet „Disability“). Dazu zählen dauerhafte Einschränkungen (Blindheit, Gehörlosigkeit, motorische Einschränkungen) ebenso wie situative (ein gebrochener Arm) und temporäre (grelles Sonnenlicht auf dem Display, laute Umgebung ohne Kopfhörer).

Die vier Behinderungsarten im Web-Kontext

  • Visuell: Blindheit, Sehbehinderung, Farbfehlsichtigkeit.
  • Auditiv: Gehörlosigkeit, Schwerhörigkeit.
  • Motorisch: eingeschränkte Feinmotorik, keine Maus nutzbar.
  • Kognitiv: Lese- und Lernschwierigkeiten, Aufmerksamkeitsstörungen, Gedächtnisprobleme.

Der POUR-Grundsatz

Die vier Grundprinzipien der WCAG (siehe nächste Lektion) lassen sich mit dem Akronym POUR merken:

  • Perceivable (wahrnehmbar)
  • Operable (bedienbar)
  • Understandable (verständlich)
  • Robust (robust, technisch stabil)

Der „Curb-Cut-Effekt“: Die abgeschrägte Bordsteinkante wurde für Rollstuhlfahrer eingeführt, hilft aber auch Menschen mit Kinderwagen, Rollkoffer oder Fahrrad. Barrierefreiheit im Web verbessert fast immer auch die Nutzung für alle – etwa Untertitel, die auch ohne Ton verstanden werden, oder klare Struktur, die Suchmaschinen ebenso hilft wie Screenreadern.

Barrieren in der Praxis

Ein Bestellformular ohne sichtbare Beschriftung der Felder, ein Video ohne Untertitel, ein Menü, das sich nur mit der Maus öffnen lässt, ein Text in hellgrauer Schrift auf weißem Grund: Jede dieser Stellen kann für bestimmte Menschen die Nutzung unmöglich machen. Oft entstehen Barrieren nicht aus böser Absicht, sondern weil niemand sie mitgedacht hat.

Wer profitiert?

Nicht nur Menschen mit dauerhaften Einschränkungen. Auch ältere Menschen, Personen mit gebrochenem Arm, Nutzende in greller Sonne oder lauter Umgebung und Menschen, die Deutsch nicht als Erstsprache sprechen, haben Vorteile von klarer Sprache, guten Kontrasten und flexiblen Bedienwegen.

Barrierefreiheit als Prozess

Sie ist kein Zusatz am Ende, sondern gehört in Konzeption, Gestaltung, Entwicklung und Test. Nachträgliche Korrekturen sind meist aufwendiger als frühzeitiges Mitdenken. Ein sinnvoller Einstieg ist, die wichtigsten Abläufe (Bestellen, Kontakt, Anmelden) mit Tastatur und Screenreader durchzugehen und Befunde nach Schwere zu ordnen.

Zum Selbermachen: eine Seite mit anderen Augen

  1. Öffne eine Seite und bediene sie zehn Minuten nur mit der Tastatur.
  2. Stelle den Browser auf 200 Prozent Vergrößerung.
  3. Schalte den Ton aus und prüfe, ob Videos verständlich bleiben.
  4. Notiere die Stellen, an denen du nicht weiterkommst.

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.

Quellen

  • World Health Organization – Fact Sheet „Disability“
  • W3C Web Accessibility Initiative (WAI) – „Accessibility, Usability, and Inclusion“

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

  1. Plane die Aufgabe bei Aktivierung und prüfe sie mit dem Plugin „WP Crontrol“ oder wp cron event list.
  2. Löse sie manuell aus und beobachte das Ergebnis.
  3. 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().

add_filter( 'the_content', 'meinpraefix_hinweis_anhaengen' );
function meinpraefix_hinweis_anhaengen( $content ) {
	if ( is_single() ) {
		$content .= '<p>Danke fürs Lesen!</p>';
	}
	return $content;
}

Vergisst ein Filter das return, verschwindet der Wert, etwa der gesamte Beitragsinhalt.

Wichtige Hooks in der Reihenfolge

  1. plugins_loaded: alle aktiven Plugins sind geladen.
  2. after_setup_theme: Theme-Features registrieren.
  3. init: Post Types, Taxonomien und Shortcodes registrieren.
  4. wp_enqueue_scripts: Skripte und Styles im Frontend einbinden.
  5. admin_menu und admin_init: Backend-Menüs und Einstellungen.
  6. 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)

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

  1. Registriere die Hooks und schreibe bei Aktivierung eine Option.
  2. Aktiviere und deaktiviere das Plugin und prüfe in der Datenbank (wp_options), was bleibt.
  3. 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)

WCAG: Prinzipien, Ebenen und Erfolgskriterien

Begriffe vorab

  • Erfolgskriterium: eine prüfbare Anforderung der WCAG.
  • Konformität: die Erfüllung aller Kriterien einer Stufe.
  • Techniken: Hinweise des W3C, wie Kriterien umgesetzt werden können; sie sind informativ, nicht verbindlich.

Die Web Content Accessibility Guidelines (WCAG) sind der internationale Standard für barrierefreie Webinhalte, herausgegeben vom W3C über die Web Accessibility Initiative (WAI). Aktuelle Fassungen sind WCAG 2.1 (2018) und WCAG 2.2 (2023), die WCAG 2.1 vollständig einschließt und um weitere Erfolgskriterien ergänzt.

Vier Prinzipien, ein Akronym

Jedes Erfolgskriterium gehört zu einem der vier POUR-Prinzipien (siehe vorige Lektion): wahrnehmbar, bedienbar, verständlich, robust.

Drei Konformitätsstufen

  • A – Mindestanforderung, ohne die manche Nutzergruppen vollständig ausgeschlossen wären.
  • AA – der in Gesetzen und Richtlinien übliche Zielstandard (siehe Rechtslektion).
  • AAA – die höchste Stufe; das W3C selbst empfiehlt AAA nicht als pauschales Ziel für ganze Websites, da einige Kriterien für bestimmte Inhalte nicht erreichbar sind.

Aufbau eines Erfolgskriteriums

Jedes Kriterium hat eine Nummer nach dem Schema Prinzip.Richtlinie.Kriterium, zum Beispiel 1.4.3 Kontrast (Minimum) unter Prinzip 1 (Wahrnehmbar), Richtlinie 4 (Unterscheidbar).

Wichtige Kriterien zum Kennenlernen

  • 1.1.1 Nicht-Text-Inhalte (A): Alternativtext für Bilder.
  • 1.4.3 Kontrast Minimum (AA): 4,5:1 für normalen, 3:1 für großen Text.
  • 2.1.1 Tastatur (A): Alle Funktionen ohne Maus erreichbar.
  • 2.4.7 Fokus sichtbar (AA): Der Tastaturfokus muss erkennbar sein.
  • 3.3.2 Beschriftungen/Anweisungen (A): Formularfelder brauchen erkennbare Labels.
  • 4.1.2 Name, Rolle, Wert (A): Interaktive Elemente müssen für assistive Technologien identifizierbar sein.

Was ist neu in WCAG 2.2?

WCAG 2.2 ergänzt unter anderem Kriterien zu größeren Klickzielen (2.5.8 Zielgröße, Minimum: mindestens 24 × 24 CSS-Pixel, mit Ausnahmen), zu einem Tastaturfokus, der nicht vollständig von anderen Inhalten verdeckt werden darf (2.4.11 Fokus nicht verdeckt, Minimum, Stufe AA), sowie zu erleichtertem Ausfüllen von Formularen (3.3.7 Redundante Eingaben, 3.3.8 Barrierefreie Authentifizierung). Das zuvor enthaltene Kriterium 4.1.1 (Parsing) wurde als veraltet entfernt, da moderne Browser fehlerhaftes HTML ohnehin robust behandeln.

WCAG-Versionsnummern und einzelne Kriterien ändern sich. Für eine verbindliche Prüfung immer die aktuelle Fassung auf w3.org/WAI/standards-guidelines/wcag/ konsultieren.

Ein Beispiel: ein Kriterium durchgehen

Kriterium 1.1.1 (Nicht-Text-Inhalte) verlangt Textalternativen. Ein Diagramm mit Umsatzzahlen braucht eine Beschreibung, die die Kernaussage wiedergibt, etwa „Umsatz stieg von 2 Mio. € auf 3 Mio. €“. Ein rein dekoratives Bild bekommt einen leeren Alternativtext, damit Screenreader es überspringen. Das Kriterium nennt also nicht einfach „Alt-Text setzen“, sondern verlangt einen zum Zweck passenden Ersatz.

Konformität ist Seitenbezogen

Konformität gilt für vollständige Seiten, nicht für einzelne Elemente; alle Inhalte einer Seite müssen die Stufe erfüllen, auch eingebundene Komponenten. Bei Abläufen wie einer Bestellung müssen alle Schritte konform sein, sonst gilt der Ablauf nicht als konform.

Wie man WCAG im Projekt nutzt

  • Zielstufe (meist AA) und Version (z. B. 2.1 oder 2.2) festlegen.
  • Kriterien in Designvorgaben und Abnahmekriterien übersetzen.
  • Bei Änderungen der Version prüfen, welche neuen Kriterien hinzukommen.

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.

Quellen

  • W3C – Web Content Accessibility Guidelines (WCAG) 2.1 und 2.2, Empfehlungen der W3C Web Accessibility Initiative
  • W3C WAI – „Understanding Conformance“ (Erläuterung der Stufen A/AA/AAA) (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.

/*
Theme Name: Mein Theme
Version: 1.0.0
Text Domain: mein-theme
*/

Template-Hierarchie

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:

  1. single-book-dune.php
  2. single-book.php
  3. single.php
  4. singular.php
  5. index.php

Weitere typische Templates: page.php, front-page.php (Startseite), home.php (Beitragsübersicht), archive.php, category.php, search.php und 404.php.

The Loop

<?php if ( have_posts() ) : while ( have_posts() ) : the_post(); ?>
	<h2><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h2>
	<?php the_excerpt(); ?>
<?php endwhile; endif; ?>

Funktionen mit the_ geben direkt aus, Funktionen mit get_ liefern den Wert zurück.

Template-Bausteine

  • get_header(), get_footer(), get_sidebar()
  • get_template_part( 'template-parts/card', 'post' ) für wiederverwendbare Teile
  • wp_head() und wp_footer() müssen vorhanden sein, damit Plugins und Kern Styles und Skripte ausgeben können.

Skripte und Styles

add_action( 'wp_enqueue_scripts', 'meinpraefix_assets' );
function meinpraefix_assets() {
	wp_enqueue_style( 'mein-theme', get_stylesheet_uri(), array(), '1.0.0' );
	wp_enqueue_script( 'mein-theme-js', get_template_directory_uri() . '/js/app.js', array(), '1.0.0', array( 'in_footer' => true, 'strategy' => 'defer' ) );
}

Child Themes

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

  1. Lege in einem Testtheme nur index.php an und rufe verschiedene Seitentypen auf.
  2. Ergänze single.php und beobachte, was sich ändert.
  3. Ergänze category-{slug}.php für eine Kategorie und prüfe, welche Datei genutzt wird.
  4. 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)

Rechtliche Grundlagen: EAA, BFSG, BITV 2.0 und internationale Regeln

Begriffe vorab

  • Richtlinie: ein EU-Rechtsakt, der von den Mitgliedstaaten in nationales Recht umgesetzt werden muss.
  • Verordnung: gilt in den Mitgliedstaaten unmittelbar.
  • Erklärung zur Barrierefreiheit: öffentliche Angabe zu Stand und bekannten Mängeln.

Barrierefreiheit ist in vielen Ländern nicht nur guter Stil, sondern Pflicht. Die folgenden Regelwerke sind im deutschsprachigen Raum und international am relevantesten.

EU: European Accessibility Act (EAA)

Die Richtlinie (EU) 2019/882 verpflichtet private Anbieter bestimmter Produkte und Dienstleistungen (u. a. E-Commerce, Bankdienstleistungen, E-Books, Personenverkehr) zur Barrierefreiheit. Die Mitgliedstaaten mussten sie in nationales Recht umsetzen; die Anwendungspflicht für Unternehmen gilt ab dem 28. Juni 2025. Kleinstunternehmen (weniger als 10 Beschäftigte und höchstens 2 Mio. € Jahresumsatz oder Jahresbilanzsumme) sind bei Dienstleistungen von der Pflicht ausgenommen; für Hersteller und Händler von Produkten gilt diese Ausnahme nicht.

Deutschland: BFSG

Das Barrierefreiheitsstärkungsgesetz (BFSG) setzt die EAA in Deutschland um und gilt seit dem 28. Juni 2025 für die betroffenen privaten Anbieter. Es ergänzt die bereits bestehende BITV 2.0 (Barrierefreie-Informationstechnik-Verordnung), die öffentliche Stellen des Bundes zur Barrierefreiheit ihrer Websites und Apps verpflichtet, inklusive einer öffentlich zugänglichen Erklärung zur Barrierefreiheit.

Der technische Unterbau: EN 301 549

Die europäische Norm EN 301 549 definiert die technischen Anforderungen an barrierefreie IKT-Produkte und -Dienstleistungen. Für Webinhalte verweist sie inhaltlich auf die WCAG. BITV 2.0 und viele nationale Umsetzungen der EAA/EU-Richtlinien stützen sich auf diese Norm.

International

  • USA – Section 508 des Rehabilitation Act verpflichtet Bundesbehörden zu barrierefreier IKT.
  • USA – Americans with Disabilities Act (ADA): Gerichte wenden Titel III zunehmend auch auf Websites privater Unternehmen an; das US-Justizministerium hat zudem eine Regel erlassen, die für Webangebote staatlicher und kommunaler Stellen (Titel II) WCAG 2.1 AA als Maßstab festlegt.
  • Weltweit: Viele Länder orientieren sich an WCAG, auch außerhalb konkreter Gesetzespflichten, unter anderem weil sich internationale Anbieter ohnehin daran ausrichten.

Gesetze, Fristen und Anwendungsbereiche ändern sich und unterscheiden sich je nach Branche und Unternehmensgröße. Diese Lektion ist eine Orientierung, keine Rechtsberatung – im Zweifel die Originaltexte oder eine Rechtsberatung konsultieren.

Ein Beispiel: Online-Shop

Ein Online-Shop für Verbraucher fällt grundsätzlich in den Anwendungsbereich des BFSG, sofern das Unternehmen nicht als Kleinstunternehmen ausgenommen ist. Die Website samt Bestellprozess, Zahlung und Kundenbereich sollte dann barrierefrei nutzbar sein. Eine rein informative Unternehmensseite ohne Verkauf ist dagegen nicht ohne Weiteres erfasst; hier entscheidet der Einzelfall.

Wer prüft und was droht?

Zuständige Marktüberwachungsbehörden können Nachbesserung verlangen und bei Verstößen Sanktionen verhängen. Zusätzlich können Betroffene und Verbände Ansprüche geltend machen. Wer betroffen sein könnte, sollte frühzeitig den Stand der eigenen Angebote prüfen und Maßnahmen planen.

Unterschied zwischen öffentlichem und privatem Sektor

Für öffentliche Stellen gelten BITV 2.0 und die Web-Accessibility-Richtlinie mit Erklärungspflicht; für private Anbieter bestimmter Produkte und Dienstleistungen gilt das BFSG. Die technische Grundlage ist in beiden Fällen die Norm EN 301 549 bzw. in der Praxis die WCAG.

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.

Quellen

  • Richtlinie (EU) 2019/882 (European Accessibility Act)
  • Barrierefreiheitsstärkungsgesetz (BFSG), Deutschland
  • Barrierefreie-Informationstechnik-Verordnung (BITV 2.0), Deutschland
  • ETSI/EN 301 549 – „Accessibility requirements for ICT products and services“
  • U.S. Access Board – Section 508 Standards; U.S. Department of Justice – ADA Title II Rule (allgemeine Referenz, nicht Zeile für Zeile geprüft)

Assistive Technologien und Screenreader

Begriffe vorab

  • Braillezeile: ein Gerät, das Bildschirmtext in tastbarer Brailleschrift ausgibt.
  • Bildschirmlupe: vergrößert Bildschirmbereiche.
  • Sprachsteuerung: Bedienung per gesprochenen Befehlen.
  • Rotor / Elementliste: Screenreader-Funktion, die Überschriften, Links oder Formularfelder auflistet.

Assistive Technologien sind Hilfsmittel, die Menschen mit Behinderungen die Nutzung digitaler Inhalte ermöglichen. Wer für Barrierefreiheit entwickelt, sollte die wichtigsten Werkzeuge zumindest einmal ausprobiert haben.

Screenreader

Screenreader lesen Bildschirminhalte vor oder geben sie in Braille-Schrift aus. Verbreitete Programme:

  • NVDA (NonVisual Desktop Access) – kostenlos, Windows.
  • JAWS – kommerziell, Windows, in Unternehmen und Behörden verbreitet.
  • VoiceOver – in macOS und iOS integriert.
  • TalkBack – in Android integriert.

Der jährliche WebAIM Screen Reader Survey erhebt, welche Programme Nutzerinnen und Nutzer bevorzugen und in Kombination mit welchen Browsern; die Ergebnisse verschieben sich leicht von Jahr zu Jahr, weshalb die aktuelle Erhebung die verlässlichste Quelle ist.

Weitere Hilfsmittel

  • Vergrößerungssoftware und Betriebssystem-Zoom für Sehbehinderungen.
  • Schalter- und Augensteuerung für Menschen ohne Feinmotorik in Händen.
  • Spracheingabe (z. B. Diktierfunktionen) als Alternative zur Tastatur.
  • Browser-Erweiterungen für Lesehilfen, etwa angepasste Schriftarten oder Vorlesefunktionen.

Wie Screenreader Webseiten „sehen“

Screenreader nutzen nicht das visuelle Layout, sondern den Accessibility Tree, eine aus dem DOM abgeleitete Struktur mit Rollen, Namen und Zuständen jedes Elements. Fehlt einem Button ein zugänglicher Name, meldet der Screenreader nur „Schaltfläche“ ohne weitere Information.

Der beste erste Test: Maus beiseitelegen, Bildschirm ausschalten oder abdecken, und versuchen, die eigene Website allein mit Tastatur und Screenreader zu bedienen. Vieles fällt dabei sofort auf, was am Bildschirm unsichtbar bleibt.

Wie Screenreader-Nutzende navigieren

Sie lesen nicht Zeile für Zeile, sondern springen gezielt: von Überschrift zu Überschrift, von Link zu Link, zu Formularfeldern oder zu Seitenbereichen (Landmarks). Deshalb sind korrekte Überschriftenhierarchie, aussagekräftige Linktexte und Landmarks entscheidend. Ein Link „hier klicken“ sagt in einer Linkliste nichts; „Preisliste herunterladen“ dagegen schon.

Eine Seite aus Sicht eines Screenreaders

Der Screenreader liest: „Schaltfläche“ – ohne Namen, weil ein Icon-Button kein Textlabel hat. Mit einem zugänglichen Namen wie „Warenkorb öffnen“ wird daraus „Warenkorb öffnen, Schaltfläche“. Kleine Ergänzungen entscheiden, ob ein Element nutzbar ist.

Selbst ausprobieren

  • Den eingebauten Screenreader des Betriebssystems aktivieren (VoiceOver, TalkBack) oder NVDA unter Windows.
  • Eine eigene Seite mit Tastatur und ausgeschaltetem Bildschirm bedienen.
  • Auf Stellen achten, an denen nichts oder Unverständliches angesagt wird.

Zum Selbermachen: ein Screenreader-Kurztest

  1. Aktiviere VoiceOver (macOS/iOS) oder TalkBack (Android).
  2. Rufe eine eigene Seite auf und navigiere nur über Überschriften.
  3. Gehe ein Formular durch und achte darauf, ob jedes Feld verständlich benannt ist.
  4. Notiere Stellen, an denen Unverständliches oder nichts angesagt 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.

Quellen

  • WebAIM – „Screen Reader User Survey“ (jährliche Erhebung) (allgemeine Referenz, nicht Zeile für Zeile geprüft)
  • W3C WAI – „Accessible Rich Internet Applications (WAI-ARIA)“, Abschnitt zum Accessibility Tree (allgemeine Referenz, nicht Zeile für Zeile geprüft)
  • NV Access – Dokumentation zu NVDA; Apple – VoiceOver-Dokumentation (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.

Block-Markup

<!-- wp:template-part {"slug":"header"} /-->
<!-- wp:group {"tagName":"main"} -->
<main class="wp-block-group">
	<!-- wp:post-title /-->
	<!-- wp:post-content /-->
</main>
<!-- /wp:group -->
<!-- wp:template-part {"slug":"footer"} /-->

theme.json

{
  "$schema": "https://schemas.wp.org/trunk/theme.json",
  "version": 3,
  "settings": {
    "color": { "palette": [ { "slug": "primary", "color": "#21759B", "name": "Primär" } ] },
    "layout": { "contentSize": "720px", "wideSize": "1200px" }
  },
  "styles": {
    "color": { "background": "#ffffff" },
    "elements": { "link": { "color": { "text": "var(--wp--preset--color--primary)" } } }
  }
}

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.

<?php
/**
 * Plugin Name: Mein Plugin
 * Description: Kurze Beschreibung.
 * Version: 1.0.0
 * Requires at least: 6.5
 * Requires PHP: 8.0
 * Text Domain: mein-plugin
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

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.

Shortcodes

add_shortcode( 'meinpraefix_hinweis', 'meinpraefix_hinweis_cb' );
function meinpraefix_hinweis_cb( $atts, $content = null ) {
	$atts = shortcode_atts( array( 'typ' => 'info' ), $atts );
	return '<div class="hinweis ' . esc_attr( $atts['typ'] ) . '">' . esc_html( $content ) . '</div>';
}

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)

Tastaturbedienung und Fokus-Management

Begriffe vorab

  • Fokus: das Element, das gerade per Tastatur bedient werden kann.
  • Fokusreihenfolge: die Reihenfolge, in der Elemente per Tab angesprungen werden.
  • Fokus-Falle: ein Bereich, aus dem man per Tastatur nicht herauskommt.

Nicht jede Nutzerin bedient eine Website mit der Maus: Menschen mit motorischen Einschränkungen, Screenreader-Nutzende und Power-User arbeiten häufig ausschließlich mit der Tastatur.

Die Grundregel (WCAG 2.1.1)

Jede Funktion muss über die Tastatur erreichbar sein, meist mit Tab (vorwärts), Umschalt+Tab (rückwärts), Enter/Leertaste (aktivieren) und den Pfeiltasten (innerhalb von Komponenten wie Menüs oder Tabs).

Keine Tastatur-Fallen

WCAG 2.1.2 verlangt, dass der Fokus eine Komponente immer wieder verlassen kann. Ein schlecht gebautes Modal, aus dem Tab nicht mehr herausführt, ist eine klassische Tastatur-Falle.

Sichtbarer Fokus (2.4.7 / 2.4.11)

Der Browser zeigt den Fokus standardmäßig mit einem Umriss (outline) an. Ein verbreiteter, aber problematischer Fehler ist *:focus { outline: none; } ohne Ersatz – damit verschwindet jede Orientierung für Tastaturnutzende. Seit WCAG 2.2 verlangt Kriterium 2.4.11 zusätzlich, dass der Fokusindikator nicht durch andere Inhalte verdeckt wird, etwa durch eine fixierte Kopfzeile.

Logische Reihenfolge

Die Tab-Reihenfolge sollte der visuellen und inhaltlichen Reihenfolge entsprechen (WCAG 2.4.3). Positives tabindex (z. B. tabindex="5") erzwingt eine eigene Reihenfolge und sollte vermieden werden; tabindex="0" reiht ein Element sinnvoll in die natürliche Reihenfolge ein, tabindex="-1" macht es nur programmatisch fokussierbar.

Skip-Links

Ein „Zum Inhalt springen“-Link am Anfang der Seite erlaubt es, die Navigation zu überspringen (WCAG 2.4.1 Bypass Blocks). Er ist oft visuell versteckt und erscheint erst beim Fokussieren.

<a class="skip-link" href="#main">Zum Inhalt springen</a>
<main id="main">…</main>

Schneller Praxistest: gesamte Seite nur mit Tab, Umschalt+Tab und Enter durchgehen. Jede Stelle, an der man nicht mehr weiterkommt oder den Fokus nicht mehr sieht, ist ein Fund.

Ein Beispiel: Dialog richtig bauen

Öffnet sich ein Dialog, muss der Fokus hineinwandern, und Tab darf nur innerhalb des Dialogs wechseln, solange er offen ist. Mit Escape lässt er sich schließen, danach kehrt der Fokus zum auslösenden Element zurück. Fehlt das, verliert man als Tastaturnutzer die Orientierung oder bleibt ungewollt hinter dem Dialog hängen.

Eigene Bedienelemente

Baut man statt eines echten Buttons ein anderes Element nach, muss man Fokussierbarkeit, Enter- und Leertastenbedienung, Rolle und Fokusanzeige selbst ergänzen. Natives HTML bringt all das mit – ein Grund mehr, es zu bevorzugen.

Typische Fehler

  • Den Fokusrahmen per CSS ohne Ersatz entfernen.
  • Elemente mit positivem tabindex in eine eigene Reihenfolge zwingen.
  • Menüs, die nur bei Mausberührung aufklappen.
  • Fixierte Kopfzeilen, die den fokussierten Inhalt verdecken.

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.

Quellen

  • WCAG 2.2 – Erfolgskriterien 2.1.1, 2.1.2, 2.4.1, 2.4.3, 2.4.7, 2.4.11
  • W3C WAI-ARIA Authoring Practices Guide (APG) – Abschnitt „Keyboard Interface“ (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().

Custom Post Types

add_action( 'init', 'meinpraefix_register_book' );
function meinpraefix_register_book() {
	register_post_type( 'book', array(
		'labels'       => array( 'name' => __( 'Bücher', 'mein-plugin' ) ),
		'public'       => true,
		'has_archive'  => true,
		'show_in_rest' => true,
		'supports'     => array( 'title', 'editor', 'thumbnail', 'custom-fields' ),
	) );
}

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.

Post Meta

update_post_meta( $post_id, 'isbn', sanitize_text_field( $isbn ) );
$isbn = get_post_meta( $post_id, 'isbn', true );

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.

WP_Query

$query = new WP_Query( array(
	'post_type'      => 'book',
	'posts_per_page' => 6,
	'meta_key'       => 'isbn',
	'orderby'        => 'date',
	'no_found_rows'  => true,
) );
if ( $query->have_posts() ) {
	while ( $query->have_posts() ) {
		$query->the_post();
		the_title();
	}
	wp_reset_postdata();
}
  • 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)

Farben, Kontraste und Wahrnehmbarkeit

Begriffe vorab

  • Kontrastverhältnis: Verhältnis der Helligkeiten von Vorder- und Hintergrundfarbe, von 1:1 (kein Kontrast) bis 21:1.
  • Großer Text: nach WCAG ab etwa 18 pt bzw. 14 pt fett.
  • Nicht-Text-Kontrast: Mindestkontrast für Bedienelemente und wichtige Grafiken (3:1).

Von angeborener Rot-Grün-Sehschwäche sind in Bevölkerungen nordeuropäischer Herkunft bis zu etwa 8 % der Männer und 0,5 % der Frauen betroffen; in anderen Bevölkerungen sind die Anteile geringer. Kontrast und Farbgestaltung betreffen aber auch Sehbehinderungen allgemein und die Nutzung bei grellem Licht.

Kontrastanforderungen nach WCAG 1.4.3 und 1.4.6

  • AA, normaler Text: mindestens 4,5:1 Kontrastverhältnis zum Hintergrund.
  • AA, großer Text (ab ca. 18pt bzw. 14pt fett): mindestens 3:1.
  • AAA, normaler Text: mindestens 7:1; AAA, großer Text: mindestens 4,5:1.

Reiner Dekor-Text und rein dekorative Bilder sind von der Kontrastanforderung ausgenommen.

Farbe niemals als einziges Merkmal (WCAG 1.4.1)

Ein Pflichtfeld nur rot zu umrahmen oder einen Link nur farblich (ohne Unterstreichung oder anderes Merkmal) vom Fließtext abzuheben, schließt Menschen mit Farbfehlsichtigkeit aus. Zusätzliche Merkmale wie Symbole, Text („* Pflichtfeld“) oder Unterstreichung sind nötig.

Kontrast praktisch prüfen

Werkzeuge wie der WebAIM Contrast Checker oder die Kontrastprüfung in Browser-Entwicklertools berechnen aus zwei Farbwerten automatisch das Kontrastverhältnis und zeigen, welche WCAG-Stufe erreicht wird.

Weitere Wahrnehmbarkeits-Kriterien

  • 1.4.4 Textgröße ändern: Text muss sich bis 200 % vergrößern lassen, ohne Inhalt oder Funktion zu verlieren.
  • 1.4.10 Reflow: Inhalte müssen bis 400 % Zoom ohne horizontales Scrollen lesbar bleiben (mit definierten Ausnahmen wie Tabellen und Karten).
  • 1.4.12 Textabstand: Nutzende dürfen Zeilenhöhe und Buchstabenabstand selbst vergrößern können, ohne dass Inhalte abgeschnitten werden.

Farbkontrast wird oft erst spät im Projekt geprüft, wenn das Designsystem schon feststeht. Kontraste gehören in die Farbpalette selbst, nicht nur in eine spätere Korrekturschleife.

Ein Beispiel: Grau auf Weiß

Ein mittleres Grau wie #999999 auf Weiß erreicht nur etwa 2,8:1 und besteht WCAG AA für normalen Text nicht. Ein dunkleres Grau wie #767676 erreicht gerade 4,5:1. Der Unterschied ist für viele Menschen mit normaler Sicht kaum auffällig, für Menschen mit Sehbehinderung oder bei Sonnenlicht aber entscheidend.

Auch Bedienelemente brauchen Kontrast

WCAG 2.1 verlangt zusätzlich mindestens 3:1 für Ränder von Eingabefeldern, Symbole und Zustände wie „ausgewählt“. Ein hellgrauer Feldrand auf weißem Grund kann das Formular unsichtbar machen.

Ein Ablauf für Designsysteme

  1. Kontrast bereits beim Festlegen der Farbpalette prüfen.
  2. Erlaubte Kombinationen dokumentieren (Text auf Hintergrund).
  3. Helle und dunkle Darstellung getrennt prüfen.
  4. Zustände wie Hover, Fokus und deaktiviert mitprüfen.

Zum Selbermachen: Kontraste prüfen

  1. Wähle fünf Farbkombinationen deiner Website (Fließtext, Links, Buttons, Platzhalter, Fehlermeldung).
  2. Prüfe sie mit einem Kontrastrechner.
  3. Notiere, welche unter 4,5:1 (Text) bzw. 3:1 (Bedienelemente) liegen.
  4. Passe die Palette an und teste in hellem und dunklem Modus 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. Die Kontrastwerte der Beispiele (#999999 ≈ 2,85:1, #767676 ≈ 4,54:1 auf Weiß) habe ich nachgerechnet.

Quellen

  • WCAG 2.2 – Erfolgskriterien 1.4.1, 1.4.3, 1.4.4, 1.4.6, 1.4.10, 1.4.12
  • WebAIM – „Contrast Checker“ und „Visual Disabilities: Color Blindness“ (allgemeine Referenz, nicht Zeile für Zeile geprüft)

Semantisches HTML und WAI-ARIA

Begriffe vorab

  • Semantik: Bedeutung von Elementen, nicht nur ihr Aussehen.
  • Rolle: Art des Elements (Button, Link, Tab).
  • Zustand: veränderliche Eigenschaft wie „aufgeklappt“.
  • Zugänglicher Name: der Name, den assistive Technik für ein Element ausgibt.

Die wirkungsvollste Barrierefreiheits-Maßnahme ist oft die unspektakulärste: korrektes, semantisches HTML.

Die erste ARIA-Regel

Der WAI-ARIA Authoring Practices Guide formuliert es als „No ARIA is better than Bad ARIA“ und als erste Regel: Wenn ein natives HTML-Element oder -Attribut mit der benötigten Semantik existiert, sollte es verwendet werden, statt ein generisches Element mit ARIA nachzubauen.

<!-- Gut -->
<button>Absenden</button>

<!-- Unnötig aufwendig und fehleranfällig -->
<div role="button" tabindex="0" onclick="..." onkeydown="...">Absenden</div>

Ein natives <button> ist automatisch per Tastatur fokussierbar und auslösbar und wird von Screenreadern korrekt als Schaltfläche angesagt. Der div muss all das händisch nachbilden und vergisst dabei leicht Details wie Leertaste als Auslöser.

Landmark-Rollen und Strukturelemente

HTML5-Elemente wie <header>, <nav>, <main>, <aside> und <footer> erzeugen automatisch ARIA-Landmarks, über die Screenreader-Nutzende direkt zwischen Seitenbereichen springen können. Überschriften (<h1> bis <h6>) müssen eine sinnvolle, nicht rein optisch motivierte Hierarchie bilden – sie sind die wichtigste Navigationshilfe für Screenreader-Nutzende.

Wann ARIA sinnvoll ergänzt

Für Komponenten, die HTML nicht nativ abdeckt (Tabs, Akkordeons, komplexe Comboboxen), definiert WAI-ARIA Rollen, Zustände und Eigenschaften:

  • role – z. B. role="tablist", role="dialog".
  • aria-expanded – zeigt an, ob ein Element auf- oder zugeklappt ist.
  • aria-label / aria-labelledby – liefert einen zugänglichen Namen, wenn kein sichtbarer Text ausreicht.
  • aria-live – meldet dynamische Inhaltsänderungen (z. B. eine Statusmeldung), ohne dass der Fokus wechseln muss.

Der WAI-ARIA Authoring Practices Guide (APG) liefert für Standardmuster wie Tabs, Akkordeons und Dialoge fertige Referenzimplementierungen inklusive Tastaturverhalten.

ARIA-Attribute ändern nur, was assistive Technologien ansagen – sie ändern nichts am tatsächlichen Verhalten. aria-hidden="true" auf einem fokussierbaren Element versteckt es zwar vor Screenreadern, per Tastatur bleibt es aber trotzdem erreichbar. Beides muss zusammenpassen.

Ein Beispiel: Akkordeon

Ein Akkordeon besteht aus einer Schaltfläche, die einen Bereich auf- und zuklappt. Mit einem echten <button>, dem Attribut aria-expanded (wahr/falsch) und aria-controls, das auf den Bereich verweist, erfährt ein Screenreader-Nutzer: „Versand, Schaltfläche, eingeklappt“. Das Zustandsattribut muss beim Klick mit dem tatsächlichen Zustand aktualisiert werden, sonst sagt die Seite etwas Falsches an.

Der Namensbaustein

Ein zugänglicher Name entsteht in dieser Reihenfolge aus sichtbarem Text, aria-labelledby, aria-label oder dem Label. Sichtbarer Text ist der beste Name, weil er mit dem übereinstimmt, was alle sehen; Sprachsteuerungsnutzer sprechen, was sie sehen.

Heading- und Landmark-Gerüst

  • Genau eine h1 je Seite, darunter logisch gestaffelte Überschriften.
  • Jeder Hauptbereich in ein passendes Landmark (main, nav, footer).
  • Mehrere Navigationen durch Namen unterscheiden.

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.

Quellen

  • W3C – WAI-ARIA 1.2 Spezifikation und „WAI-ARIA Authoring Practices Guide (APG)“ (allgemeine Referenz, nicht Zeile für Zeile geprüft)
  • WCAG 2.2 – Erfolgskriterium 4.1.2 „Name, Rolle, Wert“
  • MDN Web Docs – „ARIA“ und „HTML-Elemente nach Kategorie“ (Referenz zu semantischem HTML) (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.

echo '<a href="' . esc_url( $url ) . '" title="' . esc_attr( $titel ) . '">' . esc_html( $text ) . '</a>';
  • esc_html() für Text im HTML
  • esc_attr() für Attributwerte
  • esc_url() für URLs
  • esc_js() für Inline-JavaScript-Strings

Nonces

Nonces schützen vor Cross-Site-Request-Forgery (CSRF), indem sie bestätigen, dass eine Anfrage aus deinem Formular oder Link stammt.

// Formular
wp_nonce_field( 'meinpraefix_speichern', 'meinpraefix_nonce' );

// Verarbeitung
if ( ! isset( $_POST['meinpraefix_nonce'] ) || ! wp_verify_nonce( $_POST['meinpraefix_nonce'], 'meinpraefix_speichern' ) ) {
	wp_die( 'Ungültige Anfrage' );
}

Ein Nonce ist keine Berechtigungsprüfung. Er beweist die Herkunft, nicht das Recht des Nutzers.

Capabilities

if ( ! current_user_can( 'manage_options' ) ) {
	wp_die( 'Keine Berechtigung' );
}

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)

Barrierefreie Formulare und Fehlermeldungen

Begriffe vorab

  • Label: die sichtbare Beschriftung eines Formularfelds.
  • Fehlermeldung: Hinweis, was falsch ist und wie es behoben wird.
  • Autovervollständigung: Vorschläge des Browsers anhand gespeicherter Daten.

Formulare sind eine der fehleranfälligsten Stellen für Barrierefreiheit, weil hier Eingabe, Beschriftung, Fehlerprüfung und Fokus zusammenspielen müssen.

Jedes Feld braucht ein echtes Label

<label for="email">E-Mail-Adresse</label>
<input type="email" id="email" name="email" required>

Ein <label> mit passendem for/id-Paar verknüpft Beschriftung und Feld fest. Ein bloßer Platzhaltertext (placeholder) ist kein Ersatz: Er verschwindet bei der Eingabe, hat oft zu wenig Kontrast und wird von manchen Screenreadern nicht zuverlässig vorgelesen (WCAG 3.3.2, 1.3.1).

Fehler klar benennen

WCAG 3.3.1 verlangt, dass Fehler in Text identifiziert und beschrieben werden, nicht nur farblich markiert. Eine gute Fehlermeldung nennt das betroffene Feld und erklärt, was zu tun ist:

<p id="email-error" role="alert">E-Mail-Adresse: Bitte ein gültiges Format wie name@beispiel.de eingeben.</p>
<input type="email" id="email" aria-describedby="email-error" aria-invalid="true">

aria-describedby verknüpft die Fehlermeldung mit dem Feld, aria-invalid="true" signalisiert den fehlerhaften Zustand, und role="alert" (bzw. eine aria-live-Region) sorgt dafür, dass Screenreader die neue Meldung automatisch ansagen.

Gruppierung und Pflichtfelder

<fieldset> mit <legend> gruppiert zusammengehörige Felder, etwa eine Reihe von Radiobuttons, und gibt der Gruppe eine gemeinsame Beschriftung. Pflichtfelder sollten nicht nur farblich, sondern auch textlich oder mit einem Symbol plus Erläuterung gekennzeichnet sein (siehe Lektion zu Farben).

Ausfüllhilfen (WCAG 2.2, Kriterium 1.3.5 und 3.3.7/3.3.8)

WCAG empfiehlt, den Zweck von Eingabefeldern zusätzlich maschinenlesbar zu machen, etwa über autocomplete-Attribute (autocomplete="email", autocomplete="tel"), damit Browser und Hilfsmittel beim Ausfüllen unterstützen können. Neuere WCAG-2.2-Kriterien verlangen außerdem, dass bereits bekannte Informationen nicht erneut abgefragt werden müssen und dass Authentifizierung nicht allein auf einem Gedächtnis- oder Rechentest beruht, wenn es zugänglichere Alternativen gibt.

Praxistest: das komplette Formular nur mit der Tastatur ausfüllen und bewusst einen Fehler erzeugen (z. B. ungültige E-Mail). Wird die Fehlermeldung vom Screenreader angesagt, und ist danach klar, welches Feld korrigiert werden muss?

Ein Beispiel für eine gute Fehlerbehandlung

Nach dem Absenden erscheint oben eine Zusammenfassung: „2 Fehler: E-Mail-Adresse fehlt, Postleitzahl ungültig.“ Die Einträge sind Links zu den Feldern. Direkt am Feld steht eine verständliche Meldung („Bitte eine fünfstellige Postleitzahl eingeben“). Der Fokus springt zur Zusammenfassung, die Screenreader automatisch ansagen. So erfährt jede Person, was los ist und wo.

Verständlich statt technisch

Meldungen wie „Fehler 27“ oder „Ungültige Eingabe“ helfen nicht. Besser: sagen, was erwartet wird, möglichst mit Beispiel. Die Anweisung vor dem Feld zu nennen („Format: TT.MM.JJJJ“) ist hilfreicher, als erst danach zu beanstanden.

Weitere Stolpersteine

  • Automatisches Weiterspringen zwischen Feldern.
  • Zeitlimits ohne Verlängerungsmöglichkeit.
  • CAPTCHAs ohne zugängliche Alternative.
  • Pflichtangaben nur farblich gekennzeichnet.

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.

Quellen

  • WCAG 2.2 – Erfolgskriterien 1.3.1, 1.3.5, 3.3.1, 3.3.2, 3.3.7, 3.3.8
  • W3C WAI-ARIA APG – Muster „Form“ und „Alert“ (allgemeine Referenz, nicht Zeile für Zeile geprüft)
  • WHATWG HTML Living Standard – Abschnitt zu autocomplete

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.

{
  "$schema": "https://schemas.wp.org/trunk/block.json",
  "apiVersion": 3,
  "name": "mein-plugin/hinweis",
  "title": "Hinweis",
  "category": "text",
  "attributes": { "text": { "type": "string" } },
  "editorScript": "file:./index.js",
  "style": "file:./style-index.css",
  "render": "file:./render.php"
}

Die Registrierung erfolgt in PHP:

add_action( 'init', function () {
	register_block_type( __DIR__ . '/build/hinweis' );
} );

Statische und dynamische Blöcke

  • 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.

add_action( 'rest_api_init', function () {
	register_rest_route( 'mein-plugin/v1', '/buecher', array(
		'methods'             => 'GET',
		'callback'            => 'meinpraefix_buecher_cb',
		'permission_callback' => '__return_true',
	) );
} );

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)

Testen: automatisiert, manuell und mit echten Nutzenden

Begriffe vorab

  • Audit: eine strukturierte Prüfung gegen festgelegte Kriterien.
  • Regressionstest: wiederholte Prüfung, ob Änderungen alte Probleme zurückbringen.
  • Nutzertest: Test mit echten Nutzenden, hier auch mit Menschen mit Behinderungen.

Barrierefreiheit lässt sich nicht allein durch ein Tool „grün“ testen. Verlässliche Prüfungen kombinieren drei Ebenen.

1. Automatisierte Tests

Werkzeuge wie axe DevTools (Deque Systems), WAVE (WebAIM) und die Accessibility-Prüfung in Lighthouse (Google) finden automatisch einen Teil der WCAG-Verstöße, etwa fehlende Alternativtexte, fehlende Formular-Labels oder zu geringen Kontrast.

Automatisierte Tools finden nur einen Teil der Probleme. Belegt sind zwei Messungen: In einem Test des britischen Government Digital Service fand das beste von 13 Werkzeugen 40 % der 142 bekannten Barrieren einer Testseite; Deque ermittelte in über 2.000 Audits, dass automatisierte Tests etwa 57 % der gefundenen Probleme nach Anzahl erfassten (nach Anzahl, weil sich häufige Fehler wie fehlende Alternativtexte oder zu geringer Kontrast leicht automatisch finden lassen). Sinnvolle Lesereihenfolge, ob ein Alternativtext inhaltlich passt oder ob eine Tastaturbedienung sich wirklich sinnvoll anfühlt, kann kein automatisches Werkzeug abschließend beurteilen.

2. Manuelle Expertenprüfung

Dazu gehören die Praxistests aus den vorigen Lektionen: reine Tastaturbedienung, Test mit einem echten Screenreader (NVDA oder VoiceOver reichen für den Einstieg), Kontrastprüfung und Zoom-Test bis 400 %. Eine strukturierte Grundlage bietet die WCAG-EM (Website Accessibility Conformance Evaluation Methodology) des W3C, die beschreibt, wie eine repräsentative Stichprobe von Seiten systematisch bewertet wird.

3. Tests mit Menschen mit Behinderungen

Die verlässlichste, aber aufwendigste Ebene: echte Nutzende mit unterschiedlichen Behinderungen und ihren gewohnten Hilfsmitteln testen lassen. Nur so zeigen sich Probleme, die weder automatisierte Tools noch Experten-Heuristiken zuverlässig aufdecken.

Der WebAIM Million

WebAIM analysiert jährlich automatisiert die Startseiten der eine Million meistbesuchten Websites („WebAIM Million“). Im Bericht 2025 hatten 94,8 % der untersuchten Startseiten automatisiert erkennbare WCAG-2-Verstöße (2024: 95,9 %), im Durchschnitt 51 Fehler je Seite. Am häufigsten waren zu geringer Textkontrast (79,1 %), fehlende Alternativtexte (55,5 %), fehlende Formularbeschriftungen (48,2 %), leere Links (45,4 %), leere Buttons (29,5 %) und fehlende Sprachangabe (15,8 %). Die Zahlen ändern sich jährlich; maßgeblich ist der jeweils aktuelle Bericht.

Barrierefreiheitserklärung

Öffentliche Stellen in der EU müssen nach der Web-Accessibility-Richtlinie (EU) 2016/2102 (in Deutschland über BITV 2.0 umgesetzt) eine öffentlich zugängliche Erklärung zur Barrierefreiheit veröffentlichen, die den Grad der Konformität, bekannte Ausnahmen und einen Kontaktweg für Feedback nennt.

Ein praktikabler Einstieg in ein bestehendes Projekt: automatisierte Prüfung als Basis, danach die wichtigsten Nutzerwege (Login, Bestellung, Kontaktformular) manuell mit Tastatur und Screenreader durchgehen, priorisiert nach Schweregrad und Häufigkeit der Nutzung beheben.

Ein Prüfplan für ein kleines Projekt

  1. Automatische Prüfung der wichtigsten Vorlagen (Start, Formular, Übersicht).
  2. Tastaturtest der zentralen Abläufe.
  3. Screenreader-Kurztest mit einem Bildschirmleseprogramm.
  4. Zoom- und Kontrasttest bis 400 % und in hellem wie dunklem Modus.
  5. Dokumentation: Befunde mit Schweregrad, betroffener Seite und Lösungsvorschlag.

Barrierefreiheit im Entwicklungsablauf

Automatische Prüfungen lassen sich in die laufende Entwicklung einbinden und verhindern, dass bekannte Fehler wiederkehren. Sie ersetzen manuelle Tests nicht, fangen aber viele einfache Fehler früh ab.

Befunde priorisieren

  • Kritisch: Ein Ablauf lässt sich für eine Gruppe gar nicht durchführen.
  • Hoch: erhebliche Erschwernis in wichtigen Abläufen.
  • Mittel/Gering: Komfort- und Randprobleme.

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.

Quellen

  • W3C WAI – „Website Accessibility Conformance Evaluation Methodology (WCAG-EM)“ (allgemeine Referenz, nicht Zeile für Zeile geprüft)
  • WebAIM – „The WebAIM Million“ (jährlicher Bericht)
  • UK Government Digital Service – Vergleichstest von 13 automatisierten Prüfwerkzeugen; Deque – „The Automated Accessibility Coverage Report“
  • Deque Systems – axe-core-Dokumentation; WebAIM – WAVE-Dokumentation; Google – Lighthouse-Dokumentation (allgemeine Referenz, nicht Zeile für Zeile geprüft)
  • Richtlinie (EU) 2016/2102 über den barrierefreien Zugang zu Websites öffentlicher Stellen

Ü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.
  • _n( 'Ein Beitrag', '%d Beiträge', $anzahl, 'textdomain' ) behandelt Singular und Plural.

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:
$daten = get_transient( 'meinpraefix_api_daten' );
if ( false === $daten ) {
	$daten = meinpraefix_teure_abfrage();
	set_transient( 'meinpraefix_api_daten', $daten, HOUR_IN_SECONDS );
}
  • Mit einem persistenten Object Cache (etwa Redis) werden Transients und wp_cache_get() deutlich schneller.
  • Keine Datenbankabfragen innerhalb von Schleifen, stattdessen Daten gebündelt laden.
  • Bilder in passender Größe ausliefern, WordPress erzeugt dafür mehrere Bildgrößen und srcset.

Debugging

define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', true );
define( 'WP_DEBUG_DISPLAY', false );

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

  1. Messen, bevor man optimiert.
  2. Teure Abfragen bündeln oder zwischenspeichern.
  3. Assets nur dort laden, wo sie gebraucht werden.
  4. 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

  1. Installiere Query Monitor in einer Testumgebung.
  2. Rufe eine Seite auf und notiere die Anzahl und Dauer der Datenbankabfragen.
  3. Suche die langsamste Abfrage und finde heraus, welcher Code sie auslöst.
  4. 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)