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

Вы вставили в functions.php десять строк, которые регистрируют тип записей «Книги», и всё работает. Через полгода заказчик меняет тему, и раздел «Книги» исчезает из админки. Записи остались в базе, но WordPress больше не знает, что это за тип. Код никуда не делся: он остался в старой теме. Ниже четыре места, где может жить ваш PHP-код в WordPress 6.x, и цена каждого.
Четыре варианта и чем они отличаются
Сравниваем только место размещения. Как писать хуки и защищать плагин от уязвимостей, отдельные темы, здесь их не разворачиваем.
| Вариант | Где лежит | Переживёт смену темы | Выключается из админки | Хуки активации | Главный риск |
|---|---|---|---|---|---|
| functions.php дочерней темы | wp-content/themes/ваша-тема-child/ | Нет | Только сменой темы | Нет | Потеря кода при смене темы |
| Обычный плагин | wp-content/plugins/ | Да | Да | Да | Случайное отключение, конфликты имён |
| mu-плагин | wp-content/mu-plugins/ | Да | Нет | Нет | Ошибка роняет сайт, чинить только через файлы |
| Менеджер сниппетов | Обычно база данных | Да | Да, по каждому сниппету | Зависит от менеджера | Код вне git, PHP выполняют админы |
functions.php: быстро, но код привязан к теме
Вы выигрываете время: не нужен ни заголовок, ни отдельная папка. Платите тем, что функциональность сайта срастается с оформлением. Правило первое: только дочерняя тема. Если править functions.php родительской, обновление темы перезапишет файл.
// wp-content/themes/my-child/functions.php
add_action( 'init', function () {
register_post_type( 'book', array(
'label' => 'Книги',
'public' => true,
'show_in_rest' => true,
) );
} );Код рабочий, но тип записей теперь принадлежит теме. Здесь хорошо живёт то, что и правда про оформление: add_theme_support, add_image_size, подключение стилей и скриптов темы.
Обычный плагин: 20 строк обвязки ради переносимости
Плагин это папка в wp-content/plugins с главным PHP-файлом, в начале которого стоит комментарий-заголовок. Минимальный рабочий вариант для того же типа записей:
<?php
/**
* Plugin Name: Acme Books
* Description: Тип записей «Книги».
* Version: 1.0.0
* Requires at least: 6.0
* Requires PHP: 7.4
* License: GPL-2.0-or-later
*/
// Защита от прямого обращения к файлу
defined( 'ABSPATH' ) || exit;
function acme_books_register() {
register_post_type( 'book', array(
'label' => 'Книги',
'public' => true,
'show_in_rest' => true,
) );
}
add_action( 'init', 'acme_books_register' );
// Сбрасываем правила ссылок один раз при активации, а не на каждый запрос
function acme_books_activate() {
acme_books_register();
flush_rewrite_rules();
}
register_activation_hook( __FILE__, 'acme_books_activate' );
function acme_books_deactivate() {
flush_rewrite_rules();
}
register_deactivation_hook( __FILE__, 'acme_books_deactivate' );Цена вопроса: префикс acme_books_ у каждой функции и дисциплина с активацией. Взамен тип записей «Книги» остаётся при любой теме, а плагин можно положить в git и развернуть на другом сайте.
Вопрос не в том, что удобнее, плагин или functions.php. Вопрос в том, что должно пережить смену темы и что нельзя выключить по ошибке.
mu-плагин: то, что не должно отключаться
Must-use плагины лежат в wp-content/mu-plugins, загружаются автоматически и раньше обычных. Активировать их не нужно, и выключить кнопкой тоже нельзя. В списке плагинов они видны на отдельной вкладке «Must-Use». Подходит для вещей, отключение которых стало бы инцидентом.
<?php
// wp-content/mu-plugins/acme-hardening.php
defined( 'ABSPATH' ) || exit;
// Отключаем XML-RPC на всём сайте
add_filter( 'xmlrpc_enabled', '__return_false' );Что нужно знать заранее:
- WordPress подхватывает только файлы прямо в корне mu-plugins. Плагин в подпапке нужно подключить через файл-загрузчик с
require. - Хуки активации и деактивации не срабатывают: активации просто нет.
- Синтаксическая ошибка в таком файле роняет сайт целиком. Режим восстановления WordPress, который умеет приостанавливать обычные плагины и темы, на mu-плагины рассчитывать не стоит. Чинить придётся удалением файла по SFTP.
Менеджер сниппетов: для коротких правок без деплоя
Плагины вроде Code Snippets позволяют добавить фрагмент PHP из админки и включать его по отдельности. Это удобно, когда правка нужна сегодня, а доступа к файлам нет. Платите тем, что большинство менеджеров хранит код в базе: он не попадает в git, и его легко потерять при переносе. Проверьте в документации выбранного плагина, как работает экспорт. К тому же любой пользователь с правами администратора получает возможность выполнять PHP. Константа DISALLOW_FILE_EDIT блокирует встроенный редактор файлов, но сниппеты не затрагивает.
Когда что выбирать
- Код отвечает на вопрос «как выглядит сайт» (размеры картинок, поддержка возможностей темой, стили): дочерняя тема.
- Код отвечает на вопрос «что умеет сайт» (типы записей, шорткоды, интеграции, обработка форм): обычный плагин. Проверка простая: переключите тему на стандартную. Если функция должна остаться, это плагин.
- Код защищает или чинит сайт целиком, и его отключение стало бы аварией (закрыть XML-RPC, ограничения безопасности): mu-плагин.
- Фрагмент до 20 строк, нужен срочно, правит не разработчик: менеджер сниппетов, но с регулярным экспортом в репозиторий.
Что ломается
- Функция без префикса. Если имя совпадёт с функцией другого плагина, получите
Fatal error: Cannot redeclareи белый экран. - Регистрация типа записей вне хука
init. Тип не появится или WordPress выдаст предупреждение. - Забытый сброс правил ссылок. Записи нового типа отдают 404, пока вы не откроете «Настройки, Постоянные ссылки» и не нажмёте «Сохранить». Обратная крайность:
flush_rewrite_rulesна каждом запросе заметно нагружает сайт. - Нет проверки
ABSPATH. Файл можно вызвать напрямую по URL, минуя WordPress. - Совпадение имени папки с плагином из каталога wordpress.org. Сайт может начать предлагать обновление чужого плагина поверх вашего, поэтому выбирайте уникальное имя.
- Деактивация не удаляет данные. Чтобы убрать за собой, нужен файл uninstall.php или
register_uninstall_hook.
Что делать дальше
Возьмите functions.php вашей темы и пройдите по нему тестом смены темы. Всё, что должно остаться, вынесите в плагин с префиксом, положите в git и проверьте на тестовой копии. Включите в wp-config.php WP_DEBUG и WP_DEBUG_LOG на staging и посмотрите wp-content/debug.log после активации: предупреждения о хуках видны именно там. Следующий шаг: раздел Plugin Basics в документации разработчика, там заголовки, активация и удаление описаны для актуальных версий.
Хук (hook). Точка в ядре WordPress, к которой подключают свою функцию. Действие (action) выполняет код в нужный момент, например init. Фильтр (filter) принимает значение, меняет его и возвращает обратно, как xmlrpc_enabled.
Редактор изображений: Убрать лишнее, заменить фон, добавить объект, расширить кадр — на вашем изображении.
Спросить за 20 ₽Источники: WordPress Plugin Handbook: Plugin Basics, WordPress Plugin Handbook
Частые вопросы
Можно ли начать с functions.php, а потом перенести код в плагин?
Что если у меня другая версия WordPress или PHP?
Что делать, если плагин не появился в списке?
Что делать, если после активации сайт показывает белый экран?
Какие подводные камни ждут в проде?
Нужны ли ООП, Composer и автозагрузка для маленького плагина?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.