Hooks (хуки)

Hooks (хуки) — подход в Drupal, позволяющий влиять на результаты, процессы и возможности системы средствами PHP функций.

Введение

Hook (хук) - специальным образом названая PHP функция, которая будет вызвана при ожидаемых для неё процессах. Хуки могут вызываться в разных ситуациях при разных условиях, но всегда известно когда ожидать вызов того или иного хука, а также то, что он требует или позволяет сделать и какие вводные параметры передаёт.

Типы хуков

Хуки можно разделить на два типа:

  1. Исполняемые хуки — хуки данного типа при вызове производят какие-то собственные действия или возвращают результат.
  2. Альтер хуки — хуки данного типа всегда предназначены для изменения данных. Они используются для предоставления сторонним разработчикам возможности вмешаться в процесс и повлиять на результат или данные. Данные подлежащие изменению передаются в такие хуки по ссылке (&).

Реализация хуков

Реализация хука (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.

Регистрация (вызов) исполняемого хука

Для вызова исполняемых хуков данный сервис предоставляет два метода:

  1. ::invokeAll($hook, array $args = []) - вызывает все реализации хука $hook, передавая в качестве аргументов $args.
  2. ::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 документации и комментариев".

Смотрите также

Ссылки

Помощь и обратная связь

Если вы обнаружили ошибку или хотите внести улучшения, и желаете внести изменения самостоятельно при помощи Pull Request
Если вы желаете предложить улучшение для этого документа

Обратиться за помощью

Если вы не нашли то что искали, воспользуйтесь поиском.

Если вам нужна помощь с чем-то конкретным, обратитесь к сообществу.