Settings API позволяет создать страницу настроек WordPress без дополнительной библиотеки. Он связывает форму в админке с сохранением значений в базе и проверкой прав пользователя.
Соберём страницу настроек темы с тремя полями. Значения будут храниться в одной опции-массиве, проходить проверку при сохранении и выводиться в шаблоне.
Как устроен Settings API
register_setting()объявляет группу, тип и функцию очистки опции. Значение хранится вwp_optionsпосле сохранения; сама регистрация не создаёт строку со значением.add_settings_section()иadd_settings_field()описывают, из каких разделов и полей состоит страница.- Форма отправляется на
options.php. WordPress сам проверяет nonce и права, вызывает функцию очистки, сохраняет опцию и возвращает на страницу с сообщением.
В примере три значения объединены в одну опцию-массив. Код добавляй в functions.php или вынеси в inc/settings.php и подключи его из functions.php. Все фрагменты ниже — части одного примера.
1. Добавление страницы через add_theme_page()
function mytheme_add_settings_page() {
add_theme_page(
'Настройки темы', // заголовок страницы
'Настройки темы', // пункт меню
'edit_theme_options', // право доступа
'mytheme-settings', // ярлык страницы
'mytheme_render_settings_page' // функция вывода
);
}
add_action( 'admin_menu', 'mytheme_add_settings_page' );
add_theme_page() добавляет пункт в «Внешний вид» — логичное место для настроек темы. Для настроек плагина обычно используют add_options_page(): пункт появится в разделе «Настройки», а правом доступа будет manage_options.
2. Регистрация опции, раздела и полей
function mytheme_settings_init() {
register_setting( 'mytheme_settings', 'mytheme_options', array(
'type' => 'array',
'sanitize_callback' => 'mytheme_sanitize_options',
'default' => array(),
) );
add_settings_section(
'mytheme_contacts',
'Контакты',
'mytheme_render_contacts_section',
'mytheme-settings'
);
add_settings_field( 'phone', 'Телефон', 'mytheme_render_text_field', 'mytheme-settings', 'mytheme_contacts', array(
'key' => 'phone',
'type' => 'tel',
'label_for' => 'mytheme-phone',
) );
add_settings_field( 'telegram', 'Telegram', 'mytheme_render_text_field', 'mytheme-settings', 'mytheme_contacts', array(
'key' => 'telegram',
'type' => 'url',
'label_for' => 'mytheme-telegram',
) );
add_settings_field( 'footer_text', 'Текст в подвале', 'mytheme_render_textarea_field', 'mytheme-settings', 'mytheme_contacts', array(
'key' => 'footer_text',
'label_for' => 'mytheme-footer-text',
) );
}
add_action( 'admin_init', 'mytheme_settings_init' );
// Страница принадлежит «Внешнему виду», поэтому сохранять её может тот же круг пользователей.
function mytheme_settings_capability() {
return 'edit_theme_options';
}
add_filter( 'option_page_capability_mytheme_settings', 'mytheme_settings_capability' );
- Первый аргумент
register_setting()— группа настроек. Её же получитsettings_fields()в форме. Второй аргумент — имя опции в базе. - Четвёртый аргумент разделов и полей — ярлык страницы из
add_theme_page(). По немуdo_settings_sections()найдёт, что выводить. label_forпревращает название поля в<label for="…">. Клик по подписи ставит курсор в поле, а экранные дикторы читают подпись вместе с полем.- По умолчанию
options.phpсохраняет любую группу только для пользователей с правомmanage_options. Фильтрoption_page_capability_{группа}меняет право на то, которое указано у пункта меню.
3. HTML-поля и чтение настроек
function mytheme_get_option( $key, $default = '' ) {
$options = get_option( 'mytheme_options', array() );
return isset( $options[ $key ] ) ? $options[ $key ] : $default;
}
function mytheme_render_contacts_section() {
echo '<p>Контакты выводятся в шапке и подвале сайта.</p>';
}
function mytheme_render_text_field( $args ) {
printf(
'<input type="%1$s" id="%2$s" name="mytheme_options[%3$s]" value="%4$s" class="regular-text">',
esc_attr( $args['type'] ),
esc_attr( $args['label_for'] ),
esc_attr( $args['key'] ),
esc_attr( mytheme_get_option( $args['key'] ) )
);
}
function mytheme_render_textarea_field( $args ) {
printf(
'<textarea id="%1$s" name="mytheme_options[%2$s]" rows="4" class="large-text">%3$s</textarea>',
esc_attr( $args['label_for'] ),
esc_attr( $args['key'] ),
esc_textarea( mytheme_get_option( $args['key'] ) )
);
}
Имена полей вида mytheme_options[phone] PHP собирает в массив, и он целиком попадает в одну опцию. mytheme_get_option() пригодится и в шаблонах: она возвращает значение по ключу или пустую строку, если настройку ещё не сохраняли.
4. Форма сохранения настроек
function mytheme_render_settings_page() {
if ( ! current_user_can( 'edit_theme_options' ) ) {
return;
}
?>
<div class="wrap">
<h1><?php echo esc_html( get_admin_page_title() ); ?></h1>
<?php settings_errors(); ?>
<form action="options.php" method="post">
<?php
settings_fields( 'mytheme_settings' );
do_settings_sections( 'mytheme-settings' );
submit_button();
?>
</form>
</div>
<?php
}
settings_fields()выводит скрытые поля группы и действия, nonce и служебное поле с адресом исходной страницы.do_settings_sections()выводит разделы и поля таблицей в стиле админки.settings_errors()показывает «Настройки сохранены» и ошибки проверки. На страницах раздела «Настройки» WordPress выводит их сам, а в «Внешнем виде» и в своих разделах меню без этого вызова сообщений не будет.
5. Проверка и очистка значений
Функция очистки принимает данные формы и возвращает массив для сохранения. Собери его только из ожидаемых ключей: посторонние значения не должны попадать в опцию.
Для каждого поля отдельно задай правило. Например, текст очищается от разметки, URL проверяется как адрес, а неверный телефон сохранять не нужно.
function mytheme_sanitize_options( $input ) {
$input = is_array( $input ) ? $input : array();
$clean = array(
'phone' => sanitize_text_field( $input['phone'] ?? '' ),
'telegram' => esc_url_raw( $input['telegram'] ?? '' ),
'footer_text' => sanitize_textarea_field( $input['footer_text'] ?? '' ),
);
if ( '' !== $clean['phone'] && ! preg_match( '/^[\d\s()+-]{6,20}$/', $clean['phone'] ) ) {
add_settings_error(
'mytheme_options',
'mytheme-phone',
'Телефон может содержать только цифры, пробелы, скобки, плюс и дефис.'
);
$clean['phone'] = mytheme_get_option( 'phone' );
}
return $clean;
}
sanitize_text_field()убирает теги и переводы строк,sanitize_textarea_field()— теги, но сохраняет переводы строк.esc_url_raw()очищает URL и отсекает недопустимые протоколы. Она не проверяет, существует ли страница по этому адресу.- Если телефон не прошёл проверку, функция показывает ошибку через
add_settings_error()и оставляет прежнее значение. Остальные поля при этом сохраняются.
6. Вывод настроек в шаблоне темы
<?php
$phone = mytheme_get_option( 'phone' );
$telegram = mytheme_get_option( 'telegram' );
$footer_text = mytheme_get_option( 'footer_text' );
?>
<?php if ( $phone ) : ?>
<a href="tel:<?php echo esc_attr( preg_replace( '/[^\d+]/', '', $phone ) ); ?>"><?php echo esc_html( $phone ); ?></a>
<?php endif; ?>
<?php if ( $telegram ) : ?>
<a href="<?php echo esc_url( $telegram ); ?>">Telegram</a>
<?php endif; ?>
<?php if ( $footer_text ) : ?>
<p class="footer-text"><?php echo nl2br( esc_html( $footer_text ) ); ?></p>
<?php endif; ?>
Для ссылки tel: из номера убираются пробелы, скобки и дефисы, а на экране он остаётся в привычном виде. nl2br( esc_html() ) сохраняет переводы строк из текстового поля и не пропускает HTML.
Когда выбрать другой способ
- В классической теме настройки внешнего вида можно вынести в Customizer («Внешний вид → Настроить») с предпросмотром. В блочной теме для оформления обычно используют редактор сайта и глобальные стили. Так, например, настраивается логотип — об этом статья о кастомном логотипе.
- Если полей много, нужны картинки, повторяющиеся блоки или вкладки, быстрее собрать страницу на Carbon Fields:
Container::make( 'theme_options', 'Настройки темы' ), а читать значения черезcarbon_get_theme_option(). Как подключить библиотеку, описано в статье о метабоксах. - Settings API хорош, когда полей немного и не хочется зависимостей: это часть ядра, и работать он будет в любой теме.