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

Увлекшись написанием плагинов для WordPress’а составил правила хорошего тона…

Соглашение по именованию

С чего начинается плагин – с имени :), следовательно давайте вырабатаем правила именования плагинов:

  • Не используем тупых префиксов вида wp_ иль wp- – мы и так знаем что файлы в каталоге http://wordpress.org/extend/plugins/ предназначены для wordpress’a
  • Если хотите выделить Ваш плагин – добавьте оригинальный префикс/постфикс (я использую префикс (a) – правда не знаю насколько сие информативно)
  • Все имена классов и функций должны содержать имя Вашего плагина – дабы избежать конфликтов

Всегда создавайте директорию с плагином, даже если он состоит из одного файла, возможно в дальнейшем Вы захотите расширить функционал, и вот уже одним файлом не обойтись, и создадите директорию – а это может ввести в ступор пользователей…

readme.txt

Обязательным для каждого плагина есть наличие файла readme.txt, см. описание синтаксиса http://daringfireball.net/projects/markdown/syntax. Проверить Ваше творение можно используя валидатор.

Если Вы по каким-то причинам не заливаете свой плагин в репозиторий wordpress’a – то в любом случае создайте данный файл – многие скажут спасибо.

Заголовок

Это обязательный элемент плагина, не надо в нем сильно извращаться:

/*
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

Документация

Если от пользователя требуется внести изменения в текущую тему – то желательно описать данный процес очень подробно – а не как обычно: “Вот этот код выведет то что Вы хотите”.

Кстати “вот этот код”, не должен вызывать ошибок если Ваш плагин будет отключен, так что не забываем обрамлять вызовы функций следующей конструкцией:

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. Если у Вас есть что добавить, либо есть ссылка на полезные ресурсы по теме – милости прошу в комментарии…

При подготовке материала были использованы следующие ресурсы:

10 thoughts on “WordPress – стандарты кодирования плагинов”

  1. Добрый вечер Антон.
    У меня к вам 2 вопроса:
    *извените если вопросы глупые, т.к. в миру пресса я не долго)

    1. есть ли возможность в Прессе работать с ajax
    2. есть ли плагин, добовляющей подсветку РНР в посте? (мне не удалось найти)
    Спасибо.

  2. Кстати недоработка(не по теме):

    4 Responses to “Wordpress – стандарты кодирования плагинов” :

    Реально ведь 3 отзыва, а 4 – это всего-лишь пингбек или трекбэк. Почему так и не записать? 3 отзыва и 1 пингбек – меньше сбивает с толку ;)

  3. Вопрос:
    Как сделать автообновление плагина? Или достаточно запостить его на wordpress.org, чтобы это работало?

  4. Приветствую.
    Пытался написать простенький плагин, но сразу же сел “в лужу”…
    При подключении еще “пустого” плагина слетает кодировка в WP. Ни чем невозможно вылечить. Отключаю плагин – все нормализуется (показывает с нормальной кодировкой).
    Сначала писал в DreamWeaver, потом конвертировал (прочел о UTF-8 без BOM). Позже пытался с ноля написать в 100% адекватном к кодировкам редакторе – результат тот же.
    Может я еще чего-то не знаю – можно подсказать как решить данную проблему?

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.