WordPress – стандарты кодирования плагинов
// PHP

Увлекшись написанием плагинов для 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 файл текущей темы – так что советую подчистить marging’и, 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', '<')) {
require_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_Compact.
Вполне вероятно Вам может так же понадобиться поддержка старых версий WordPress’a:
// подключаем в файле плагина
if (version_compare(get_bloginfo('version'), '2.6.0', '<')) {
require_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. Если у Вас есть что добавить, либо есть ссылка на полезные ресурсы по теме – милости прошу в комментарии…
При подготовке материала были использованы следующие ресурсы: