WordPress Settings API — секции, поля и настройки

Когда дело доходит до разработки плагинов и тем под WordPress, разработчик сталкивается с десятком разных способов сделать страницу настроек и проверку вводимых данных. Кто-то пишет сырой HTML в колбэке, кто-то прикручивает собственный валидатор форм, кто-то хранит данные в отдельных таблицах. Все эти подходы объединяет одно: они игнорируют встроенный механизм WordPress и создают проблемы с безопасностью, совместимостью и поддержкой.

Единственный корректный способ работы с настройками в WordPress сегодня — Settings API. Он предоставляет унифицированный интерфейс для регистрации опций, отрисовки полей, валидации и санитизации данных. Встроен в ядро с версии 2.7, стабилен, документирован и используется тысячами плагинов из официального репозитория. Если вы пишете тему или плагин с админ-настройками — вы обязаны знать этот API.

Settings API не работает на фронтенде. Он предназначен исключительно для административной панели WordPress. Если вам нужны пользовательские настройки — смотрите в сторону User Meta.

Три кита 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НазначениеПривязка
Settingregister_setting()Регистрирует опцию в wp_optionsПривязывается к группе опций (option group)
Sectionadd_settings_section()Создаёт логическую группу полейПривязывается к странице (page slug)
Fieldadd_settings_field()Добавляет элемент вводаПривязывается к секции и странице
Порядок регистрации имеет значение. Сначала register_setting(), потом add_settings_section(), потом add_settings_field(). Если перепутать — поля не отобразятся, и WordPress не выдаст ошибку. Будет просто пустая страница.

Песочница для экспериментов

Чтобы освоить 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_groupstringДаИмя группы настроек. Должно совпадать с параметром settings_fields() на странице опций
$option_namestringДаИмя опции в базе данных. Уникальный ключ в wp_options
$argsarrayДа (с 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 символы.

Имя опции должно быть уникальным. Не используйте общие названия типа site_title или theme_options — рискуете получить конфликт с другими плагинами и темами. Префикс sandbox_ защищает от коллизий.

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.

Всегда экранируйте вывод: esc_attr() для атрибутов, esc_html() для текста, esc_url() для URL. Не пропускайте пользовательский ввод через echo без обработки — это прямой путь к XSS-уязвимости.

Санитизация — защита от грязных данных

Параметр sanitize_callback в register_setting() — не опция, а обязательный рубеж обороны. Каждый элемент ввода должен иметь свою функцию очистки. WordPress предоставляет набор готовых санитайзеров:

ФункцияНазначениеПример входаПример выхода
sanitize_text_field()Очистка текста: удаляет теги, обрезает пробелы, фильтрует UTF-8<script>alert(1)</script> hellohello
sanitize_email()Валидация и очистка emailUser@Example.com user@example.com
sanitize_url()Очистка URL, удаление невалидных символовjavascript:alert(1)(пустая строка)
absint()Приведение к неотрицательному целому-42.742
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]
Никогда не используйте один и тот же санитайзер для всех полей. sanitize_text_field() удалит URL-схему из ссылки, а absint() обрежет текст до нуля. Каждому типу поля — свой санитайзер.

Собираем страницу опций

Теперь объединим всё в рабочую страницу настроек темы. Полный код 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 запросов при загрузкеОтдельный колбэк на каждое полеВысокий (каждое имя должно быть уникальным)
WordPress автоматически загружает все опции с флагом autoload=yes в одном запросе при старте. Если у вас 20 отдельных опций — это 20 строк в wp_options, но всё равно один запрос благодаря autoload. Проблема возникает, когда вы вызываете get_option() для не-autoload опций — каждый вызов порождает отдельный запрос к БД.

Проверка результата

После регистрации настроек полезно убедиться, что всё работает, до того как создавать интерфейс. Добавьте этот код в 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">.

Функция add_settings_error() не блокирует сохранение данных. Она лишь информирует пользователя о проблеме. Если нужно прервать сохранение при критической ошибке, верните из санитайзера старые значения опции через get_option().

Валидацию и санитизацию удобно совмещать в одной функции, передаваемой в 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 — об этом нужно позаботиться отдельно через хук регистрации деактивации темы.