WordPress Settings API — секции, поля и настройки
Когда дело доходит до разработки плагинов и тем под WordPress, разработчик сталкивается с десятком разных способов сделать страницу настроек и проверку вводимых данных. Кто-то пишет сырой HTML в колбэке, кто-то прикручивает собственный валидатор форм, кто-то хранит данные в отдельных таблицах. Все эти подходы объединяет одно: они игнорируют встроенный механизм WordPress и создают проблемы с безопасностью, совместимостью и поддержкой.
Единственный корректный способ работы с настройками в WordPress сегодня — Settings API. Он предоставляет унифицированный интерфейс для регистрации опций, отрисовки полей, валидации и санитизации данных. Встроен в ядро с версии 2.7, стабилен, документирован и используется тысячами плагинов из официального репозитория. Если вы пишете тему или плагин с админ-настройками — вы обязаны знать этот API.
Три кита Settings API
Прежде чем писать код, разберитесь с тремя фундаментальными понятиями, на которых держится весь Settings API. Без этого понимания вы будете копировать примеры из документации механически, не осознавая, что именно происходит.
Settings (настройки) — это зарегистрированные опции, которые WordPress хранит в таблице wp_options. Каждая настройка имеет уникальное имя (ключ) и значение. Регистрация через register_setting() говорит WordPress: «Вот опция, её разрешено сохранять через Settings API, а перед сохранением прогнать через вот эту функцию санитизации». Без регистрации WordPress отклонит попытку сохранить значение — это встроенный механизм защиты от подмены произвольных опций.
Sections (секции) — логические группы полей на странице настроек. Представьте страницу «Общие настройки» WordPress: «Название сайта» и «Краткое описание» — это одна секция, «Часовой пояс» и «Формат даты» — другая. Секции создаются функцией add_settings_section() и служат для визуальной организации полей. Каждая секция имеет заголовок и опциональное описание.
Fields (поля) — минимальная единица интерфейса настроек. Текстовое поле, чекбокс, радиокнопка, выпадающий список, поле загрузки файла, редактор WYSIWYG — всё это поля. Каждое поле привязано к определённой секции и странице. Создаётся через add_settings_field(). Именно в колбэке поля вы пишете HTML-код элемента ввода.
| Элемент API | Функция WordPress | Назначение | Привязка |
|---|---|---|---|
| Setting | register_setting() | Регистрирует опцию в wp_options | Привязывается к группе опций (option group) |
| Section | add_settings_section() | Создаёт логическую группу полей | Привязывается к странице (page slug) |
| Field | add_settings_field() | Добавляет элемент ввода | Привязывается к секции и странице |
Песочница для экспериментов
Чтобы освоить Settings API, создадим изолированную среду — отдельную тему-песочницу. Она не будет влиять на основной сайт и позволит экспериментировать без страха что-то сломать.
Создайте директорию темы:
wp-content/themes/wordpress-settings-sandbox/[/codeblock]Минимальный набор файлов для распознавания темы WordPress:
style.css — обязательный файл, содержащий заголовок темы в комментарии. Без него WordPress не увидит тему в списке доступных.
/*
Theme Name: WordPress Settings Sandbox
Theme URI: https://photolessons.org
Author: Admin
Author URI: https://photolessons.org
Description: A sandbox theme for learning the WordPress Settings API.
Version: 1.0.0
License: GPL v2 or later
*/[/codeblock]index.php — точка входа, без которой WordPress считает тему неполной. Оставляем пустым или с минимальной разметкой.
functions.php — здесь будет вся логика Settings API. Начинаем с подключения хуков:Теперь активируйте тему через Appearance → Themes. Песочница готова.register_setting() — регистрируем опцию
Функция register_setting() принимает три параметра:
| Параметр | Тип | Обязательный | Описание |
|---|---|---|---|
| $option_group | string | Да | Имя группы настроек. Должно совпадать с параметром settings_fields() на странице опций |
| $option_name | string | Да | Имя опции в базе данных. Уникальный ключ в wp_options |
| $args | array | Да (с WP 4.7) | Массив аргументов: type, description, sanitize_callback, show_in_rest, default |
Пример регистрации одной опции:
function sandbox_register_settings() {
register_setting(
'sandbox_options_group',
'sandbox_site_title',
array(
'type' => 'string',
'description' => 'Custom site title for the sandbox theme',
'sanitize_callback' => 'sanitize_text_field',
'default' => 'My Sandbox Site',
)
);
}
add_action( 'admin_init', 'sandbox_register_settings' );[/codeblock]После этого вызова WordPress знает, что опция sandbox_site_title существует, принадлежит группе sandbox_options_group и перед сохранением должна пройти через sanitize_text_field() — встроенную функцию WordPress, которая удаляет HTML-теги и невалидные UTF-8 символы.
add_settings_section() — группируем поля
Секция нужна, чтобы поля не висели в воздухе. Функция принимает следующие параметры:
add_settings_section(
'sandbox_general_section',
'General Settings',
'sandbox_general_section_callback',
'sandbox_options_page'
);[/codeblock]Колбэк описания необязателен, но рекомендован — он выводит пояснительный текст под заголовком секции:
function sandbox_general_section_callback() {
echo 'These settings control the basic appearance of your sandbox theme.
'; }[/codeblock]add_settings_field() — создаём элементы ввода
Поле — это то, что пользователь видит и заполняет. Функция принимает шесть параметров:
add_settings_field(
'sandbox_site_title_field',
'Site Title',
'sandbox_site_title_field_callback',
'sandbox_options_page',
'sandbox_general_section',
array(
'label_for' => 'sandbox_site_title',
)
);[/codeblock]Колбэк поля отвечает за HTML-разметку:
function sandbox_site_title_field_callback( $args ) {
$value = get_option( 'sandbox_site_title', 'My Sandbox Site' );
?>
Enter the title displayed in the header.
Санитизация — защита от грязных данных
Параметр sanitize_callback в register_setting() — не опция, а обязательный рубеж обороны. Каждый элемент ввода должен иметь свою функцию очистки. WordPress предоставляет набор готовых санитайзеров:
| Функция | Назначение | Пример входа | Пример выхода |
|---|---|---|---|
| sanitize_text_field() | Очистка текста: удаляет теги, обрезает пробелы, фильтрует UTF-8 | <script>alert(1)</script> hello | hello |
| sanitize_email() | Валидация и очистка email | User@Example.com | user@example.com |
| sanitize_url() | Очистка URL, удаление невалидных символов | javascript:alert(1) | (пустая строка) |
| absint() | Приведение к неотрицательному целому | -42.7 | 42 |
| sanitize_key() | Очистка строки до букв, цифр, дефисов и подчёркиваний | Hello World!@# | helloworld |
| wp_filter_nohtml_kses() | Удаление всех HTML-тегов, включая разрешённые в kses | <strong>text</strong> | text |
Для сложной санитизации пишут кастомный колбэк. Например, для группы опций, где одно поле — текст, другое — URL, третье — чекбокс:
function sandbox_sanitize_options( $input ) {
$sanitized = array();
if ( isset( $input['site_title'] ) ) {
$sanitized['site_title'] = sanitize_text_field( $input['site_title'] );
}
if ( isset( $input['logo_url'] ) ) {
$sanitized['logo_url'] = esc_url_raw( $input['logo_url'] );
}
if ( isset( $input['enable_banner'] ) ) {
$sanitized['enable_banner'] = (bool) $input['enable_banner'] ? 1 : 0;
}
return $sanitized;
}[/codeblock]Собираем страницу опций
Теперь объединим всё в рабочую страницу настроек темы. Полный код functions.php песочницы:
'array',
'description' => 'Sandbox theme configuration',
'sanitize_callback' => 'sandbox_sanitize_options',
'default' => array(
'site_title' => 'My Sandbox',
'logo_url' => '',
'enable_banner' => 1,
'footer_text' => '',
),
)
);
add_settings_section(
'sandbox_general_section',
'General Settings',
'sandbox_general_section_callback',
'sandbox_options_page'
);
add_settings_field(
'sandbox_site_title_field',
'Site Title',
'sandbox_text_field_callback',
'sandbox_options_page',
'sandbox_general_section',
array(
'label_for' => 'sandbox_site_title',
'option' => 'sandbox_theme_options',
'key' => 'site_title',
)
);
add_settings_field(
'sandbox_logo_url_field',
'Logo URL',
'sandbox_text_field_callback',
'sandbox_options_page',
'sandbox_general_section',
array(
'label_for' => 'sandbox_logo_url',
'option' => 'sandbox_theme_options',
'key' => 'logo_url',
)
);
add_settings_field(
'sandbox_enable_banner_field',
'Enable Banner',
'sandbox_checkbox_field_callback',
'sandbox_options_page',
'sandbox_general_section',
array(
'label_for' => 'sandbox_enable_banner',
'option' => 'sandbox_theme_options',
'key' => 'enable_banner',
)
);
add_settings_field(
'sandbox_footer_text_field',
'Footer Text',
'sandbox_textarea_field_callback',
'sandbox_options_page',
'sandbox_general_section',
array(
'label_for' => 'sandbox_footer_text',
'option' => 'sandbox_theme_options',
'key' => 'footer_text',
)
);
}
add_action( 'admin_init', 'sandbox_register_settings' );
function sandbox_general_section_callback() {
echo 'Configure the basic appearance settings for the sandbox theme.
'; } function sandbox_text_field_callback( $args ) { $options = get_option( $args['option'], array() ); $value = isset( $options[ $args['key'] ] ) ? $options[ $args['key'] ] : ''; ?> > После добавления этого кода зайдите в админку. Страница опций пока не видна — мы зарегистрировали настройки, но не создали меню для доступа к ним. Этим займёмся в следующей статье. Однако вы можете проверить, что опция появилась в базе данных, заглянув в таблицуwp_options через phpMyAdmin или выполнив get_option('sandbox_theme_options') в любом хуке после admin_init.Группа опций против одиночных опций
В примере выше все поля хранятся в одном массиве sandbox_theme_options. Это рекомендуемый подход для тем и плагинов. Альтернатива — регистрировать каждое поле как отдельную опцию. Разница принципиальная:
| Подход | Строк в wp_options | Производительность | Удобство санитизации | Риск коллизий |
|---|---|---|---|---|
| Массив (один ключ) | 1 | Один запрос на все настройки | Одна функция на все поля | Низкий (префикс защищает) |
| Отдельные опции | N (по числу полей) | N запросов при загрузке | Отдельный колбэк на каждое поле | Высокий (каждое имя должно быть уникальным) |
Проверка результата
После регистрации настроек полезно убедиться, что всё работает, до того как создавать интерфейс. Добавьте этот код в functions.php для вывода дампа опций в футере (только для администраторов):
function sandbox_debug_options() {
if ( current_user_can( 'manage_options' ) ) {
echo '';
}
}
add_action( 'wp_footer', 'sandbox_debug_options' );[/codeblock]Теперь откройте любую страницу сайта как администратор и посмотрите HTML-код. В футере будет закомментированный массив настроек. Это примитивный, но действенный способ отладки Settings API на ранних этапах. Рекомендую использовать его до тех пор, пока не появится интерфейс страницы опций с визуальной обратной связью.
Типичные ошибки новичков
Забыли admin_init. Все вызовы register_setting(), add_settings_section() и add_settings_field() должны выполняться внутри хука admin_init. Если вы вызываете их напрямую в functions.php, WordPress ещё не загрузил API настроек, и функции просто не существуют. Результат — фатальная ошибка Call to undefined function.
Несовпадение option group. Параметр $option_group в register_setting() должен совпадать с тем, что вы передаёте в settings_fields() при отрисовке страницы опций. Если не совпадает — форма не пройдёт проверку nonce, и WordPress молча отклонит сохранение. Никаких ошибок не будет, просто данные не сохранятся.
Санитизация возвращает не то, что ожидается. Допустим, вы написали sanitize_callback, который возвращает массив, а ожидаете строку. WordPress сохранит то, что вернула функция, и get_option() вернёт массив там, где код ожидает строку. Результат — PHP Warning: Array to string conversion.
Забыли экранировать вывод в колбэках полей. Кажется мелочью, пока кто-то не введёт <script>alert(document.cookie)</script> в поле Site Title. Всегда используйте esc_attr() для атрибутов и esc_html() для текстового содержимого.
Валидация данных перед сохранением: add_settings_error()
Санитизация и валидация — разные процессы. Санитизация чистит данные, валидация проверяет, соответствуют ли данные заданным критериям. WordPress предоставляет функцию add_settings_error(), которая выводит сообщения об ошибках на странице настроек:
function sandbox_validate_options( $input ) {
$sanitized = sandbox_sanitize_options( $input );
$site_title = $sanitized['site_title'];
if ( strlen( $site_title ) < 3 ) {
add_settings_error(
'sandbox_theme_options',
'site_title_too_short',
'Site title must be at least 3 characters long.',
'error'
);
$sanitized['site_title'] = get_option( 'sandbox_theme_options' )['site_title'] ?? '';
}
if ( strlen( $site_title ) > 60 ) {
add_settings_error(
'sandbox_theme_options',
'site_title_too_long',
'Site title cannot exceed 60 characters.',
'warning'
);
}
return $sanitized;
}[/codeblock]Последний параметр add_settings_error(): error для критических ошибок, warning для предупреждений, success для подтверждений и info для нейтральных уведомлений. Сообщения отображаются после сохранения только если в шаблоне страницы вызвана функция settings_errors() — обычно её размещают сразу после открывающего тега <div class="wrap">.
Валидацию и санитизацию удобно совмещать в одной функции, передаваемой в sanitize_callback. WordPress вызывает эту функцию при каждом сохранении, и она может как чистить данные, так и добавлять сообщения об ошибках. Один колбэк вместо двух, компактный код, единая точка ответственности.
Использование хука pre_update_option для дополнительного контроля
Если стандартной санитизации недостаточно, WordPress предоставляет фильтр pre_update_option_{$option}, который срабатывает непосредственно перед записью значения в базу данных. В отличие от sanitize_callback, этот хук получает и старое, и новое значение опции, что позволяет реализовать более сложную логику — например, вести журнал изменений или откатывать нежелательные правки:
add_filter( 'pre_update_option_sandbox_theme_options', function( $new_value, $old_value ) {
error_log( sprintf(
'[Sandbox] Options updated. Old: %s | New: %s',
wp_json_encode( $old_value ),
wp_json_encode( $new_value )
) );
if ( ! empty( $new_value['site_title'] ) && $new_value['site_title'] === $old_value['site_title'] ) {
error_log( '[Sandbox] Site title unchanged — skipping redundant save logic' );
}
return $new_value;
}, 10, 2 );[/codeblock]Этот подход полезен для аудита изменений настроек в крупных проектах, где несколько администраторов могут модифицировать конфигурацию темы одновременно. Вы также можете блокировать определённые значения до сохранения — просто верните $old_value вместо $new_value, и WordPress запишет старые данные, как если бы изменений не было.
Работа с разными типами полей: практические примеры
Помимо текстовых полей и чекбоксов, в админ-панели WordPress часто используются выпадающие списки и радиокнопки. Рассмотрим, как реализовать их через Settings API.
Выпадающий список (select):
function sandbox_select_field_callback( $args ) {
$options = get_option( $args['option'], array() );
$current = isset( $options[ $args['key'] ] ) ? $options[ $args['key'] ] : '';
$choices = array(
'left' => 'Left Sidebar',
'right' => 'Right Sidebar',
'none' => 'No Sidebar',
);
?>
Радиокнопки:function sandbox_radio_field_callback( $args ) {
$options = get_option( $args['option'], array() );
$current = isset( $options[ $args['key'] ] ) ? $options[ $args['key'] ] : 'light';
$choices = array(
'light' => 'Light Theme',
'dark' => 'Dark Theme',
);
foreach ( $choices as $value => $label ) {
?>
Обратите внимание на использование функций
selected() и checked() — это помощники WordPress, которые выводят атрибут selected или checked, если переданные значения совпадают. Они избавляют от писанины тернарных операторов в HTML и делают шаблоны чище.\u{201c}Settings API — это контракт между вами и WordPress. Вы регистрируете настройку и говорите «вот так её надо чистить». WordPress сохраняет и загружает. Не пытайтесь обойти этот контракт прямыми запросами к wp_options через $wpdb — потеряете и безопасность, и совместимость, и собственное время на отладку.
Часто задаваемые вопросы по Settings API
Что такое Settings API в WordPress?
Settings API — это встроенный в ядро WordPress (с версии 2.7) набор функций для регистрации, отображения и сохранения настроек тем и плагинов. Он включает три ключевые функции: register_setting() для регистрации опций, add_settings_section() для группировки полей и add_settings_field() для создания элементов ввода. API автоматически обрабатывает nonce-проверки, санитизацию и сохранение в таблицу wp_options.
Обязательно ли использовать Settings API при разработке темы?
Нет, WordPress не блокирует прямой доступ к wp_options через update_option() и get_option(). Но ручное сохранение форм без Settings API означает, что вы берёте на себя nonce-верификацию, проверку прав, санитизацию и защиту от подмены произвольных опций. Settings API делает всё это автоматически. Прямой доступ оправдан только в нестандартных сценариях — например, сохранение настроек через AJAX без перезагрузки страницы.
В каком хуке регистрировать настройки?
Все вызовы register_setting(), add_settings_section() и add_settings_field() должны происходить внутри хука admin_init. Этот хук срабатывает после загрузки ядра, но до рендеринга админ-страниц. Вызов функций Settings API до admin_init приведёт к фатальной ошибке, так как функции ещё не определены.
Можно ли хранить несколько полей в одной опции?
Да, и это рекомендуемый подход. Вместо регистрации 10 отдельных опций типа mytheme_color, mytheme_font, mytheme_layout, зарегистрируйте одну опцию mytheme_options как массив. Это даёт один вызов get_option() вместо десяти, упрощает санитизацию (одна функция на весь массив) и снижает риск конфликта имён с другими плагинами.
Как правильно писать sanitize_callback?
Функция санитизации получает на вход грязные данные из $_POST и должна вернуть очищенные данные того же типа. Для массивов опций — принимает массив и возвращает массив. Для одиночных — строку и возвращает строку. Не доверяйте ни одному значению из $input. Каждое поле проверяйте на существование через isset() и пропускайте через соответствующий типу санитайзер.
Почему мои настройки не сохраняются?
Три самые частые причины: несовпадение $option_group в register_setting() и settings_fields() на странице опций; отсутствие хука admin_init для регистрации; санитизация возвращает пустое значение (например, sanitize_text_field() для пустой строки). Проверьте также права пользователя — current_user_can('manage_options') должно возвращать true.
Как добавить WYSIWYG-редактор в поле настроек?
Используйте функцию wp_editor() в колбэке поля. Но учтите: Settings API по умолчанию не обрабатывает HTML в значениях опций. Вам нужен кастомный sanitize_callback с wp_kses_post() вместо sanitize_text_field(). Никогда не сохраняйте unfiltered HTML — это позволит пользователю с правами редактора выполнить XSS-атаку через поле WYSIWYG.
Что такое option group и зачем она нужна?
Option group — это строковый идентификатор, связывающий зарегистрированные настройки с конкретной формой на странице опций. Когда вы вызываете settings_fields('my_group') внутри формы, WordPress вставляет скрытые поля с nonce и идентификатором группы. При отправке формы WordPress сверяет группу из запроса с зарегистрированными настройками и применяет соответствующие sanitize_callback. Без совпадения групп сохранение не происходит.
Чем Settings API отличается от Customizer API?
Settings API работает в админке и предназначен для разработчиков тем и плагинов. Customizer API показывает настройки в реальном времени на фронтенде через Customizer — он ориентирован на конечных пользователей. Технически Customizer использует тот же механизм wp_options и может вызывать register_setting(). Выбор зависит от аудитории.
Можно ли регистрировать настройки динамически в зависимости от условий?
Да, но с оговорками. Вы можете обернуть register_setting() в условный оператор внутри хука admin_init. Например, регистрировать поле Facebook URL только если пользователь включил социальные сети в основной настройке. Однако при изменении условия ранее сохранённые значения не удаляются автоматически из wp_options — об этом нужно позаботиться отдельно через хук регистрации деактивации темы.



