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