WordPress. Обработка POST-запросов. Часть 1

05.08.2019

Теги: CMSHookPOSTWeb-разработкаWordPressПлагинСобытиеФорма

В процессе загрузки WordPress происходит множество событий. К каждому из этих событий можно привязать функцию, которая выполнит какое-то действие (action) или изменит данные (filter). Отправка формы не является исключением — мы можем «прицепить» свою функцию к подходящему хуку и обработать POST-данные.

Кодекс WordPress говорит, что данные формы нужно отправлять на admin-post.php в директории wp-admin. А сама форма должна содержать поле action. Посмотрим на исходный код этого скрипта:

if (!defined('WP_ADMIN')) {
    define('WP_ADMIN', true);
}

if (defined('ABSPATH')) {
    require_once(ABSPATH . 'wp-load.php');
} else {
    require_once(dirname(dirname(__FILE__)) . '/wp-load.php');
}

send_origin_headers();

require_once(ABSPATH . 'wp-admin/includes/admin.php');

nocache_headers();

do_action('admin_init');

$action = empty($_REQUEST['action']) ? '' : $_REQUEST['action'];

if (!wp_validate_auth_cookie()) {
    // если пользователь не авторизован
    if (empty($action)) {
        do_action('admin_post_nopriv');
    } else {
        do_action('admin_post_nopriv_'.$action);
    }
} else {
    // если пользователь авторизован
    if (empty($action)) {
        do_action('admin_post');
    } else {
        do_action('admin_post_'.$action);
    }
}

Все очень просто:

  • когда пользователь не авторизован
    • если action не установлен, происходит событие admin_post_nopriv
    • если action установлен, происходит событие admin_post_nopriv_{action}
  • когда пользователь авторизован
    • если action не установлен, происходит событие admin_post
    • если action установлен, происходит событие admin_post_{action}

Все вышеописанные хуки можно применить и для GET запроса, если адрес примерно такой:

http://www.server.com/wp-admin/admin-post.php?action=some_action&data=some_data

Плагин «Форма обратной связи»

Давайте применим полученные знания на практике и создадим плагин, который позволит добавить на сайт форму обратной связи. Создаем директорию tokmakov-feedback и внутри нее — файл tokmakov-feedback.php.

<?php
/*
Plugin Name: Форма обратной связи
Plugin URI: https://tokmakov.msk.ru
Description: Регистрирует шорткод [tokmakov-feedback] для формы обратной связи, отправляет сообщения на почту.
Version: 1.0
Author: Евгений Токмаков
Author URI: https://tokmakov.msk.ru
*/

register_activation_hook(__FILE__, function() {
    // проверяем права пользователя на активацию плагинов
    if (!current_user_can('activate_plugins')) {
        return;
    }
});

register_deactivation_hook(__FILE__, function() {
    // проверяем права пользователя на деактивацию плагинов
    if (!current_user_can('deactivate_plugins')) {
        return;
    }
});

Для начала зарегистрируем шорткод [tokmakov-feedback]:

/*
 * Регистрируем шорткод [tokmakov-feedback], который позволит
 * вставить форму обратной связи на страницу или запись блога
 */
add_shortcode('tokmakov-feedback', function () {
    ob_start();
    ?>
    <div class="tokmakov-feedback">
        <div class="response"></div>
        <form action="<?= admin_url('admin-post.php'); ?>" method="post">
            <input type="hidden" name="action" value="tokmakov_feedback" />
            <input type="hidden" name="redirect" value="<?= get_permalink(); ?>" />
            <label for="name">
                <span>Имя</span>
                <input type="text" name="name" value="" required />
            </label>
            <label for="email">
                <span>Email</span>
                <input type="text" name="email" value="" required />
            </label>
            <label for="phone">
                <span>Телефон</span>
                <input type="text" name="phone" value="" />
            </label>
            <label for="message">
                <span>Сообщение</span>
                <textarea name="message" required></textarea>
            </label>
            <input type="submit" value="Отправить" />
        </form>
    </div>
    <?php
    return ob_get_clean();
});

У нас есть скрытое поле формы action со значением tokmakov_feedback — значит, нам доступны два хука:

  • admin_post_nopriv_tokmakov_feedback
  • admin_post_tokmakov_feedback

Форму мы должны обработать независимо от того, залогинился пользователь или нет:

/*
 * Обрабатываем отправленные данные формы обратной связи
 */
add_action('admin_post_nopriv_tokmakov_feedback', 'tokmakov_process_feedback_form');
add_action('admin_post_tokmakov_feedback', 'tokmakov_process_feedback_form');

function tokmakov_process_feedback_form() {
    /*
     * Здесь уже можно обрабатывать массивы $_POST или $_GET
     */
}

Что ж, давайте напишем функцию, которая будет обрабатывать данные формы и отправлять письмо администратору:

function tokmakov_process_feedback_form() {
    /*
     * Обрабатываем данные, полученные из формы
     */
    $data['name'] = trim(iconv_substr(strip_tags($_POST['name']), 0, 50));
    $data['email'] = trim(iconv_substr(strip_tags($_POST['email']), 0, 50));
    $data['phone'] = trim(iconv_substr(strip_tags($_POST['phone']), 0, 50));
    $data['message'] = trim(iconv_substr(strip_tags($_POST['message']), 0, 1000));

    // были допущены ошибки при заполнении формы?
    if (empty($data['name'])) {
        $errors[] = 'Не заполнено обязательное поле «Имя»';
    }
    if (empty($data['email'])) {
        $errors[] = 'Не заполнено обязательное поле «E-mail»';
    }
    if (empty($data['message'])) {
        $errors[] = 'Не заполнено обязательное поле «Сообщение»';
    }

    if (!empty($errors)) {
        /*
         * были допущены ошибки при заполнении формы, сохраняем введенные
         * пользователем данные, чтобы после редиректа снова показать форму,
         * заполненную введенными ранее даннными и сообщением об ошибке
         */
        $_SESSION['tokmakov_feedback']['success'] = false;
        $message = 'При заполнении формы были допущены ошибки';
        $_SESSION['tokmakov_feedback']['message'] = $message;
        $_SESSION['tokmakov_feedback']['errors'] = $errors;
        $_SESSION['tokmakov_feedback']['data'] = $data;
    } else {
        // отправляем письмо администратору
        if (tokmakov_sendmail($data)) {
            $_SESSION['tokmakov_feedback']['success'] = true;
            $message = 'Ваше сообщение успешно отправлено';
            $_SESSION['tokmakov_feedback']['message'] = $message;
        } else {
            $_SESSION['tokmakov_feedback']['success'] = false;
            $message = 'Произошла ошибка при отправке письма';
            $_SESSION['tokmakov_feedback']['message'] = $message;
            $_SESSION['tokmakov_feedback']['data'] = $data;
        }
    }

    // после отправки формы делаем редирект, чтобы предотвратить
    // повторную отправку, если пользователь обновит страницу
    $redirect = home_url();
    if (isset($_POST['redirect'])) {
        $redirect = $_POST['redirect'];
        $redirect = wp_validate_redirect($redirect, home_url());
    }
    wp_redirect($redirect);
    die();
}
function tokmakov_sendmail($data) {
    $message = 'Имя: ' . $data['name'] . PHP_EOL;
    $message .= 'E-mail: ' . $data['email'] . PHP_EOL;
    if (!empty($data['phone'])) {
        $message .= 'Телефон: ' . $data['phone'] . PHP_EOL;
    }
    $message .= PHP_EOL . 'Сообщение: ' . PHP_EOL . $data['message'] . PHP_EOL;
    $result = wp_mail(
        get_bloginfo('admin_email'),
        'Заполнена форма обратной связи',
        $message
    );
    return $result;
}

Как видите, функция проверяет корректность данных и записывает результат проверки в сессию. Чтобы после редиректа показать результаты проверки пользователю. Если форма содержит ошибки, в сессию будут также записаны введенные пользователем данные. Будет некрасиво, если пользователь потратил время, чтобы написать сообщение, а мы это сообщение потеряем при проверке.

А теперь доработаем функцию, которая регистрирует шорткод. Кроме самой формы, она будет показывать сообщения об ошибках или сообщение об успешной отправке:

/*
 * Регистрируем шорткод [tokmakov-feedback], который позволит
 * вставить форму обратной связи на страницу или запись блога
 */
add_shortcode('tokmakov-feedback', function () {
    $name = '';
    $email = '';
    $phone = '';
    $message = '';
    if (isset($_SESSION['tokmakov_feedback']['data'])) {
        $name = htmlspecialchars($_SESSION['tokmakov_feedback']['data']['name']);
        $email = htmlspecialchars($_SESSION['tokmakov_feedback']['data']['email']);
        $phone = htmlspecialchars($_SESSION['tokmakov_feedback']['data']['phone']);
        $message = htmlspecialchars($_SESSION['tokmakov_feedback']['data']['message']);
    }
    ob_start();
    ?>
    <div class="tokmakov-feedback">
        <div class="response">
            <?php if (isset($_SESSION['tokmakov_feedback'])): ?>
                <p><?= $_SESSION['tokmakov_feedback']['message']; ?></p>
                <?php if (isset($_SESSION['tokmakov_feedback']['errors'])): ?>
                    <ul>
                    <?php foreach ($_SESSION['tokmakov_feedback']['errors'] as $error): ?>
                        <li><?= $error; ?></li>
                    <?php endforeach; ?>
                    </ul>
                <?php endif; ?>
                <?php unset($_SESSION['tokmakov_feedback']); ?>
            <?php endif; ?>
        </div>
        <form action="<?= admin_url('admin-post.php'); ?>" method="post">
            <input type="hidden" name="action" value="tokmakov_feedback" />
            <input type="hidden" name="redirect" value="<?= get_permalink(); ?>" />
            <label>
                <span>Имя</span>
                <input type="text" name="name" value="<?= $name; ?>" required />
            </label>
            <label>
                <span>E-mail</span>
                <input type="text" name="email" value="<?= $email; ?>" required />
            </label>
            <label>
                <span>Телефон</span>
                <input type="text" name="phone" value="<?= $phone; ?>" />
            </label>
            <label>
                <span>Сообщение</span>
                <textarea name="message" required><?= $message; ?></textarea>
            </label>
            <input type="submit" value="Отправить" />
        </form>
    </div>
    <?php
    return ob_get_clean();
});

Поскольку мы используем суперглобальный массив $_SESSION, давайте стартанем сессию при наступлении события init:

/*
 * Запускаем сессию, чтобы передавать сообщение о результате
 * обработки формы обратной связи после перезагрузки страницы
 */
add_action('init', function () {
    if (session_id() == '') {
        session_start();
    }
});

Если при проверке данных формы были обнаружены ошибки, элемент массива $_SESSION с ключом tokmakov_feedback будет содержать вот что:

Array
(
    [success] => false
    [message] => При заполнении формы были допущены ошибки
    [errors] => Array
        (
            [0] => Не заполнено обязательное поле «E-mail»
        )
    [data] => Array
        (
            [name] => Евгений
            [email] =>
            [phone] =>
            [message] => Какое-то сообщение
        )
)

Если данные формы были успешно отправлены, этот элемент массива будет таким:

Array
(
    [success] => true
    [message] => Ваше сообщение успешно отправлено
)

Скачать плагин «Форма обратной связи» можно здесь.

Поиск: CMS • Hook • POST • Web-разработка • WordPress • Событие • Форма • Обратная связь • Плагин • Plugin • Плагин • Feedback

Каталог оборудования
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Производители
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Функциональные группы
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.