Einstellungsseiten mit der Settings API und register_setting

Management Summary
Einstellungsseiten ermöglichen in WordPress Änderungen an Erweiterungen, ohne deren Programmdateien zu bearbeiten. Der Artikel erklärt das an einer Hinweisleiste mit Nachricht, Position und Ein-Aus-Schalter. WordPress stellt dafür fertige Bausteine für Formulare und gespeicherte Werte bereit. Das erleichtert eine einheitliche Verwaltung. Gleichzeitig muss das Speichern verlässlich bleiben: Nur berechtigte Personen dürfen Änderungen vornehmen, und eine Auswahl darf andere Einstellungen nicht versehentlich löschen.
Inhalt
- Welche Funktion welche Aufgabe übernimmt
- Zusammengehörige Werte bündeln, Formulare getrennt halten
- Autoload: Wann WordPress eine Option vorab lädt
- Das Plugin und seine Datenprüfung
- Warum Registrierung und Formularaufbau getrennt sind
- Was beim Formularspeichern passiert
- Welche Regeln für /wp/v2/settings gelten
- REST-Schaltfläche ohne veralteten Formularstand
- Prüfumfang und Grenzen
- Fragen und Antworten
- Quellen
Eine Plugin-Einstellungsseite braucht Formularfelder, eine Rechteprüfung und einen sicheren Speicherweg. Die Settings API von WordPress stellt dafür Bausteine bereit. Das folgende Beispiel verwaltet eine Hinweisleiste mit drei Werten: eingeschaltet oder ausgeschaltet, Text und Position auf der Seite.
Die drei Werte liegen zusammen in der Option lwsd_settings. Eine Option ist ein benannter Eintrag in der WordPress-Tabelle wp_options. Das Plugin bietet ein normales Formular und eine zweite Schaltfläche, die über die REST-API arbeitet. Diese Schnittstelle liest und schreibt Einstellungen per HTTP-Anfrage. Die Erklärung bezieht sich auf WordPress 7.1; das ursprüngliche Beispiel wurde mit 7.1.2 und PHP 8.4 getestet.
Welche Funktion welche Aufgabe übernimmt
WordPress führt Funktionen an festgelegten Stellen aus, den Hooks. admin_menu ist beispielsweise der passende Zeitpunkt zum Hinzufügen eines Menüpunkts. Die folgende Aufteilung trennt sichtbare Felder von der Registrierung und Speicherung der Daten.
| Funktion | Ausführungszeitpunkt | Aufgabe |
|---|---|---|
add_options_page() | admin_menu | Menüeintrag und nötiges Benutzerrecht |
register_setting() | init | Registriert Option, Bereinigung, Standardwert und REST-Regeln |
add_settings_section(), add_settings_field() | admin_init | Überschriften und Felder im Admin-Formular |
settings_fields(), do_settings_sections() | Im Seiten-Callback | Versteckte Prüffelder sowie sichtbare Abschnitte und Felder |
Eine Capability ist ein einzelnes Benutzerrecht. Hier heißt das nötige Recht manage_options. add_options_page() verwendet dieses Recht für Menü und Seitenzugriff. Der Seiten-Callback, also die Funktion für die Ausgabe, prüft es ebenfalls mit current_user_can(). Beim Speichern prüft options.php das Recht erneut.
Drei Namen haben unterschiedliche Aufgaben: lwsd_settings ist die gespeicherte Option. lwsd_notice_bar ist die Optionsgruppe für das Formular. lwsd-notice-bar bezeichnet die Admin-Seite. Der Gruppenname in register_setting() muss zu settings_fields() passen; der Seitenname verbindet Abschnitte und Felder mit deren Ausgabe.
Zusammengehörige Werte bündeln, Formulare getrennt halten
Ein Array bündelt mehrere benannte Werte. Dadurch liegen Schalter, Text und Position in einer Datenbankzeile. Bei einer Änderung wird diese Zeile gemeinsam geschrieben. Das ist keine zusätzliche Transaktion über beliebige Lese- und Schreibschritte und verhindert keine Konflikte zwischen gleichzeitig arbeitenden Browsern.
Wichtig ist eine eigene Optionsgruppe für unabhängig gespeicherte Formulare. options.php verarbeitet alle Optionen der übermittelten Gruppe. Fehlt eine davon im Formular, wird null an deren Speicherfunktion übergeben. Beispiel: Formular A sendet nur Option A, beide Optionen gehören aber zur gleichen Gruppe. Dann kann der Bereinigungs-Callback von Option B deren Werte zurücksetzen. Eine Array-Option je Formular allein verhindert das nicht; auch die Gruppen müssen getrennt sein.
Das Array wird automatisch serialisiert, also als zusammengehörige Zeichenfolge gespeichert. Die einzelnen Werte stehen dadurch nicht als eigene SQL-Spalten mit passenden Indizes bereit. Für häufige Datenbanksuchen nach einzelnen Eigenschaften sind andere Datenmodelle geeigneter. WordPress-Datenbank: Leistungsgrenzen von wp_postmeta und der Aufbau eigener Tabellen.
Autoload: Wann WordPress eine Option vorab lädt
Autoload bedeutet, dass WordPress eine Option zusammen mit anderen Optionen früh lädt, bevor ein einzelner Aufruf den Wert anfordert. Für die kleine Hinweisleisten-Konfiguration ist das sinnvoll, weil jede normale Frontend-Seite die Werte benötigt. Bei nur selten benötigten Admin-Einstellungen wäre false meist passender.
Bei add_option() setzt true ausdrücklich den Datenbankwert on, false setzt off. Der Standard null überlässt die Entscheidung seit WordPress 6.6 den automatischen Regeln. Daraus können auto, auto-on oder auto-off entstehen. Ohne ausdrückliche Entscheidung verhindert WordPress standardmäßig Autoload, wenn der serialisierte Wert größer als 150.000 Bytes ist. Filter können die Grenze oder Entscheidung verändern; ein ausdrückliches true wird nicht durch diese Größenregel aufgehoben.
Die Aktivierung legt eine noch fehlende Option mit add_option( …, true ) an. Eine bereits vorhandene Option wird dabei nicht geändert. options.php übergibt später keinen eigenen Autoload-Wert. Beim tatsächlichen Ändern einer vorhandenen Option bleiben ausdrückliche on/off-Werte erhalten; automatische Werte können neu bewertet werden.
Für eine gezielte Änderung ohne neuen Optionswert gibt es seit WordPress 6.4 wp_set_option_autoload(). update_option() beendet den Vorgang dagegen früh, wenn sich der Wert nicht ändert. Die alten API-Angaben yes/no sind seit 6.7 veraltet, werden aber aus Kompatibilitätsgründen weiterhin verstanden. Autoload in WordPress: Werte, Abfragen und Speicherbedarf.
Das Plugin und seine Datenprüfung
Die Hauptdatei liegt beispielsweise unter lw-settings-demo/lw-settings-demo.php. Das weiter unten gezeigte JavaScript liegt daneben unter js/settings-rest.js. Die Vorsilbe lwsd_ hält die eigenen Namen von Funktionen und anderen Plugin-Namen auseinander.
Der Bereinigungs-Callback ist die Funktion, die einen zulässigen gespeicherten Wert erzeugt. Hier entfernt die Funktion HTML aus dem Text, begrenzt den Text auf 200 Zeichen und erlaubt nur top oder bottom als Position. Eine leere Nachricht schaltet die Leiste aus. Nur true, die Zahl 1 und der Formularwert "1" schalten ein; die Zeichenfolge "false" schaltet nicht versehentlich ein.
/**
* Plugin Name: LW Settings Demo
* Description: Notice bar with a settings page built on the Settings API, also available via /wp/v2/settings.
* Version: 1.0.0
* Requires at least: 6.6
* Requires PHP: 7.4
* Author: Lukas Wojcik
* License: GPL-2.0-or-later
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
* Text Domain: lw-settings-demo
*
* @package LW_Settings_Demo
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
const LWSD_OPTION = 'lwsd_settings'; // Row in wp_options.
const LWSD_GROUP = 'lwsd_notice_bar'; // Option group, sent as option_page.
const LWSD_PAGE = 'lwsd-notice-bar'; // Menu slug and settings page ID.
/**
* Default values of the bundled option.
*
* @return array
*/
function lwsd_default_settings() {
return array(
'enabled' => false,
'message' => '',
'position' => 'bottom',
);
}
/**
* Returns the stored settings merged with the defaults.
*
* @return array
*/
function lwsd_get_settings() {
$settings = get_option( LWSD_OPTION );
if ( ! is_array( $settings ) ) {
$settings = array();
}
return wp_parse_args( $settings, lwsd_default_settings() );
}
/**
* Registers the option on init, so admin screens and REST requests both know it.
*/
function lwsd_register_setting() {
register_setting(
LWSD_GROUP,
LWSD_OPTION,
array(
'type' => 'object',
'label' => __( 'Notice bar', 'lw-settings-demo' ),
'description' => __( 'Visibility, text and position of the notice bar.', 'lw-settings-demo' ),
'sanitize_callback' => 'lwsd_sanitize_settings',
'default' => lwsd_default_settings(),
'show_in_rest' => array(
'schema' => array(
'type' => 'object',
'properties' => array(
'enabled' => array(
'type' => 'boolean',
),
'message' => array(
'type' => 'string',
'maxLength' => 200,
),
'position' => array(
'type' => 'string',
'enum' => array( 'top', 'bottom' ),
),
),
),
),
)
);
}
add_action( 'init', 'lwsd_register_setting' );
/**
* Sanitizes the whole settings array.
*
* Runs for options.php and for REST writes, because both end in update_option().
* Repeated sanitizing must leave the result unchanged; a new row can cause two passes.
*
* @param mixed $input Raw value.
* @return array
*/
function lwsd_sanitize_settings( $input ) {
$defaults = lwsd_default_settings();
if ( ! is_array( $input ) ) {
return $defaults;
}
$message = isset( $input['message'] ) ? sanitize_text_field( $input['message'] ) : '';
$position = isset( $input['position'] ) ? sanitize_key( $input['position'] ) : '';
$output = array(
'enabled' => in_array( $input['enabled'] ?? false, array( true, 1, '1' ), true ),
'message' => mb_substr( $message, 0, 200 ),
'position' => in_array( $position, array( 'top', 'bottom' ), true ) ? $position : $defaults['position'],
);
if ( $output['enabled'] && '' === $output['message'] ) {
$output['enabled'] = false;
// add_settings_error() lives in wp-admin/includes/template.php,
// which a REST request does not load.
if ( function_exists( 'add_settings_error' ) ) {
add_settings_error(
LWSD_OPTION,
'lwsd_empty_message',
__( 'The notice bar stays off until a message is entered.', 'lw-settings-demo' ),
'warning'
);
}
}
return $output;
}
/**
* Adds the page below Settings and loads its script only there.
*/
function lwsd_add_settings_page() {
$hook_suffix = add_options_page(
__( 'Notice bar', 'lw-settings-demo' ),
__( 'Notice bar', 'lw-settings-demo' ),
'manage_options',
LWSD_PAGE,
'lwsd_render_settings_page'
);
if ( $hook_suffix ) {
add_action( 'load-' . $hook_suffix, 'lwsd_load_settings_page' );
}
}
add_action( 'admin_menu', 'lwsd_add_settings_page' );
/**
* Runs only when the settings page is requested.
*/
function lwsd_load_settings_page() {
add_action( 'admin_enqueue_scripts', 'lwsd_enqueue_admin_script' );
}
/**
* Enqueues the REST demo script.
*/
function lwsd_enqueue_admin_script() {
wp_enqueue_script(
'lwsd-settings-rest',
plugins_url( 'js/settings-rest.js', __FILE__ ),
array( 'wp-api-fetch', 'wp-i18n' ),
'1.0.0',
array( 'in_footer' => true )
);
wp_set_script_translations( 'lwsd-settings-rest', 'lw-settings-demo' );
}
/**
* Declares section and fields. Only needed on admin screens.
*/
function lwsd_register_fields() {
add_settings_section(
'lwsd_section_main',
__( 'Display', 'lw-settings-demo' ),
'lwsd_render_section',
LWSD_PAGE
);
add_settings_field(
'lwsd_enabled',
__( 'Show notice bar', 'lw-settings-demo' ),
'lwsd_render_enabled_field',
LWSD_PAGE,
'lwsd_section_main',
array( 'label_for' => 'lwsd_enabled' )
);
add_settings_field(
'lwsd_message',
__( 'Message', 'lw-settings-demo' ),
'lwsd_render_message_field',
LWSD_PAGE,
'lwsd_section_main',
array( 'label_for' => 'lwsd_message' )
);
add_settings_field(
'lwsd_position',
__( 'Position', 'lw-settings-demo' ),
'lwsd_render_position_field',
LWSD_PAGE,
'lwsd_section_main',
array( 'label_for' => 'lwsd_position' )
);
}
add_action( 'admin_init', 'lwsd_register_fields' );
/**
* Section intro.
*/
function lwsd_render_section() {
echo '<p>' . esc_html__( 'All three values are stored together in one option.', 'lw-settings-demo' ) . '</p>';
}
/**
* Checkbox field.
*/
function lwsd_render_enabled_field() {
$settings = lwsd_get_settings();
printf(
'<input type="checkbox" id="lwsd_enabled" name="%1$s[enabled]" value="1" %2$s />',
esc_attr( LWSD_OPTION ),
checked( $settings['enabled'], true, false )
);
}
/**
* Text field.
*/
function lwsd_render_message_field() {
$settings = lwsd_get_settings();
printf(
'<input type="text" class="regular-text" id="lwsd_message" name="%1$s[message]" value="%2$s" maxlength="200" />',
esc_attr( LWSD_OPTION ),
esc_attr( $settings['message'] )
);
}
/**
* Select field.
*/
function lwsd_render_position_field() {
$settings = lwsd_get_settings();
$choices = array(
'top' => __( 'Top of the page', 'lw-settings-demo' ),
'bottom' => __( 'Bottom of the page', 'lw-settings-demo' ),
);
printf( '<select id="lwsd_position" name="%s[position]">', esc_attr( LWSD_OPTION ) );
foreach ( $choices as $value => $label ) {
printf(
'<option value="%1$s" %2$s>%3$s</option>',
esc_attr( $value ),
selected( $settings['position'], $value, false ),
esc_html( $label )
);
}
echo '</select>';
}
/**
* Settings page. The form posts to options.php, not to this page.
*/
function lwsd_render_settings_page() {
if ( ! current_user_can( 'manage_options' ) ) {
return;
}
<div class="wrap">
<h1> echo esc_html( get_admin_page_title() ); </h1>
<form id="lwsd-settings-form" action="options.php" method="post">
settings_fields( LWSD_GROUP );
do_settings_sections( LWSD_PAGE );
submit_button();
</form>
<p>
<button type="button" class="button" id="lwsd-rest-toggle"> esc_html_e( 'Toggle via REST API', 'lw-settings-demo' ); </button>
<span id="lwsd-rest-status" aria-live="polite"></span>
</p>
</div>
}
/**
* Prints the notice bar on the front end.
*
* @param string $position Where the calling hook prints: top or bottom.
*/
function lwsd_print_notice( $position ) {
$settings = lwsd_get_settings();
if ( ! $settings['enabled'] || $position !== $settings['position'] || '' === $settings['message'] ) {
return;
}
printf(
'<div class="lwsd-notice lwsd-notice-%1$s" role="status">%2$s</div>',
esc_attr( $position ),
esc_html( $settings['message'] )
);
}
/**
* Top position, needs a theme that calls wp_body_open().
*/
function lwsd_print_top_notice() {
lwsd_print_notice( 'top' );
}
add_action( 'wp_body_open', 'lwsd_print_top_notice' );
/**
* Bottom position.
*/
function lwsd_print_bottom_notice() {
lwsd_print_notice( 'bottom' );
}
add_action( 'wp_footer', 'lwsd_print_bottom_notice' );
/**
* Creates the option with an explicit autoload value.
*/
function lwsd_activate() {
// Read on every front-end request, so autoload it on purpose.
add_option( LWSD_OPTION, lwsd_default_settings(), '', true );
}
register_activation_hook( __FILE__, 'lwsd_activate' );Warum Registrierung und Formularaufbau getrennt sind
register_setting() läuft hier an init. Dieser Hook wird im Admin-Bereich, bei normalen Seitenaufrufen und bei REST-Anfragen ausgeführt. Eine ausschließliche Registrierung an admin_init würde für das Admin-Formular reichen, aber nicht für die normale REST-Anfrage. Auch der registrierte Standardwert soll im Frontend verfügbar sein. Abschnitte und Formularfelder bleiben an admin_init, weil diese Elemente nur im Admin-Bereich gebraucht werden.
Formular und REST-Speicherweg erreichen beide update_option() und damit den registrierten Bereinigungs-Callback. Das REST-Schema prüft zusätzlich die Schnittstellenanfrage. Eine REST-Nachricht mit mehr als 200 Zeichen wird beispielsweise abgelehnt; der Bereinigungs-Callback begrenzt einen über das Formular eingereichten längeren Text dagegen auf 200 Zeichen. Das HTML-Attribut maxlength allein wäre keine serverseitige Prüfung.
Die Bereinigung muss bei Wiederholung denselben Wert ergeben. Bei einer fehlenden Option können update_option() und anschließend add_option() beide bereinigen. Entspricht der neue Wert schon dem registrierten Standardwert, kann der erste Aufruf jedoch früh enden, ohne eine Zeile anzulegen. Zwei Durchläufe sind deshalb möglich, aber nicht bei jedem ersten Speichern garantiert.
add_settings_error() ist bei einer gewöhnlichen REST-Anfrage nicht geladen. Die Prüfung mit function_exists() verhindert einen Aufruf einer fehlenden Funktion. Auf einer Seite unter „Einstellungen“ gibt WordPress Meldungen bereits über options-head.php aus. Ein weiterer Aufruf von settings_errors() im Seiten-Callback würde Meldungen doppeln. Eine Warnung aus der Bereinigung kann die allgemeine Erfolgsmeldung ersetzen, obwohl andere Werte gespeichert wurden.
Was beim Formularspeichern passiert
Das Formular sendet an options.php. settings_fields() ergänzt vier versteckte Felder: Optionsgruppe, Aktion update, Nonce und Rücksprungadresse. Eine Nonce ist hier ein zeitlich begrenzter Prüfwert, der an Aktion und angemeldete Sitzung gebunden ist. Dieser Wert hilft gegen fremd ausgelöste Anfragen, ersetzt aber weder Anmeldung noch Benutzerrecht.
options.php prüft zuerst das Benutzerrecht, dann die Nonce und die zugelassene Optionsgruppe. Danach liest WordPress die Werte der registrierten Optionen, entfernt transportbedingte Escape-Zeichen und ruft update_option() auf. Nach Bereinigung und vorgeschalteten Filtern wird nur ein tatsächlich geänderter Wert geschrieben. Meldungen werden kurz zwischengespeichert; anschließend führt eine Weiterleitung zurück zur Einstellungsseite.
Menü, Seiten-Callback und Speicherweg müssen passende Rechte verlangen. Ein Menü mit edit_pages reicht nicht aus, wenn options.php weiterhin manage_options verlangt. Dafür gibt es den Filter option_page_capability_lwsd_notice_bar. Eine Änderung dieses Formularrechts ändert jedoch nicht automatisch das Recht des REST-Endpunkts. Sanitizing, Escaping, Nonces, Capabilities im Plugin-Review.
Welche Regeln für /wp/v2/settings gelten
show_in_rest macht eine registrierte Option für diesen REST-Endpunkt sichtbar. Für ein Objekt beschreibt properties die erlaubten Eigenschaften, bei einem Array beschreibt items dessen Elemente. Das Schema ist also ein Regelwerk für Typen und zulässige Werte. Ohne eigene abweichende Regel ergänzt der Controller bei Objekten additionalProperties: false; unbekannte Eigenschaften sind dann nicht zulässig.
Lesen und Schreiben verlangen im Standardcontroller manage_options. Der Antwortschlüssel entspricht dem Optionsnamen, sofern show_in_rest.name keinen anderen Namen festlegt. Für öffentlich verfügbare Werte wäre eine eigene Route mit bewusst begrenzter Ausgabe und eigener Berechtigungsfunktion nötig.
Eine fehlende ganze Option in der Anfrage bleibt unverändert. Ein übermitteltes Objekt wird dagegen nicht automatisch mit seinem alten Wert zusammengeführt. Fehlt im Beispiel innerhalb von lwsd_settings die Eigenschaft enabled, ergänzt die Bereinigung false; eine fehlende Position wird bottom. Eine Teilanfrage kann also andere Eigenschaften zurücksetzen.
Ein ungültiger gespeicherter Wert wird bei GET als null ausgegeben. Ein möglicher Grund ist eine alte, im Schema nicht mehr erlaubte Eigenschaft. Eine POST-Anfrage mit null löscht eine gültige Option und lässt danach den Standardwert greifen. Bei einem ungültigen gespeicherten Wert lehnt der Controller diese Löschung mit rest_invalid_stored_value und Status 500 ab. Eine Migration oder ein passend korrigiertes Schema muss den Widerspruch auflösen.
REST-Schaltfläche ohne veralteten Formularstand
Das Skript wird nur auf der eigenen Einstellungsseite geladen. Die Abhängigkeit wp-api-fetch stellt dort unter anderem die REST-Adresse und Nonce-Unterstützung bereit. Die Anmeldung entsteht dadurch nicht neu: Erforderlich bleiben gültige Login-Cookies, die passende REST-Nonce und das Benutzerrecht.
Die Schaltfläche liest das gespeicherte Objekt, kehrt den Schalter um und sendet das vollständige Objekt. Bei noch ungespeicherten Formularänderungen beginnt keine Anfrage. Während der Anfrage bleiben die Formularelemente gesperrt. Nach Erfolg übernimmt das Formular die tatsächlich vom Server zurückgegebenen Werte. Das verhindert, dass ein anschließend gespeichertes, veraltetes Formular den Schalter sofort wieder zurücksetzt.
Ist nach dem Absenden unklar, ob der Server gespeichert hat, bleibt das Formular bis zum Neuladen gesperrt. Eine verlorene Antwort beweist keinen fehlgeschlagenen Schreibvorgang. Das Lesen und anschließende Schreiben des ganzen Objekts schützt außerdem nicht vor gleichzeitigen Änderungen in einem anderen Browser. Für solche Fälle ist eine serverseitige Konfliktprüfung nötig. Beispiel: Der Browser hat Version 4 gelesen, inzwischen liegt Version 5 vor. Der Server lehnt den alten Änderungsversuch ab, statt die neueren Werte zu überschreiben.
/* global wp */
( function () {
'use strict';
const { __ } = wp.i18n;
const form = document.getElementById( 'lwsd-settings-form' );
const button = document.getElementById( 'lwsd-rest-toggle' );
const status = document.getElementById( 'lwsd-rest-status' );
const enabled = document.getElementById( 'lwsd_enabled' );
const message = document.getElementById( 'lwsd_message' );
const position = document.getElementById( 'lwsd_position' );
if ( ! form || ! button || ! status || ! enabled || ! message || ! position ) {
return;
}
const readForm = () => JSON.stringify( [ enabled.checked, message.value, position.value ] );
let baseline = readForm();
let busy = false;
form.addEventListener( 'submit', ( event ) => {
if ( busy ) {
event.preventDefault();
}
} );
button.addEventListener( 'click', async () => {
if ( busy ) {
return;
}
if ( readForm() !== baseline ) {
status.textContent = __( 'Unsaved form changes: saving the form is required before REST toggling.', 'lw-settings-demo' );
return;
}
busy = true;
button.disabled = true;
const controls = Array.from( form.elements );
const disabledBefore = controls.map( ( control ) => control.disabled );
controls.forEach( ( control ) => { control.disabled = true; } );
status.textContent = __( 'Request in progress.', 'lw-settings-demo' );
let writeStarted = false;
let uncertain = false;
try {
const settings = await wp.apiFetch( { path: '/wp/v2/settings' } );
const current = settings.lwsd_settings;
if ( ! current || typeof current.enabled !== 'boolean' ) {
throw new Error( __( 'Stored value does not match the schema.', 'lw-settings-demo' ) );
}
writeStarted = true;
const saved = await wp.apiFetch( {
path: '/wp/v2/settings',
method: 'POST',
data: { lwsd_settings: { ...current, enabled: ! current.enabled } },
} );
const value = saved.lwsd_settings;
if ( ! value || typeof value.enabled !== 'boolean' ||
typeof value.message !== 'string' || ! [ 'top', 'bottom' ].includes( value.position ) ) {
throw new Error( __( 'Unexpected response. Reloading is required to verify the stored state.', 'lw-settings-demo' ) );
}
enabled.checked = value.enabled;
message.value = value.message;
position.value = value.position;
baseline = readForm();
status.textContent = value.enabled
? __( 'Notice bar is on.', 'lw-settings-demo' )
: __( 'Notice bar is off.', 'lw-settings-demo' );
} catch ( error ) {
uncertain = writeStarted;
status.textContent = uncertain
? __( 'Save result uncertain. Reloading is required before further changes.', 'lw-settings-demo' )
: ( error && error.message ? error.message : __( 'Request failed.', 'lw-settings-demo' ) );
} finally {
if ( ! uncertain ) {
controls.forEach( ( control, index ) => { control.disabled = disabledBefore[ index ]; } );
button.disabled = false;
busy = false;
}
}
} );
} )();Prüfumfang und Grenzen
Die archivierten Tests des ursprünglichen Beispiels unter WordPress 7.1.2 und PHP 8.4 dokumentieren Formular- und REST-Speicherung. Die überarbeitete Bereinigung wurde mit WordPress-Hilfsfunktionen geprüft; das neue JavaScript mit simulierten Antworten für Erfolg, ungespeicherte Änderungen, ungültige Daten und verlorene Antworten. Das ersetzt keinen neuen vollständigen Browser- und Installationstest des Plugins.
Die obere Ausgabe benötigt den Theme-Aufruf wp_body_open(), die untere wp_footer(). Das Beispiel enthält noch keine Deinstallation mit Datenlöschung und wurde nicht für Multisite-Netzwerkeinstellungen geprüft. Plugin-Lebenszyklus: Aktivierung, Deaktivierung, Deinstallation.
Fragen und Antworten
Reicht die Registrierung an admin_init?
Für das Admin-Formular ja. Für normale REST-Anfragen und registrierte Standardwerte im Frontend muss die Registrierung auch dort stattfinden. Das Beispiel nutzt deshalb init.
Warum liefert die REST-API null?
Der gespeicherte Wert kann dem Schema widersprechen, etwa durch eine nicht mehr erlaubte Eigenschaft. null ist dann ein Fehlerhinweis im Ergebnis und keine Aufforderung, den Wert ungeprüft zurückzusenden.
Kann eine einzelne Objekteigenschaft geändert werden?
Der Standardcontroller führt ein gesendetes Teilobjekt nicht automatisch mit alten Eigenschaften zusammen. Ein vollständiger Lese- und Schreibvorgang erhält die anderen Werte, kann aber weiterhin mit gleichzeitigen Änderungen kollidieren. Eine eigene Route kann Teiländerungen und eine Konfliktprüfung ausdrücklich umsetzen.
Schützt eine Nonce allein?
Nein. Anmeldung und Benutzerrecht werden getrennt geprüft. Ein gültiger Prüfwert verleiht keine zusätzlichen Rechte.
Quellen
- Plugin Handbook: Settings API, abgerufen am 29.09.2026
- Plugin Handbook: Using Settings API, abgerufen am 29.09.2026
- Plugin Handbook: Custom Settings Page, abgerufen am 29.09.2026
- Plugin Handbook: Options API, abgerufen am 29.09.2026
- Code Reference: register_setting(), abgerufen am 29.09.2026
- Code Reference: settings_fields(), abgerufen am 29.09.2026
- Code Reference: add_option(), abgerufen am 29.09.2026
- Code Reference: wp_set_option_autoload(), abgerufen am 29.09.2026
- REST API Handbook: Settings, abgerufen am 29.09.2026
- Block Editor Handbook: @wordpress/api-fetch, abgerufen am 29.09.2026
- WordPress Developer Blog: How to use WordPress React components for plugin pages, abgerufen am 29.09.2026
- Make WordPress Core: Settings registration in WordPress 4.7, abgerufen am 29.09.2026
- Make WordPress Core: New option functions in 6.4, abgerufen am 29.09.2026
- Make WordPress Core: Options API: Disabling autoload for large options, abgerufen am 29.09.2026
- wordpress-develop: wp-admin/options.php (branch 7.1), abgerufen am 29.09.2026
- wordpress-develop: wp-admin/options-head.php (branch 7.1), abgerufen am 29.09.2026
- wordpress-develop: wp-includes/option.php (branch 7.1), abgerufen am 29.09.2026
- wordpress-develop: class-wp-rest-settings-controller.php (branch 7.1), abgerufen am 29.09.2026
- wordpress-develop: wp-admin/includes/plugin.php (branch 7.1), abgerufen am 29.09.2026
- wordpress-develop: wp-admin/includes/template.php (branch 7.1), abgerufen am 29.09.2026
- wordpress-develop: wp-includes/script-loader.php (branch 7.1), abgerufen am 29.09.2026