В прошлой статье я говорил об идее и плане. Поэтому обсудим идею и план, и только после этого перейдём непосредственно к реализации …
Начнём с идеи.
В WordPress существует функция шаблона comments_number, которая выводит количество комментариев для каждой статьи, а также используется в функции comments_popup_link для тех же самых целей. Не вдаваясь в технические подробности, можно сказать, что функция эта расчитана исключительно на англо-саксонские языки, имеющие лишь две формы числительного: один и много. Другие языки, имеющие большее количество форм числительных, данная функция обслуживает некорректно. Этот недостаток мы исправим с помощью небольшого плагина, а заодно, разберём как это делается. Это и есть идея, идея моего плагина Multilingual Comments Number.
Перейдём к плану.
Во-первых, мы должны решить какой метод программирования мы изберём. Лично я являюсь сторонником современных методов программирования и поэтому, обычно, выбираю ООП. Боюсь, что для Вас, уважаемый читатель, этот довод не является исчерпывающим, поэтому приведу иные доводы.
WordPress, несмотря на тяжёлое наследие прошлого (довольно большая часть написана методами процедурного программирования), постепенно выбирается в сторону полностью ООП-продукта. К примеру, совсем недавно был введён новый механизм написания и обслуживания виджетов – WordPress Widget Factory. Ещё одним доводом побуждающим программистов использовать ООП является огромное количество плагинов накопившихся в репозитарии WordPress. При таком обилии плагинов становится всё труднее давать имена функциям, которые не конфликтовали бы с уже имеющимися. В случае ООП реализации данная проблема снимается сама-собой. Обращать внимание необходимо лишь на название класса и переменной его представляющей. Что касается названий функций, то они становятся стандартными и внятными. Я в своих плагинах использую стандартные названия. Надеюсь этих доводов достаточно, чтобы запланировать написание плагина с помощью ООП.
А что у нас во-вторых? Во-вторых у нас ответ на самый главный вопрос планирования: Как? Как именно мы будем реализовывать нужную функциональность? Разумеется есть варианты. Например можно было бы перехватывать вывод функции comments_number своими средствами или написать заменяющую функцию. Однако, имеющийся механизм фильтров WordPress позволяет легко решить эту задачу, а возможности функций PHP Gettext дают возможность решить задачу для любого языка, а не только для русского, что весьма актуально в свете “парада суверенитетов” стран СНГ. Дело в том, что в механизм Gettext заложена не просто замена базовых фраз на локальные, но и учёт количества форм числительных. Таким образом, мы можем не просто не заботиться о количестве этих форм, но и переложить часть функциональности плагина на файлы локализации.
Ну вот, план составлен. Пора переходить к реализации плагина.
Произошла ошибка. Попробуйте ещё раз позднее. |
Итак, ООП. Создаём класс, естественно, предварительно проверив нет ли уже класса с таким же именем:
1 2 |
if (!class_exists('MultilingualCommentsNumber')) { class MultilingualCommentsNumber { |
Затем создаем несколько переменных (в нашем случае две) и конструктор класса:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
var $adminOptionsName = "MultilingualCommentsNumberAdminOptions"; var $mcnInitOptions = array('commentStringZero' => '', 'commentStringOne' => ''); function MultilingualCommentsNumber() { //load language $plugin_dir = basename( dirname( __FILE__ ) ); if ( function_exists( 'load_plugin_textdomain' ) ) load_plugin_textdomain( 'multilingual-comments-number', PLUGINDIR . $plugin_dir, $plugin_dir ); //Actions and Filters add_action('admin_menu', array(&$this, 'onAdminPage')); add_action('activate_multilingual-comments-number/multilingual-comments-number.php', array(&$this, 'onActivate')); add_action('deactivate_multilingual-comments-number/multilingual-comments-number.php', array(&$this, 'onDeactivate')); add_filter( 'comments_number', array( &$this, 'commentsNumber' ), 8, 2); } |
Из двух созданных переменных, первая ($adminOptionsName) задаёт имя для массива параметров плагина, под которым он будет храниться в базе данных, а вторая ($mcnInitOptions) – массив значений параметров задаваемых по умолчанию. До недавнего времени программисты сохраняли параметры как отдельные переменные. Однако параметры лучше сохранять как массив данных. И вот почему: при хранении параметров по отдельности, получение каждого параметра требует отдельного SQL запроса к базе данных, что серьёзно увеличивает нагрузку как на базу данных, так и на процессор, кроме того, каждый параметр занимает одну запись в базе, увеличивая её размер. В случае хранения параметров в виде массива все параметры хранятся в одной записи базы данных и их получение требует одного SQL запроса. Конструктор класса задан именем класса в стиле PHP4. В стиле PHP5 конструктор должен иметь зарезервированное имя _construct, однако, WordPress позиционируется как продукт для PHP4 и довольно много пользователей пользуются именно этой связкой. Если мы хотим чтобы нашим плагином пользовалось как можно больше блоггеров, необходимо придерживаться общих правил. Кроме того, нашему плагину не нужны расширенные возможности PHP5 и мы можем ими пренебречь.
Первым делом, в соответствии с рекомендациями разработчиков WordPress, загружаем TEXTDOMAIN, т.е. указываем WordPress, где и в каких файлах хранятся значения языково-зависимых строковых переменных. Затем определяем обработчики событий: actions и filters. Определяются эти обработчики одинаково: actions с помощюю функции add_action и filters с помощью функции add_filter. Параметры обоих функций идентичны: “имя события”, “имя функции обработчика”, “приоритет выполнения”, “количество принимаемых параметров”. Разница между этими обработчиками заключается в том, что actions определяют реакцию на системные события, а filters – на пользовательские. В нашем случае мы определяем реакцию на системное событие admin_menu (возникает при построении меню на административных страницах) и системные события возникающие при активации и деактивации нашего плагина. О событии admin_menu мы поговорим в следующей части этой статьи. Сейчас же разберём события возникающие при активации и деактивации плагина. С деактивацией всё более или менее понятно:
1 2 3 |
function onDeactivate() { delete_option($this->adminOptionsName); } |
При обработке этого события мы просто убираем мусор, ведь если пользователь деактивировал плагин, параметры плагина становятся мусором засоряющим базу данных.
Перейдём к обработке события activate.
1 2 3 4 |
function onActivate() { $mcnAdminOptions = $this->getOptions(); update_option($this->adminOptionsName, $mcnAdminOptions); } |
Обрабатывая событие activate мы записываем параметры в базу данных со значениями заданными по умолчанию. Для того чтобы получить заданные по умолчанию данные, мы вызываем функцию getOptions. На первый взгляд может показаться, что это довольно странный ход, что проще было бы записать в базу данных массив $mcnInitOptions, но не стоит торопиться с выводами. Для начала посмотрим код функции getOptions:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
function getOptions() { $mcnAdminOptions = $this->mcnInitOptions; $mcnOptions = get_option($this->adminOptionsName); if (!empty($mcnOptions)) { foreach ($mcnOptions as $key => $option) { $mcnAdminOptions[$key] = $option; } } if($mcnAdminOptions['commentStringZero'] === '') $mcnAdminOptions['commentStringZero'] = __( 'There are no comments', 'multilingual-comments-number' ); if($mcnAdminOptions['commentStringOne'] === '') $mcnAdminOptions['commentStringOne'] = __( 'One Comment', 'multilingual-comments-number' ); return $mcnAdminOptions; } |
Рассмотрим что делает эта функция.
Первым делом мы берём параметры заданные по умолчанию, затем загружаем параметры из базы данных и, если таковые имеются, записываем с помощью цикла в переменную с заданными по умолчанию параметрами. Затем проверяем по условию некоторые параметры (в данном случае все) и, если условие выполнено (в данном случае пустая строка), заменяем значение выбранных параметров на локализованную версию значения параметра по умолчанию. Другими словами, данная функция выполняет следующие задачи:
- первое и основное – получение массива сохранённых параметров из базы данных
- второе – получение массива параметров заданных по умолчанию с учётом текущей локализации плагина при его первичной активации
- третье – получение массива сохранённых параметров с расширением числа параметров (upgrade) при реактивации
Поясню третий пункт. Любой плагин развивается функционально, а это может потребовать увеличения числа сохраняемых параметров. При автоматическом upgrade и уж тем более при ручном, событие deactivate не возникает (для того, чтобы параметры пользователя не были потеряны), поэтому, в случае увеличения числа параметров, мы не можем просто загрузить старые параметры, так как это приведёт к ошибке исполнения плагина. Загрузка параметров заданных по умолчанию приведёт к потере уже проведённых пользователем настроек. Именно поэтому приходится использовать компромисный вариант, который будет работать при любых условиях и именно поэтому я использую функцию getOptions при обработке события activate.
Завершая разговор об actions, хотелось бы обратить ваше внимание на весьма важную деталь: в качестве первого параметра функции add_action добавляющей обработчики событий activate и deactivate используется название событий с суффиксом, в качестве которого выступает имя папки и название основного файла активируемого/деактивируемого плагина.
Пожалуй на сегодня достаточно. В следующей статье мы рассмотрим фильтры и основную функцию плагина (ради которой и “городился огород”), а так же подключение страницы параметров плагина и её автоматическую генерацию.
Продолжение следует …
© 2010 – 2014, minimus. Все права защищены. При копировании и републикации статьи, ссылка на первоисточник обязательна.