Здравствуйте, уважаемые читатели, сегодня предлагаю затронуть очень редкую тему, информации о которой в интернете, кране не хватает. Говорить мы будем о том, как самостоятельно сделать систему плагинов для своей CMS.
Проработав некоторое время с движком WordPress, я не мог не оценить его механизм взаимодействия плагинов и ядра, который основан на системе хуков (Hooks – с англ. крючек, зацепка). Опираясь именно на эту концепцию системы плагинов, я решил разработать ее аналог в своей CMS.
PHP hooks – хуки и их концепция
Система хуков как нельзя лучше подходит для расширения функционала CMS, поскольку позволяет довольно гибко оперировать функциями движка, меняя их логику и поведение. Поскольку мне не довелось встретить толкового и обширного объяснения природы плагинов на основе хуков, я попробую изложить ее смысл, настолько – насколько сам его понимаю.
Предлагаю рассмотреть, что такое php hooks, и как их реализовать на простом примере.
В упрощенном виде, система хуков в PHP может выглядеть таким образом:
Имеется главный класс Main, в котором реализована вся логика приложения.
Имеется файл plugin.php, содержащий обработчики событий, которые должны произойти классе Main. Например, событием может являться выполнение метода __construct().
Также есть файл index.php, объединяющий эти два файла в одну систему.
Запуская index.php инклудом подключается файл plugin.php, и создается экземпляр класса Main.
В коде метода __construct() класса Main создаются хук (зацепка), для обработчика.
В момент создания хука, выполняется тело функции из класса файла plugin.php, тем самым изменяя обычное создание экземпляра класса Main.
В общих чертах система хуков именно так и должна работать, но это далеко не все ее возможности. С помощью хуков в php можно полностью изменить тело функции, в которой был создан хук, а можно лишь повлиять на результат, перед тем как она его вернет, также можно передать в исходную функцию другие параметры. Все это позволяет сделать систему неимоверно гибкой.
Но за такую гибкость приходится платить производительностью системы в целом, поэтому перед реализацией в php системы хуков, советую хорошенько подготовиться теоретически.
Как реализовать Hooks
На самом деле система хуков намного сложнее, чем приведенная ниже, но на первом этапе знакомства будет вполне достаточно рассмотреть в миниатюре механизм взаимодействия плагинов и ядра.
Давайте начнем разбираться с файла index.php
<?php
//Флаг использования плагинов
$plugin = true;
//подключаем библиотеку для хуков
include 'libHooks.php';
// Инициализация менеджера плагинов, он будет следить за произходящими событиями
$PM = new PM();
//Подключение плагина
if($plugin) {
include 'plugin.php';
}
//псевдо ядро системы
class Core{
public function __construct(){
echo "...Действия ядра...";
// Используем глобальный менеджер плагинов
global $PM;
//Происходит событие demoHook, где-то в недрах ядра
$PM->createHook('demoHook');
echo "...Действия ядра...";
}
}
//Эмуляция запуска ядра системы
$core = new Core();
В данном скрипте происходит:
подключение плагина;
подключение библиотеки хуков;
создание менеджера плагинов – экземпляр класса который предназначен для связи плагина с ядром систмы;
эмуляция запуска дейсвий ядра.
Обратите внимание на конструктор класса Core, в нем происходит инициализаци хука.
Следующая строка, создает действие с именем ‘demoHook’
$PM->createHook('demoHook');
Если ранее в плагине был зарегистрирован обработчик данного события, то менеджер плагинов $PM найдет его и выполнит необходимую пользовательскую функцию.
Откроем из архива файл plugin.php
<?php
//пользовательская функция выполняющаяся при определенном событии
function myFunction(){
echo "<br/> >>>Действия плагина<<< <br/>";
}
//регистрируем пользовательскую функцию как обработчика для события demoHook
$PM->registration(new EventHook('demoHook', 'myFunction'));
Этот файл представляет собой аналог плагина, в котором имеется функция myFunction(), назначением которой является расширение стандартного функционала системы.
В плагине с помощью системы хуков (hooks) , мы должны привязать пользовательскую функцию к определенному событию, которое может произойти в ядре в любой момент и не единожды.
Строка:
$PM->registration(new EventHook('demoHook', 'myFunction'));
Регистрирует нашу функцию в качестве обработчика, для события demoHook. Теперь когда бы не произошло событие demoHook всегда будет выполнено тело функции myFunction().
Запустив файлы архива, мы увидим такой результат:
Как устроена система хуков
В выше приведенном примере использовалась библиотека для механизма крючков и зацепок, мы подключали ее строкой:
include 'libHooks.php';
Давайте откроем этот файл и посмотрим, что же в нем хранится, и как все это работает.
<?php
/**
* Библиотека хуков, содержит интерфейсы PluginManager и Hook,
* а также класс PM и EventHook
* Класс PM предназначен для управления плагинами
* Класс Hook создает обработчик событий
*
* Автор: Авдеев Марк
* Ссылка на описание: http://lifeexampl.nichost.ru/php-primeryi-skriptov/php-sistema-plaginov-na-osnove-hukov-hooks.html
*/
//Интерфейс для слушаемого
interface PluginManager {
function registration(Hook $hook);
function delete(Hook $hook);
function createHook($hookName);
}
//Интерфейс для слушателя
interface Hook {
function run(PluginManager $hookName);
}
// Клас PM (plugin Manager) управляет плагинами,
// регистрирует плагины и устанавливает их взаимодействие с системой.
class PM implements PluginManager{
// Заристрированные обработчики хуков
private $_eventHook;
public function __construct(){
$this->_eventHook = array();
}
// регистрация обработчика для хука
public function registration(Hook $eventHook){
$this->_eventHook[] = $eventHook;
}
// удаление заданного обработчика
public function delete(Hook $eventHook){
if($id = array_search($eventHook, $this->_eventHook, TRUE)){
unset($this->_eventHook[$id]);
}
}
// Если в программе создан hook, сказать об этом всем
// обработчикам, вдруг кто-то из них ждет этого
// хука, для выполнения пользовательсякой функции.
public function createHook($hookName){
foreach($this->_eventHook as $eventHook){
if($eventHook->getHookName() == $hookName){
$eventHook->run($this);
}
}
}
}
//Вешает обработчик для заданного хука
class EventHook implements Hook{
// Наименование хука
private $_hookName;
// пользовательская функция, которая сработает при хуке
private $_functionName;
public function __construct($hookName, $functionName){
$this->_hookName = $hookName;
$this->_functionName = $functionName;
}
// Запуск обработчика для хука
public function run(PluginManager $pm){
if(function_exists($this->_functionName)){
call_user_func($this->_functionName);
}
}
public function getHookName(){
return $this->_hookName;
}
}
Вся логика взаимодействия хуков с обработчиками основана на паттерне observer, также известного как "Издатель и Подписчик". Я не буду сейчас описывать суть данного шаблона проектирования, поскольку делал это ранее.
Смысл заключается в том, что все пользовательские функции регистрируются для соответствующих действий классом PM в качестве обработчиков событий-хуков (EventHook).
Когда в системе происходит событие createHook("имя события – хука"), сгенерированное опять таки классом PM, то он как генератор имеет доступ к реестру пользовательских функций и начинает подбирать подходящую из них, для обработки текущего события.
В результате, зарегистрированные на этапе подключения плагинов функции, могут влиять на поведения любого из стандартных системных методов, а это полностью удовлетворяет задачу плагинов.
Вывод
Согласитесь, что самостоятельно придумать и спроектировать систему плагинов не простое занятие. Этот материал о системе плагинов и хуков, который я изложил в данной статье, должен лишь натолкнуть вас на правильное решение, но не стоит воспринимать его как законченную систему плагинов.
Надеюсь эта реализация системы хуков оказалась для вас настолько же интересной как и для меня.