Как структурировать плагин
Этот вопрос не о том, как создать WordPress плагин. Скорее, о том, какие руководства можно применить к организации файловой архитектуры любого плагина.
Некоторые другие языки программирования или библиотеки имеют строго контролируемые способы организации директорий и файлов. Иногда это раздражает и подчеркивает свободу, которую предоставляет PHP, но с другой стороны, WordPress плагины собираются любым способом, определенным их автором.
Здесь нет правильного ответа, но я надеюсь усовершенствовать то, как я и другие создают плагины, чтобы сделать их более удобными для анализа другими разработчиками, проще для отладки, легче для навигации и, возможно, более эффективными.
Итоговый вопрос: как по вашему мнению лучше всего организовать плагин?
Ниже приведены несколько примеров структур, но это далеко не исчерпывающий список. Не стесняйтесь добавлять свои рекомендации.
Предполагаемая стандартная структура
/wp-content
/plugins
/my-plugin
my-plugin.php
Метод Model View Controller (MVC)
/wp-content
/plugins
/my-plugin
/controller
Controller.php
/model
Model.php
/view
view.php
my-plugin.php
Три части MVC:
- Модель взаимодействует с базой данных, выполняет запросы и сохраняет данные, содержит логику.
- Контроллер содержит теги шаблонов и функции, которые использует представление.
- Представление отвечает за отображение данных, предоставленных моделью и сконструированных контроллером.
Метод организации по типу
/wp-content
/plugins
/my-plugin
/admin
admin.php
/assets
css/
images/
/classes
my-class.php
/lang
my-es_ES.mo
/templates
my-template.php
/widgets
my-widget.php
my-plugin.php
WordPress Plugin Boilerplate
Доступно на Github
Основано на Plugin API, Стандартах кодирования и Стандартах документации.
/wp-content
/plugins
/my-plugin
/admin
/css
/js
/partials
my-plugin-admin.php
/includes
my_plugin_activator.php
my_plugin_deactivator.php
my_plugin_i18n.php
my_plugin_loader.php
my_plugin.php
/languages
my_plugin.pot
/public
/css
/js
/partials
my-plugin-public.php
LICENSE.txt
README.txt
index.php
my-plugin.php
uninstall.php
Свободный метод организации
/wp-content
/plugins
/my-plugin
css/
images/
js/
my-admin.php
my-class.php
my-template.php
my-widget.php
my-plugin.php

Обратите внимание, что все плагины являются "контроллерами" по стандартам WordPress.
Это зависит от того, что должен делать плагин, но во всех случаях я бы старался максимально отделить вывод на экран от PHP-кода.
Вот один из способов сделать это легко — сначала определите функцию, которая загружает шаблон:
function my_plugin_load_template(array $_vars){
// вы не можете позволить locate_template загружать ваш шаблон,
// потому что разработчики WP позаботились о том, чтобы вы не могли передавать
// переменные в ваш шаблон :(
$_template = locate_template('my_plugin', false, false);
// используйте шаблон по умолчанию, если тема его не имеет
if(!$_template)
$_template = 'views/template.php';
// загружаем его
extract($_vars);
require $_template;
}
Теперь, если плагин использует виджет для отображения данных:
class Your_Widget extends WP_Widget{
...
public function widget($args, $instance){
$title = apply_filters('widget_title', $instance['title'], $instance, $this->id_base);
// этот виджет показывает последние 5 "фильмов"
$posts = new WP_Query(array('posts_per_page' => 5, 'post_type' => 'movie'));
if($title)
print $before_title . $title . $after_title;
// здесь мы полагаемся на шаблон для отображения данных на экране
my_plugin_load_template(array(
// переменные, которые вы хотите передать в шаблон
'posts' => $posts,
));
print $before_widget;
}
...
}
Шаблон:
<?php while($posts->have_posts()): $posts->the_post(); ?>
<p><?php the_title(); ?></p>
<?php endwhile; ?>
Файлы:
/plugins/my_plugin/plugin.php <-- только хуки
/plugins/my_plugin/widget.php <-- класс виджета, если у вас есть виджет
/themes/twentyten/my_plugin.php <-- шаблон
/plugins/my_plugin/views/template.php <-- резервный шаблон
Где размещать CSS, JS, изображения или как проектировать контейнер для хуков — менее важно. Это, наверное, дело личных предпочтений.

Это зависит от плагина. Вот моя базовая структура для почти каждого плагина:
my-plugin/
inc/
Дополнительные PHP-файлы, специфичные для плагина, располагаются здесь
lib/
Классы библиотек, CSS, JS и другие файлы, которые я использую во многих
плагинах, находятся здесь
css/
js/
images/
lang/
Файлы переводов
my-plugin.php
readme.txt
Это будет чем-то, что должно находиться в папке lib
.
Если это особенно сложный плагин с большим количеством функционала в админ-панели, я бы добавил папку admin
для хранения всех этих PHP-файлов. Если плагин делает что-то вроде замены включенных файлов темы, может быть также папка template
или theme
.
Таким образом, структура каталогов может выглядеть так:
my-plugin/
inc/
lib/
admin/
templates/
css/
js/
images/
lang/
my-plugin.php
readme.txt

Вы также включите CSS и JS файлы администратора в папку /admin? Таким образом, создав еще /css и /js внутри /admin?

По моему скромному мнению, самый простой, мощный и удобный в поддержке подход — это использование MVC-структуры. WP MVC специально разработан, чтобы максимально упростить создание MVC-плагинов (хотя я могу быть немного предвзят...). С WP MVC вам достаточно создать модели, представления и контроллеры, а все остальное фреймворк берет на себя.
Вы можете создавать отдельные контроллеры и представления для публичной части сайта и административной панели. Весь фреймворк активно использует встроенные возможности WordPress. Структура файлов и большая часть функциональности точно такая же, как и в самых популярных MVC-фреймворках (Rails, CakePHP и др.).
Дополнительную информацию и руководство можно найти здесь:

Мы используем комбинацию всех методов. Прежде всего, мы используем Zend Framework 1.11 в наших плагинах, поэтому нам пришлось использовать аналогичную структуру для файлов классов из-за механизма автозагрузки.
Структура нашего основного плагина (который используется всеми нашими плагинами в качестве базы) выглядит примерно так:
webeo-core/
css/
images/
js/
languages/
lib/
Webeo/
Core.php
Zend/
/** файлы ZF **/
Loader.php
views/
readme.txt
uninstall.php
webeo-core.php
- WordPress вызывает файл
webeo-core.php
в корневой папке плагина. - В этом файле мы устанавливаем путь для включения PHP-файлов и регистрируем хуки активации и деактивации плагина.
- Также в этом файле у нас есть класс
Webeo_CoreLoader
, который устанавливает некоторые константы плагина, инициализирует автозагрузчик классов и вызывает метод setup классаCore.php
внутри папкиlib/Webeo
. Это выполняется на хуке действияplugins_loaded
с приоритетом9
. - Класс
Core.php
— это файл-загрузчик нашего плагина. Название основано на имени плагина.
Как видите, у нас есть подпапка внутри папки lib
для всех наших вендорных пакетов (Webeo
, Zend
). Все подпакеты внутри вендора структурированы по модулям. Например, для новой админ-формы Mail Settings
у нас будет следующая структура:
webeo-core/
...
lib/
Webeo/
Form/
Admin/
MailSettings.php
Admin.php
Core.php
Form.php
Наши под-плагины имеют такую же структуру, за одним исключением. Мы идем на уровень глубже внутри вендорной папки, чтобы избежать конфликтов имен во время события автозагрузки. Мы также вызываем класс-загрузчик плагина (например, Faq.php
) с приоритетом 10
внутри хука plugins_loaded
.
webeo-faq/ (использует/расширяет webeo-core)
css/
images/
js/
languages/
lib/
Webeo/
Faq/
Faq.php
/** все файлы классов, относящиеся к плагину **/
views/
readme.txt
uninstall.php
webeo-faq.php
В следующем релизе я, вероятно, переименую папку lib
в vendors
и перемещу все публичные папки (css, images, js, languages) в папку с названием public
.

Как уже многие здесь ответили, это действительно зависит от того, что должен делать плагин, но вот моя базовая структура:
my-plugin/
admin/
содержит все файлы для административной части
js/
содержит все JavaScript файлы для админки
css/
содержит все CSS файлы для админки
images/
содержит все изображения для админки
admin_file_1.php файл с функционалом админки
admin_file_2.php ещё один файл с функционалом админки
js/
содержит все JavaScript файлы для фронтенда
css/
содержит все CSS файлы для фронтенда
inc/
содержит все вспомогательные классы
lang/
содержит все файлы перевода
images/
содержит все изображения для фронтенда
my-plugin.php основной файл плагина с метаданными, в основном включает хуки действий и фильтров
readme.txt
changelog.txt
license.txt

Мне нравится следующая структура плагина, хотя обычно она меняется в зависимости от требований плагина.
wp-content/
plugins/
my-plugin/
inc/
Специфичные файлы только для этого плагина
admin/
Файлы для административных задач
lib/
Библиотеки/хелпер-классы
css/
CSS файлы плагина
js/
JS файлы
images/
Изображения для плагина
lang/
Файлы переводов
plugin.php
Главный файл, который подключает другие файлы
README
Обычно здесь указываю детали лицензии и полезную информацию
Я еще не создавал WordPress плагин, требующий архитектуры в стиле MVC, но если бы мне пришлось это сделать, я бы организовал структуру с отдельной директорией MVC, содержащей views/controllers/models.

Все мои плагины следуют этой структуре, которая очень похожа на то, что делают большинство других разработчиков:
plugin-folder/
admin/
css/
images/
js/
core/
css/
images/
js/
languages/
library/
templates/
plugin-folder.php
readme.txt
changelog.txt
license.txt
Файл plugin-folder.php обычно представляет собой класс, который загружает все необходимые файлы из папки core/. Чаще всего это делается на хуке init или plugins_loaded.
Раньше я добавлял префиксы ко всем своим файлам, но, как отметил @kaiser выше, это избыточно, и я недавно решил отказаться от этого в будущих плагинах.
Папка library/ содержит все внешние вспомогательные библиотеки, от которых может зависеть плагин.
В зависимости от плагина, в корне может также находиться файл uninstall.php. Однако чаще всего это обрабатывается через register_uninstall_hook().
Очевидно, что некоторым плагинам могут не требоваться файлы админки или шаблоны и т.д., но приведенная выше структура работает для меня. В конечном итоге вам просто нужно найти структуру, которая подходит именно вам, и придерживаться ее.
У меня также есть базовый плагин (starter plugin), основанный на приведенной выше структуре, который я использую как отправную точку для всех своих плагинов. Затем мне остается только выполнить поиск/замену префиксов функций/классов, и можно приступать к работе. Когда я еще добавлял префиксы к файлам, это был дополнительный шаг (довольно раздражающий), но теперь мне нужно просто переименовать папку плагина и его основной файл.

Моя логика: чем больше плагин, тем больше структуры я использую.
Для крупных плагинов я склоняюсь к использованию MVC.
Я использую это как отправную точку и пропускаю то, что не нужно.
controller/
frontend.php
wp-admin.php
widget1.php
widget2.php
model/
standard-wp-tables.php // если нужно, разделяю на части
custom-tabel1.php
custom-tabel2.php
view/
helper.php
frontend/
files...php
wp-admin/
files...php
widget1/
file...php
widget2/
file...php
css/
js/
image/
library/ //только php, в основном для Zend Framework, опять же если нужно
constants.php //часто использую
plugin.php //инициализирующий файл
install-unistall.php //только для больших плагинов

Также, смотрите этот отличный шаблон виджета для WP. Он дает полезные подсказки по структуре (даже если там нет отдельного класса или папки для моделей).

Менее распространённый подход к структурированию файлов и директорий плагина — группировка по типам файлов. Для полноты картины стоит упомянуть и его:
plugin-name/
js/
sparkle.js
shake.js
css/
style.css
scss/
header.scss
footer.scss
php/
class.php
functions.php
plugin-name.php
uninstall.php
readme.txt
Каждая директория содержит файлы только одного типа. Стоит отметить, что такой подход становится неудобным, когда у вас много файлов разных типов .png .gif .jpg
, которые логичнее было бы разместить в одной директории, например, images/
.

Я создал шаблонный репозиторий на GitHub для плагинов WordPress, вобравший более чем 10-летний опыт в структурированный проект!
https://github.com/EdwardBock/wordpress-plugin-starterkit
Шаблон соответствует стандартам PSR-4 для минимизации подключений строк и использует пространства имён для кратких названий классов, включая класс компонента шаблонизации. Также в комплекте идёт файл docker compose для разработки плагина в изолированной среде. Однако его можно просто поместить в существующий проект, где он также отлично работает.
Приятного изучения и не стесняйтесь вносить свой вклад!
