Прием и обработка POST в WordPress на PHP

Раздел: Веб-разработка на PHP -> Администрирование WordPress

Обработка POST данных в админке WordPress

При создании собственных страниц настроек, форм обратной связи или любых других функциональных элементов в административной панели WordPress возникает необходимость обработать данные, отправленные методом POST. Система предоставляет несколько проверенных способов для этого. Ниже рассмотрены наиболее популярные подходы с примерами кода и указанием типичных ошибок.

Как обработать POST запрос от формы в админке с помощью admin_post?

Основной способ - использовать хук admin_post_{action}. WordPress автоматически вызывает зарегистрированную функцию, когда запрос приходит на admin-post.php с параметром action, равным указанному вами.

Пример регистрации обработчика:


add_action('admin_post_save_my_settings', 'handle_save_my_settings');
function handle_save_my_settings() {
    // Проверка nonce
    if (!isset($_POST['_wpnonce']) || !wp_verify_nonce($_POST['_wpnonce'], 'my_settings_nonce')) {
        wp_die('Ошибка безопасности');
    }
    // Проверка прав
    if (!current_user_can('manage_options')) {
        wp_die('Недостаточно прав');
    }
    // Обработка данных
    $value = sanitize_text_field($_POST['my_option']);
    update_option('my_option', $value);
    // Редирект с сообщением
    wp_redirect(add_query_arg('message', 'saved', wp_get_referer()));
    exit;
}
  

Wp login php action (действие логина wordpress)

Форма в админке:


<form method="post" action="<?php echo admin_url('admin-post.php'); ?>">
    <?php wp_nonce_field('my_settings_nonce'); ?>
    <input type="hidden" name="action" value="save_my_settings">
    <input type="text" name="my_option" value="<?php echo esc_attr(get_option('my_option')); ?>">
    <?php submit_button('Сохранить'); ?>
</form>
  

Admin post new php (создание нового поста в админке wordpress)

Типичные ошибки:

  • Не указан параметр action в форме или он не совпадает с частью хука после admin_post_.
  • Забыта проверка nonce - форма уязвима для CSRF.
  • Использование wp_die без сообщения может запутать пользователя.
  • Редирект без exit - выполнение скрипта продолжается.

Как выполнить асинхронный POST запрос через AJAX с помощью wp_ajax?

Для интерактивных элементов (например, кнопок управления без перезагрузки страницы) используется wp_ajax_{action} и wp_ajax_nopriv_{action} для неавторизованных пользователей.

Обработчик:


add_action('wp_ajax_my_async_action', 'handle_my_async_action');
function handle_my_async_action() {
    check_ajax_referer('my_async_nonce', 'nonce');
    if (!current_user_can('edit_posts')) {
        wp_send_json_error('Нет прав');
    }
    $data = sanitize_text_field($_POST['some_data']);
    // ... обработка
    wp_send_json_success('Готово');
}
  

Wp admin options php (страница настроек в админке wordpress)

JavaScript:


jQuery(document).ready(function($) {
    $('#my-button').on('click', function() {
        $.post(ajaxurl, {
            action: 'my_async_action',
            nonce: '<?php echo wp_create_nonce('my_async_nonce'); ?>',
            some_data: $('#my-field').val()
        }, function(response) {
            if (response.success) {
                alert(response.data);
            }
        });
    });
});
  

Admin post php (администрирование post в php)

Результат: асинхронная обработка без перезагрузки.

Ошибки: неправильное имя action, отсутствие ajaxurl (глобальная переменная в admin), забытая проверка nonce, отправка неверных типов данных.

Как обработать POST через REST API WordPress?

Более современный метод - создание собственного REST маршрута. Это особенно полезно для взаимодействия с внешними сервисами или одностраничными приложениями.

Пример регистрации маршрута:


add_action('rest_api_init', function() {
    register_rest_route('myplugin/v1', '/data/', array(
        'methods' => 'POST',
        'callback' => 'handle_rest_post',
        'permission_callback' => function() {
            return current_user_can('manage_options');
        }
    ));
});
function handle_rest_post($request) {
    $params = $request->get_params();
    $value = sanitize_text_field($params['value']);
    update_option('rest_option', $value);
    return new WP_REST_Response(array('status' => 'ok'), 200);
}
  

Запрос: POST к /wp-json/myplugin/v1/data с телом {"value": "text"} и заголовком X-WP-Nonce: <?php echo wp_create_nonce('wp_rest'); ?>.

Проблемы: неверный nonce, не настроены permission_callback (возвращает null - доступ открыт), не обработаны обязательные параметры, игнорирование кэширования.

Как обработать POST при сохранении стандартных настроек?

Если вы используете Settings API, WordPress автоматически обрабатывает POST при отправке формы, созданной с помощью settings_fields() и do_settings_sections(). Дополнительных обработчиков не требуется - функция, зарегистрированная как sanitize_callback, вызывается автоматически.


add_action('admin_init', function() {
    register_setting('my_option_group', 'my_option', 'intval');
    add_settings_section('my_section', 'Настройки', null, 'my-page');
    add_settings_field('my_option', 'Поле', function() {
        echo '<input type="number" name="my_option" value="' . esc_attr(get_option('my_option')) . '">';
    }, 'my-page', 'my_section');
});
  

Форма выводится на странице:


<form method="post" action="options.php">
    <?php settings_fields('my_option_group'); ?>
    <?php do_settings_sections('my-page'); ?>
    <?php submit_button(); ?>
</form>
  

Ошибки: неправильное имя группы в settings_fields, несоответствие имени опции и имени поля. Если sanitize_callback не возвращает значение, опция не сохраняется.

Расширенные примеры обработки POST в админке WordPress

Пример 1: Полная страница настроек с несколькими секциями и обработкой ошибок

Создается страница, на которой пользователь может изменить несколько опций. При сохранении выполняется валидация, и при ошибке отображается сообщение.

Пример

// Регистрация страницы настроек
add_action('admin_menu', function() {
    add_options_page('Супер настройки', 'Супер настройки', 'manage_options', 'super-settings', 'render_super_settings_page');
});

// Обработчик POST через admin_post (не через Settings API, чтобы показать ручной контроль)
add_action('admin_post_save_super_settings', function() {
    if (!isset($_POST['_wpnonce']) || !wp_verify_nonce($_POST['_wpnonce'], 'super_settings_nonce')) {
        wp_die('Ошибка проверки подлинности');
    }
    if (!current_user_can('manage_options')) {
        wp_die('Недостаточно прав');
    }

    $errors = array();
    $email = sanitize_email($_POST['admin_email']);
    if (!is_email($email)) {
        $errors[] = 'Некорректный email';
    }
    $number = intval($_POST['items_per_page']);
    if ($number < 1 || $number > 100) {
        $errors[] = 'Число должно быть от 1 до 100';
    }

    if (!empty($errors)) {
        set_transient('super_settings_errors', $errors, 30);
        wp_redirect(add_query_arg('error', '1', wp_get_referer()));
        exit;
    }

    update_option('admin_email', $email);
    update_option('items_per_page', $number);

    wp_redirect(add_query_arg('message', 'saved', wp_get_referer()));
    exit;
});

function render_super_settings_page() {
    ?>
    <div class="wrap">
        <h1>Супер настройки</h1>
        <?php
        if (isset($_GET['error'])) {
            $errors = get_transient('super_settings_errors');
            if ($errors) {
                echo '<div class="error"><ul>';
                foreach ($errors as $e) {
                    echo '<li>' . esc_html($e) . '</li>';
                }
                echo '</ul></div>';
                delete_transient('super_settings_errors');
            }
        }
        if (isset($_GET['message']) && $_GET['message'] == 'saved') {
            echo '<div class="updated"><p>Настройки сохранены.</p></div>';
        }
        ?>
        <form method="post" action="<?php echo admin_url('admin-post.php'); ?>">
            <?php wp_nonce_field('super_settings_nonce'); ?>
            <input type="hidden" name="action" value="save_super_settings">
            <table class="form-table">
                <tr>
                    <th><label for="admin_email">Email админа</label></th>
                    <td><input type="email" name="admin_email" id="admin_email" value="<?php echo esc_attr(get_option('admin_email')); ?>" class="regular-text"></td>
                </tr>
                <tr>
                    <th><label for="items_per_page">Элементов на странице</label></th>
                    <td><input type="number" name="items_per_page" id="items_per_page" value="<?php echo esc_attr(get_option('items_per_page', 10)); ?>" min="1" max="100"></td>
                </tr>
            </table>
            <?php submit_button('Сохранить настройки'); ?>
        </form>
    </div>
    <?php
}

Результат: страница настроек с валидацией, выводом ошибок и сообщением об успешном сохранении.

Пример 2: AJAX загрузка файла через POST с проверкой

Загрузка файла в админке без перезагрузки страницы. Используется wp_ajax и встроенная функция wp_handle_upload.

Пример

// Обработчик AJAX
add_action('wp_ajax_upload_custom_file', function() {
    check_ajax_referer('upload_file_nonce', 'nonce');
    if (!current_user_can('upload_files')) {
        wp_send_json_error('Нет прав на загрузку файлов');
    }

    if (empty($_FILES['custom_file'])) {
        wp_send_json_error('Файл не передан');
    }

    $uploaded = wp_handle_upload($_FILES['custom_file'], array('test_form' => false));
    if (isset($uploaded['error'])) {
        wp_send_json_error($uploaded['error']);
    }

    // Сохраняем URL в опцию
    update_option('custom_uploaded_file', $uploaded['url']);
    wp_send_json_success(array('url' => $uploaded['url'], 'file' => $uploaded['file']));
});
Пример

// JavaScript на странице админки
jQuery(document).ready(function($) {
    $('#upload-button').on('click', function(e) {
        e.preventDefault();
        var fileData = new FormData();
        fileData.append('action', 'upload_custom_file');
        fileData.append('nonce', '<?php echo wp_create_nonce('upload_file_nonce'); ?>');
        fileData.append('custom_file', $('#file-input')[0].files[0]);

        $.ajax({
            url: ajaxurl,
            type: 'POST',
            data: fileData,
            contentType: false,
            processData: false,
            success: function(response) {
                if (response.success) {
                    $('#preview').html('<img src="' + response.data.url + '" style="max-width:200px">');
                } else {
                    alert('Ошибка: ' + response.data);
                }
            }
        });
    });
});

Результат: при нажатии на кнопку файл загружается на сервер, а в div #preview появляется превью.

Пример 3: REST маршрут с кастомной валидацией и отложенной обработкой

Создается маршрут, который принимает POST и ставит задачу в очередь (wp_schedule_single_event).

Пример

add_action('rest_api_init', function() {
    register_rest_route('myapp/v1', '/queue-task', array(
        'methods' => 'POST',
        'callback' => 'handle_queue_task',
        'permission_callback' => function() {
            return current_user_can('manage_options');
        },
        'args' => array(
            'task_type' => array(
                'required' => true,
                'validate_callback' => function($param) {
                    return in_array($param, array('email', 'report'));
                }
            ),
            'recipient' => array(
                'required' => false,
                'sanitize_callback' => 'sanitize_email'
            )
        )
    ));
});

function handle_queue_task($request) {
    $type = $request->get_param('task_type');
    $recipient = $request->get_param('recipient');
    // Планируем задачу на выполнение через 5 минут
    wp_schedule_single_event(time() + 300, 'my_custom_task_event', array($type, $recipient));
    return new WP_REST_Response(array('scheduled' => true, 'eta' => 300), 200);
}

Результат ответа:

{
  "scheduled": true,
  "eta": 300
}

Данный подход снижает нагрузку на обработку тяжелых задач.

Пример 4: Экспорт данных через admin_post с потоковой передачей

Пользователь нажимает кнопку, инициируется загрузка CSV файла без лишних буферизаций.

Пример

add_action('admin_post_export_csv', function() {
    if (!current_user_can('export')) {
        wp_die('Нет прав');
    }
    if (!wp_verify_nonce($_GET['_wpnonce'], 'export_csv_nonce')) {
        wp_die('Nonce error');
    }

    header('Content-Type: text/csv; charset=utf-8');
    header('Content-Disposition: attachment; filename="export.csv"');
    $out = fopen('php://output', 'w');
    fputcsv($out, array('ID', 'Имя', 'Email'));
    $users = get_users();
    foreach ($users as $user) {
        fputcsv($out, array($user->ID, $user->display_name, $user->user_email));
    }
    fclose($out);
    exit;
});

Форма-ссылка:

Пример

<a href="<?php echo wp_nonce_url(admin_url('admin-post.php?action=export_csv'), 'export_csv_nonce'); ?>" class="button">Скачать CSV</a>

При клике сразу начинается загрузка файла.

Администрирование POST в PHP - comments

En
Admin post php (php)