Увлекшись написанием плагинов для Wordpress'а составил правила хорошего тона…
Соглашение по именованию
С чего начинается плагин — с имени :), следовательно давайте вырабатаем правила именования плагинов:
- Не используем тупых префиксов вида wp_ иль wp- — мы и так знаем что файлы в каталоге http://wordpress.org/extend/plugins/ предназначены для wordpress'a
- Если хотите выделить Ваш плагин — добавьте оригинальный префикс/постфикс (я использую префикс (a) — правда не знаю насколько сие информативно)
- Все имена классов и функций должны содержать имя Вашего плагина — дабы избежать конфликтов
Всегда создавайте директорию с плагином, даже если он состоит из одного файла, возможно в дальнейшем Вы захотите расширить функционал, и вот уже одним файлом не обойтись, и создадите директорию — а это может ввести в ступор пользователей…
readme.txt
Обязательным для каждого плагина есть наличие файла readme.txt, см. описание синтаксиса http://daringfireball.net/projects/markdown/syntax. Проверить Ваше творение можно используя валидатор.
Если Вы по каким-то причинам не заливаете свой плагин в репозиторий wordpress'a — то в любом случае создайте данный файл — многие скажут спасибо.
Заголовок
Это обязательный элемент плагина, не надо в нем сильно извращаться:
<?php
/*
Plugin Name: Name Of The Plugin
Plugin URI: http://URI_Of_Page_Describing_Plugin_and_Updates
Description: A brief description of the Plugin.
Version: The Plugin's Version Number, e.g.: 1.0
Author: Name Of The Plugin Author
Author URI: http://URI_Of_The_Plugin_Author
*/
?>
Стандарты кодирования
Полноценных стандартов от разработчиков я не видел — по этой причине использую стандарты Zend Framework'a, чего и Вам советую. (в примерах я не буду использовать коментарии для PHP Documentator'а — дабы сократить листинг сорцов).
И еще — наш плагин не должен вызывать ошибок (даже уровня Notice), так что при разработке включите отображение ошибок:
error_reporting(E_ALL);
Динамическая подгрузка файлов
Подгружать сразу весь плагин, затем повесить все атцать хуков и ничего не сделать — такое поведение плагинов встречается часто, давайте будем умнее, для начала желательно убрать весь функционал по классам и файлам, и уже в функциях подгружать необходимые файлы:
// добавим фильтр контента
add_filter('the_content', array('%PluginName%', 'the_content'), 1000);
class %PluginName% {
var $some_variable;
/**
* filter fo the_content
*
* @return void
*/
function the_content($content)
{
include_once 'class/Content.php';
$Content = new %PluginName%_Content();
return $Content->parseContent($content);
}
}
Так же желательно вешать хуки отдельно для каждого состояния — см. список:
if (is_admin()) {
// хуки для админки
} else {
// хуки для фронт-енда
}
// и так далее ...
Можно даже так:
if (is_admin()) {
include_once '%PluginName%_admin.php';
} else {
include_once '%PluginName%_front.php';
}
// и так далее ...
Переменные и пространство имен
Поскольку пространство имен в PHP еще не реализовано (имеются ввиду стабильные версии), то с данной задачей нам поможет справиться статический класс объединяющий в себе все функции для хуков:
add_action('%hook_name%', array('%PluginName%', '%hook_name%'));
class %PluginName% {
var $some_variable;
/**
* some function description
*
* @return void
*/
function %hook_name%()
{
// ...
}
}
Или же обычный класс:
// создаем сущность нашего класса
$PluginName = new %PluginName%();
add_action('%hook_name%', array($PluginName, '%hook_name%'));
class %PluginName% {
var $some_variable;
/**
* some function description
*
* @return void
*/
function %hook_name%()
{
// ...
}
}
// удаляем переменную за ненадобностью
unset($PluginName);
При использование таблицы options (это функции add_option, update_option, delete_option) следует так же использовать префикс из имени плагина, таким образом мы будем эмулировать namespace наших опций (по какой причине до этого не додумались разработчики я не знаю)…
Следуя данным советам мы избежим конфликтов с другими плагинами…
Установка плагина
Для инициализации системы есть хук register_activation_hook, используя его Вы сможете внести необходимые измения в БД (создать таблицы, внести изменения в options и т.д.). Поверьте — пользователь не всегда читаем readme.txt где будет написано, что необходимо после инициализации обязательно сохранить настройки плагина дабы значения по умолчанию были сохранены в БД…
Настройки плагина
Давайте не будет ломать красивую админку Wordpress'a — настройки плагина должны быть расположены в соответствующем меню — Settings » %Plugin Name%.
Так же советую вынести страницу с настройками в отдельный файл с информативным названием (к примеру %PluginName%_options.php либо %PluginName%_settings.php):
if (is_admin()) {
add_action('admin_menu', array('%PluginName%', 'adminMenu'));
}
class %PluginName% {
function adminMenu()
{
if (function_exists('add_options_page')) {
add_options_page('%PluginName%','%PluginName%', 'manage_options', '%PluginName%/%PluginName%_options.php') ;
}
}
}
Чтобы всё было красиво — используйте стили прописанные в wp-admin/wp-admin.css — за такой подход Вам скажут спасибо…
Деактивация плагина
Если Вы при установке плагина вносите какие-либо изменения в БД или на файловой системе — то желательно подчистить сие после отключения плагина, в этом Вам поможет хук register_deactivation_hook.
Кастомизация
Добавляем CSS и JavaScript используя следующую конструкцию:
// регистрируем наш CSS файл
wp_register_style('%PluginName%', get_option('siteurl') . '/wp-content/plugins/%PluginName%/%PluginName%.css');
// либо
wp_enqueue_style('%PluginName%', get_option('siteurl') . '/wp-content/plugins/%PluginName%/%PluginName%.css');
// регистрируем наш JS файл (с указанием зависимостей)
wp_register_script( '%PluginName%', get_option('siteurl') . '/wp-content/plugins/%PluginName%/%PluginName%.js', array('jquery'));
// либо
wp_enqueue_script( '%PluginName%', get_option('siteurl') . '/wp-content/plugins/%PluginName%/%PluginName%.js', array('jquery'));
Этот способ сработает отлично в том случае если данные методы будут находиться в хуке на wp_head либо admin_head, иначе вам самим надо будет вызвать метод wp_print_styles или wp_print_scripts, и передать им имя скрипта для вывода…
Так же не забываем о конфликтах, как их обойти почитайте в статье How to load JavaScript in WordPress plugins
Если Ваш плагин имеет некое графическое оформление — и оное обычно изменяют под конкретную тему — то желательно сделать проверку на наличие CSS файла для нашего плагина в директории текущей темы:
if (file_exists(TEMPLATEPATH.'/%PluginName%.css')) {
wp_register_style('%PluginName%', get_bloginfo('template_directory') . '/%PluginName%.css');
} else {
wp_register_style('%PluginName%', get_option('siteurl') . '/wp-content/plugins/%PluginName%/%PluginName%.css');
}
При создании CSS будьте очень внимательны, Ваш CSS файл не должен ломать текущий дизайн, так что опять — используйте либо префиксы, либо жесткую привязку:
.%PluginName%-sidebar { /*...*/ }
#%PluginName% { /*...*/ }
#%PluginName% > div { /*...*/ }
Не стоит так же забывать о том, что на внешний вид Вашего плагина может влиять CSS файл текущей темы — так что советую подчистить margin'ги, padding'и и border'ы…
Вот такими нехитрыми приемами мы облегчим жизнь себе и дизайнерам…
Мультиязычность
Не планируете делать мультиязычность, но тогда дайте возможность другим помочь Вам, подготовьте плагин к переводу, для этого достаточно будет создать файл локализация содержащий лишь Ваш язык, да использовать следующие функции для вывода текста:
__('String', '%PluginName%');
_e('String', '%PluginName%');
_c('String', '%PluginName%');
__ngettext('String', 'Strings', $c, '%PluginName%')
Файлы перевода желательно так же помещать в отдельную директории language — дабы не засорять корневой каталог плагина…
Более подробную информацию смотрите на странице I18n for WordPress Developers
Документация
Если от пользователя требуется внести изменения в текущую тему — то желательно описать данный процес очень подробно — а не как обычно: «Вот этот код выведет то что Вы хотите».
Кстати «вот этот код», не должен вызывать ошибок если Ваш плагин будет отключен, так что не забываем обрамлять вызовы функций следующей конструкцией:
<?php
if (function_exists('%FunctionName%')) {
%FunctionName%();
}
?>
Помните — конечный потребитель зачастую не программист, и ему необходимо всё разжевать и в рот положить…
Совместимость
К сожалению Wordpress позиционирует себя как система с поддержкой PHP4, так что если Вы используете PHP5, то лучше заранее сообщить об этом пользователю на этапе включения плагина, либо создайте файл для обеспечения совместимости (если вы используете лишь какие-либо специфичные функции):
// подключаем в файле плагина
if (version_compare(phpversion(), '5.1.0', '<')) {
requery_once '%PluginName%_compatibility.php'
}
// что может быть в файле
if (!function_exists('array_diff_key')) {
function array_diff_key()
{
$args = func_get_args();
return array_flip(call_user_func_array('array_diff',
array_map('array_flip',$args)));
}
}
Дабы не изобретать велосипедов — советую посмотреть на пакет PEAR PHP_Compat.
Вполне вероятно Вам может так же понадобиться поддержка старых версий Wordpress'a:
// подключаем в файле плагина
if (version_compare(get_bloginfo('version'), '2.6.0', '<')) {
requery_once '%PluginName%_compatibility.php'
}
Выводы
Подведу итого.
Директория плагина может выглядеть следующим образом:
\plugin-name
|--\languages
|--\library
|--\javascript
|--\css
|-- plugin-name.php
|-- plugin-name_admin.php
|-- plugin-name_front.php
|-- plugin-name_settings.php
|-- plugin-name_compatibility.php
|-- readme.txt
- префикс %PluginName% не обязателен, и при большом количестве файлов даже избыточен
- languages — все переводы будут лежать тут
- library — директория для сторонних библиотек
- javascript и css — содержат javascript и css файлы для вашего плагина
- readme.txt — обязательно
- функционал разнесен по нескольким файлам (admin.php, front.php, single.php и т.д.)
- обеспечена совместимость версий (compatibility.php)
P.S. Если у Вас есть что добавить, либо есть ссылка на полезные ресурсы по теме — милости прошу в комментарии…
При подготовке материала были использованы следующие ресурсы:
Для подстветки синтаксиса кода использован ресурс highlight.hohli.com