Hooks (хуки) — подход в Drupal, позволяющий влиять на результаты, процессы и возможности системы средствами PHP функций.
¶Введение
Hook (хук) - специальным образом названая PHP функция, которая будет вызвана при ожидаемых для неё процессах. Хуки могут вызываться в разных ситуациях при разных условиях, но всегда известно когда ожидать вызов того или иного хука, а также то, что он требует или позволяет сделать и какие вводные параметры передаёт.
¶Типы хуков
Хуки можно разделить на два типа:
- Исполняемые хуки — хуки данного типа при вызове производят какие-то собственные действия или возвращают результат.
-
Альтер хуки — хуки данного типа всегда предназначены для изменения данных. Они используются для предоставления сторонним разработчикам возможности вмешаться в процесс и повлиять на результат или данные. Данные подлежащие изменению передаются в такие хуки по ссылке (
&).
¶Реализация хуков
Реализация хука (implementation) — это процесс использования конкретного хука в своём коде. В данном случае вы заранее знаете, как называется хук, что он передаёт, что делает и что ожидает от реализации.
Для реализации хука, вам необходимо создать функцию в соответствии с названием и сигнатурой хука. Данная информация всегда документируется модулем, который регистрирует и вызывает данный хук. Подобная информация находится в MODULENAME.api.php файле и содержит пример использования.
Хуки можно реализовывать как в модулях, так и в темах оформления.
В случае с модулем, хуки обычно реализуют в файле *.module, а в случае темы оформления в файле *.theme.
Хуки можно выносить в отдельные файлы, некоторые хуки поддерживают автоподгрузку файлов по своей группе, но для этого убедитесь, что файл, в котором вы реализуете хук, будет загружен в процессе обработки запроса, иначе хук не будет вызываться.
Вы можете встретить несколько вариантов именования хуков в зависимости от их типа и универсальности.
¶Реализация хуков вида hook_foo()
Хуки вида hook_foo() — стандартное именование исполняемых хуков. Для их реализации достаточно заменить hook на название вашего модуля.
Пример реализации хука hook_install(), который вызывается в процессе установки модуля в котором объявлен:
/**
* Implements hook_install().
*/
function example_install(): void {
\Drupal::messenger()->addMessage(new TransltableMarkup('Hello World!'));
}
¶Реализация хуков вида hook_foo_alter()
Хуки вида hook_foo_alter() — стандартное именование альтер хуков. Для их реализации до статочно заменить hook на название вашего модуля.
Пример реализации хука hook_form_alter(), который вызывается при подготовке любой формы с использованием Form API и позволяет изменять форму прежде чем она будет отрендерена:
/**
* Implements hook_form_alter().
*/
function example_form_alter(array &$form, \Drupal\Core\Form\FormStateInterface $form_state, string $form_id): void {
$form['antibot_checkbox'] = [
'#type' => 'checkbox',
'#title' => new TranslatableMarkup('Uncheck if you are not the bot.'),
'#default_value' => TRUE,
'#validate' => ['example_antibot_checkbox_validation'],
];
}
¶Реализация хуков вида hook_foo_BAR()
Хуки вида hook_foo_BAR() — специальное именование исполняемых хуков где BAR является динамической частью. Как правило, такие хуки делают одно и тоже, но при помощи динамической части сужают область своего применения.
Пример реализации хука hook_preprocess_HOOK(), который вызывается при подготовке шаблона HOOK:
/**
* Implements hook_preprocess_HOOK() for 'page--front.html.twig'.
*/
function example_preprocess_page__front(array &$variables): void {
$variables['foo'] = 'bar';
}
¶Реализация хуков вида hook_foo_BAR_alter()
Хуки вида hook_foo_BAR_alter() — специальное именование альтер хуков где BAR является динамической частью. Как правило, такие хуки делают одно и тоже, но при помощи динамической части сужают область своего применения.
Пример реализации хука hook_form_FORM_ID_alter(), который вызывается при подготовке формы FORM_ID:
/**
* Implements hook_form_FORM_ID_alter() for 'user_login'.
*/
function example_form_user_login_alter(array &$form, \Drupal\Core\Form\FormStateInterface $form_state, string $form_id): void {
$form['antibot_checkbox'] = [
'#type' => 'checkbox',
'#title' => new TranslatableMarkup('Uncheck if you are not the bot.'),
'#default_value' => TRUE,
'#validate' => ['example_antibot_checkbox_validation'],
];
}
¶Создание хуков
Создание хука — процесс регистрации нового хука в системе под свои нужды.
У хуков нет централизованного хранилища или менеджера, поэтому производить их вызов и обработку можно из любого модуля или функции в коде Drupal.
Ввиду особенностей работы хуков, их необходимо вызвать там, где они нужны и для какой цели они были созданы. Таким образом, их можно найти где угодно, и в каком угодно количестве.
Единственное что соединяет хуки с API - это сервисы module_handler, через которые и происходит вызов хуков.
Сервис module_handler отвечает за куда большее количество задач, чем просто хуки, при этом, хуки, вызванные через данный сервис, также и вызываются у тем оформления, а не только у модулей.
Данный сервис также делит хуки на два типа: исполняемые хуки и альтер хуки.
¶Нужно ли мне создавать хук?
Прежде всего, стоит определиться, действительно ли вам нужны именно хуки? Хуки в Drupal 9, это отчасти legacy подход, который пытаются убрать из ядра. Тем самым, это не самый предпочтительный способ добавить гибкости своему модулю.
Они могут быть легко заменены сервисами с метками, плагинами или же событиями. Все эти инструменты — ООП реализации на замену хукам с более конкретными целями и задачами.
Прежде чем использовать хуки, рассмотрите данные варианты, так как, скорее всего, правильнее всего делать с использованием одного, или нескольких перечисленных ранее инструментов.
Создание новых хуков — не самый лучший выбор в реалиях Drupal 9, тем не менее, он является поддерживаемым, и хуки, скорее всего, не исчезнут и в Drupal 10.
¶Регистрация (вызов) исполняемого хука
Для вызова исполняемых хуков данный сервис предоставляет два метода:
-
::invokeAll($hook, array $args = [])- вызывает все реализации хука$hook, передавая в качестве аргументов$args. -
::invoke($module, $hook, array $args = [])- вызывающий реализации хука$hook, передавая в качестве аргументов$args, но только для указанного модуля или темы$module.
Обратите внимание, что в качестве аргументов, передаваемых хуку, принимается массив $args, но каждый элемент данного массива, будет передан как отдельный параметр функции.
¶Пример простого вызова
Пример регистрации (вызова) хука hook_to_rule_them_all() для всех активных модулей и тем, что реализуют данный хук.
\Drupal::moduleHandler()->invokeAll('to_rule_them_all');Данный хук будет вызван, а что произойдет внутри - не важно модулю, который его вызывает.
¶Пример вызова с передачей аргументов
Пример регистрации (вызова) хука hook_to_rule_them_all($foo, $bar = NULL) с передачей двух аргументов.
$args = [
'Hello',
'World',
];
\Drupal::moduleHandler()->invokeAll('to_rule_them_all', $args);Все реализации хука получат в качестве аргумента $foo значение Hello, а для аргумента $bar - World.
¶Пример вызова и ожидание результата
Пример регистрации (вызова) хука hook_to_rule_them_all(), где ожидается, что реализация хука вернет что-то.
$results = \Drupal::moduleHandler()->invokeAll('to_rule_them_all');В результате данного вызова, переменная $results будет массивом, содержащим все ответы от реализаций данных хуков. Если ответов не было, или реализации отсутствуют, то массив окажется пустым.
¶Регистрация (вызов) альтер хука
Для альтер хуков в сервисе module_handler всего один метод alter().
Альтер хуки вызываются несколько иначе от исполняемых. Метод alter() принимает следующие аргументы:
-
$type: Название альтер хука. Приставка_alter()будет добавлена к названию автоматически. -
&$data: Аргумент, который будет передан в альтер хук первым по ссылке. -
&$context1: (опционально) Дополнительная переменная для передачи данных в хуки. -
&$context2: (опционально) Дополнительная переменная для передачи данных в хуки.
Таким образом, у альтер хуков ограниченное число переменных для передачи. Если вам не хватает 3 аргументов, рекомендуется использовать их в качестве ассоциативных массивов.
¶Пример вызова альтер хука
Пример регистрации хука hook_to_rule_them_all_alter($value).
$value_to_alter = 'Hello World!';
\Drupal::moduleHandler()->alter('to_rule_them_all', $value_to_alter);
¶Документирование хука
Ввиду специфики работы и вызова хуков, процесс вызова и регистрации с ними происходит хаотично. Поэтому в Drupal сообществе принято, что каждый вызываемый хук модулем, необходимо задокументировать в *.api.php файле модуля, который их и вызывает.
В данном файле вы должны описать каждый хук в виде функции, где её название равно названию хука, а также его аргументами. Вы должны написать документацию в комментарии, а в теле функции, привести пример использования.
Пример документации хука:
/**
* Allows you to alter some value.
*
* @param mixed $value
* The value to alter.
*/
function hook_to_rule_them_all_alter(&$value) {
if ($value == 'Hello World!') {
$value = 'World Hello!';
}
}Для более детальной информации обращайтесь к "Стандарты API документации и комментариев".
¶Смотрите также
¶Ссылки
- Drupal 8: hooks (+ YouTube), Niklan, 2018