Свой плагин для WordPress: как написать без переделок
Код в functions.php живёт до первой смены темы, а одна опечатка роняет сайт. Разбираем, где держать свой код в WordPress 6.x и как собрать плагин, который включается, настраивается и удаляется без следов.

Заказчик просит шорткод с телефоном, и код уходит в functions.php активной темы: быстро, работает. Через год тему меняют, и на всех страницах вместо ссылки висит текст [mysite_phone]. Хуже, если опечатка попала в тот же файл: сайт отдаёт белый экран, а вернуть его можно только через FTP. Ниже: куда класть свой код в WordPress 6.x, как собрать минимальный плагин и что в нём ломается чаще всего.
Три места для кода и цена каждого
| Подход | Переживает смену темы | Отключается в админке | Цена |
|---|---|---|---|
| functions.php темы | Нет | Нет | Быстро, но код привязан к теме; отключить его можно только правкой файла |
| Обычный плагин | Да | Да | Нужна дисциплина: префиксы, экранирование, uninstall.php |
| mu-плагин | Да | Нет | Файлы из wp-content/mu-plugins грузятся всегда и раньше обычных плагинов; подпапки не подхватываются, активации нет |
Скелет плагина
Создайте папку wp-content/plugins/mysite-phone и файл mysite-phone.php внутри. Пример рассчитан на WordPress 6.x и PHP 7.4 и новее. Заголовки Requires at least и Requires PHP читает сам WordPress: на неподходящей версии плагин не активируется.
<?php
/**
* Plugin Name: MySite Phone
* Version: 1.0.0
* Requires at least: 6.0
* Requires PHP: 7.4
* License: GPL-2.0-or-later
* Text Domain: mysite-phone
*/
// Прямой запрос к файлу в обход WordPress ничего выполнять не должен.
defined( 'ABSPATH' ) || exit;
function mysite_phone_shortcode() {
$phone = get_option( 'mysite_phone', '' );
if ( '' === $phone ) {
return '';
}
// В ссылке оставляем цифры и плюс, в тексте берём номер как есть.
$href = preg_replace( '/[^\\d+]/', '', $phone );
// Экранируем при выводе, что бы ни лежало в базе.
return '<a href=\"' . esc_url( 'tel:' . $href ) . '\">' . esc_html( $phone ) . '</a>';
}
add_shortcode( 'mysite_phone', 'mysite_phone_shortcode' );Три мелочи. Проверка ABSPATH закрывает прямой вызов файла по URL. Префикс mysite_ защищает от конфликта имён с другим плагином. Закрывающий ?> не нужен: пробел после него уйдёт в вывод и сломает заголовки ответа.
Настройки без самодельных обработчиков
Страницу настроек проще собрать на Settings API. Форма уходит на options.php, а он сам проверяет nonce и права пользователя, поэтому разбирать $_POST вручную не нужно. Добавьте в тот же файл:
function mysite_phone_register_settings() {
register_setting( 'mysite_phone_group', 'mysite_phone', array(
'type' => 'string',
'sanitize_callback' => 'sanitize_text_field',
) );
}
add_action( 'admin_init', 'mysite_phone_register_settings' );
function mysite_phone_add_menu() {
add_options_page( 'Телефон', 'Телефон', 'manage_options', 'mysite-phone', 'mysite_phone_render_page' );
}
add_action( 'admin_menu', 'mysite_phone_add_menu' );
function mysite_phone_render_page() {
echo '<div class=\"wrap\"><form action=\"options.php\" method=\"post\">';
settings_fields( 'mysite_phone_group' );
echo '<label for=\"mysite_phone\">Телефон</label> ';
printf(
'<input type=\"text\" id=\"mysite_phone\" name=\"mysite_phone\" value=\"%s\">',
esc_attr( get_option( 'mysite_phone', '' ) )
);
submit_button();
echo '</form></div>';
}Защит здесь две. sanitize_callback чистит значение при сохранении, esc_attr и esc_html экранируют при выводе. Одной мало: в базу значение может попасть и в обход вашей формы.
Хук (hook) — точка в ядре, куда плагин подключает свою функцию. Действия (add_action) что-то выполняют, фильтры (add_filter) принимают значение и возвращают изменённое.
Деактивация — не удаление
Отключение не должно стирать данные: владелец мог выключить плагин на минуту ради проверки. Очистку делают при удалении, в файле uninstall.php в корне плагина. WordPress запускает его, только когда плагин удаляют из админки.
<?php
// Файл uninstall.php в корне плагина. Запускается при удалении из админки.
if ( ! defined( 'WP_UNINSTALL_PLUGIN' ) ) {
exit;
}
delete_option( 'mysite_phone' );Плагин, который нельзя аккуратно удалить, остаётся на сайте навсегда: его боятся трогать.
Когда что выбирать
Критерий один: должна ли функция остаться на месте после смены темы?
- Да (шорткоды, типы записей, интеграции, счётчики): обычный плагин. Это выбор по умолчанию.
- Нет, это оформление (размеры миниатюр, подключение шрифтов): functions.php дочерней темы.
- Да, и отключать код нельзя, а настройки и активация не нужны (служебные фильтры, запреты на проде): mu-plugins.
Что ломается
Fatal error: Cannot redeclare mysite_phone_shortcode(): имя функции занято другим плагином или темой. Нужен уникальный префикс илиnamespace.Cannot modify header information - headers already sent: перед<?phpпробел или BOM, либо после?>перенос строки. Сохраняйте файл в UTF-8 без BOM._load_textdomain_just_in_time was called incorrectly: уведомление появилось в WordPress 6.7. Перевод запрошен раньше хукаinit, например вызовом__()в теле файла. Переносите такие вызовы внутрь функций, которые срабатывают позже.- Свой обработчик формы без nonce и проверки прав или запрос к базе с переменной прямо в строке. Первое закрывают
check_admin_referer()иcurrent_user_can(), второе —$wpdb->prepare().
Что делать дальше
На тестовой копии включите в wp-config.php константы WP_DEBUG и WP_DEBUG_LOG (true), а WP_DEBUG_DISPLAY выключите (false), затем пройдите по страницам, где работает плагин. Ошибки попадут в wp-content/debug.log; цель — ни одной строки от вашего плагина. После этого прогоните код через плагин Plugin Check из каталога WordPress.org: он проверяет требования каталога, включая безопасность и переводы. Блоки Gutenberg и REST-эндпоинты — отдельные темы, здесь не разбираем. Остальное — в Plugin Handbook.
Создатель презентаций: Структура и текст каждого слайда: заголовки-утверждения, одна мысль на слайд, заметки докладчику.
Спросить за 150 ₽Источники: Plugin Handbook, WordPress Developer Resources, Security, WordPress Developer Resources
Частые вопросы
Что делать, если у меня другая версия WordPress или PHP?
Плагин вызвал белый экран. Как вернуть сайт?
Нужен ли префикс, если я пишу код в классах?
Что проверить перед выкладкой на продакшен?
Я отключил плагин, а настройки остались. Это ошибка?
А если плагин не появился в списке или не активируется?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.