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 документации и комментариев".

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

Ссылки

🌱 Помогите нам сделать документацию лучше!

Вся документация Druki с отрытым исходным кодом. Нашли ошибку или неточность? Создайте pull request.

Редактировать текущий документ Обсудить улучшение

Или узнайте как контрибутить.

🤔 По-прежнему нужна помощь?

Не нашли ответа на свой вопрос? Попросите помощи у сообщества!

Задайте вопрос на GitHub Смотрите другие ресурсы сообщества