Только для читателей Lifeexample возможно открыть интернет-магазин на Moguta.CMS со скидкой в 15%
PHP система плагинов на основе хуков (hooks)
Здравствуйте, уважаемые читатели блога LifeExample, сегодня предлагаю затронуть очень редкую тему, информации о которой в интернете, кране не хватает. Говорить мы будем о том, как самостоятельно сделать систему плагинов для своей CMS.
Однажды, я где-то читал мысль, что каждый уважающий себя php программист, обязан, хотя бы попробовать сделать свою CMS. И на самом деле очень много, моих знакомых разрабатывали свои домашние системы управления сайтом. Вот и я не стал исключением, и сделал собственную CMS, которая в последствии вылилась в обособленный проект MOGUTA.CMS.
MOGUTA.CMS теперь имеет все задатки, для того, чтобы попытаться покорить сердца начинающих предпринимателей, дав им возможность комфортно управлять своими интернет-магазинами. Именно поэтому у меня давно уже зрело желание создать систему плагинов для своей 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 – файл запускающий псевдо систему;
- plugin.php – мини плагин;
- libHook.php – библиотека для механизма хуков.
Давайте начнем разбираться с файла index.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | <?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’
1 | $PM->createHook('demoHook'); |
Если ранее в плагине был зарегистрирован обработчик данного события, то менеджер плагинов $PM найдет его и выполнит необходимую пользовательскую функцию.
Откроем из архива файл plugin.php
1 2 3 4 5 6 7 8 9 | <?php //пользовательская функция выполняющаяся при определенном событии function myFunction(){ echo "<br/> >>>Действия плагина<<< <br/>"; } //регистрируем пользовательскую функцию как обработчика для события demoHook $PM->registration(new EventHook('demoHook', 'myFunction')); |
Этот файл представляет собой аналог плагина, в котором имеется функция myFunction(), назначением которой является расширение стандартного функционала системы.
В плагине с помощью системы хуков (hooks) , мы должны привязать пользовательскую функцию к определенному событию, которое может произойти в ядре в любой момент и не единожды.
Строка:
1 | $PM->registration(new EventHook('demoHook', 'myFunction')); |
Регистрирует нашу функцию в качестве обработчика, для события demoHook. Теперь когда бы не произошло событие demoHook всегда будет выполнено тело функции myFunction().
Запустив файлы архива, мы увидим такой результат:
Как устроена система хуков
В выше приведенном примере использовалась библиотека для механизма крючков и зацепок, мы подключали ее строкой:
1 | include 'libHooks.php'; |
Давайте откроем этот файл и посмотрим, что же в нем хранится, и как все это работает.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | <?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, то он как генератор имеет доступ к реестру пользовательских функций и начинает подбирать подходящую из них, для обработки текущего события.
В результате, зарегистрированные на этапе подключения плагинов функции, могут влиять на поведения любого из стандартных системных методов, а это полностью удовлетворяет задачу плагинов.
Вывод
Согласитесь, что самостоятельно придумать и спроектировать систему плагинов не простое занятие. Этот материал о системе плагинов и хуков, который я изложил в данной статье, должен лишь натолкнуть вас на правильное решение, но не стоит воспринимать его как законченную систему плагинов.
Ну а если вам все же интересно как можно развить данную заготовку до полноценной системы плагинов, рекомендую ознакомиться с моим движком MOGUTA.CMS, в котором механизм хуков учитывает все атрибуты настоящих плагинов такие как:
- Регистрация плагина в системе;
- Активация плагина;
- Деактивация;
- Изменение результата системной функции;
- Изменение тела системной функции;
- Подмена параметров системной функции.
Также всех желающих разобраться с тем как устроен движок, и помочь в его дальнейшем развитии приглашаю сюда.
Надеюсь эта реализация системы хуков оказалась для вас настолько же интересной как и для меня.
Читайте также похожие статьи:
Чтобы не пропустить публикацию следующей статьи подписывайтесь на рассылку по E-mail или RSS ленту блога.
Комментарии
Ого Спасибо !
Подскажите пожалуйста, вы тут использовали паттерн Фабрика ?
Я вот смотрю на ваши статьи, и думаю, что у вас всё старомодно, не вызывающе, сделайте пару статей про автозагрузчик на SPL с применением логов и загрузкой карты подключения путей, пару статей про namespace, и одну большую статью про создание мощной модульной MVC системы с применеием автозагрузчика, и namespace… И так далее в том же духе можете писать статьи, пишите о новом, надоело читать одно и тоже старье. Хочется нового!!!
Спасибо, Влад. Обязательно сделаю статьи не старомодными)) Если вообще можно применить это слово к теоретическим материалам. Есть такой ресурс — хабрахабр, думаю там вы найдете много нового для себя.
Спасибо за статью, просто и информативно!
hookname.pre — Запускается перед функцией
hookname.post- Запускается после функцией
hookname.rewrite — Перезаписывает функцию
Вот так грамотно..
Отличный пример. А как это всё в ООП засунуть?)