---
title: 'API хуков и разработка модулей'
url: 'https://graker.ru/news/2010/04/13/hooks_api'
language: ru
type: post
author: 'Роман Грачев'
published: '2010-04-13T16:07:07+04:00'
updated: '2017-03-13T13:09:45+03:00'
categories:
  - Друпальчики
  - '20 API за 20 дней'
---

# API хуков и разработка модулей

Перевел следующую часть цикла «20 API за 20 дней». В данной статье рассмотрен API хуков Друпала. Рассказано, что такое хуки и зачем они нужны. Кратко перечислены популярные хуки, показано, как реализовать хук в своем модуле. В конце статьи разъясняется, как создавать собственные хуки.

Оригинальная статья: [The Hooks API and Custom Modules](http://www.trellon.com/content/blog/hooks-api).
Автор: Мартин Валасек.

Далее — перевод.

##### API хуков и разработка модулей

Спросите кого-нибудь, что такое «хук» — по ответу сможете многое понять о человеке. Это слово используется в отношении пиратов, музыки, персонажей в историях у костра, воздухоплавания и т.д. (*hook в переводе означает крюк — прим. переводчика*) Так или иначе, это слово имеет особенный смысл в программировании и служит важной структурной концепцией большинства современных языков программирования и фреймворков.

Почти всем разработчикам под Друпал хорошо известна его система хуков. Хуки — это пусковые механизмы, срабатывающие, когда происходят особые события в процессе генерации страницы. Они способны модифицировать данные, когда их обрабатывают, и совершать другие действия при определенных условиях. Некоторые из обычных хуков срабатывают, когда загружается нода, генерируется форма или кто-то сохраняет пользовательские данные. В этой части цикла «20 API на 20 дней» рассматривается сущность хуков, и как они функционируют, являясь основой большинства модулей.

###### Что делает API хуков?

Для тех, кто знаком с объектно-ориентированным программированием, хуки концептуально связаны с основной идеей этой методики. Они представляют стандартные способы взаимодействия со структурами данных, которые действуют независимо от отдельных обратных вызовов. В отличие от традиционных ООП-конструкций, хуки не привязаны к отдельным объектам, они привязаны к процедурным точкам в процессе генерации страницы. Также многие хуки зависят от состояния и выполняют разные операции в зависимости от того, что именно произошло, когда хук был вызван.

Для тех, кто достаточно разумен, чтобы избежать холивара на тему «Что такое ООП», вот что действительно нужно знать о работе API хуков Друпала:

- Хук — это PHP-функция, реализованная в модуле, она исполняет код в ответ на события или условия, произошедшие во время генерации страницы.
- Хуки позволяют модулям взаимодействовать с другими модулями, включая модули из ядра Друпала.
- API хуков задает стандарт именования для хуков, что позволяет разработчикам реализовывать хуки в модулях без нужны во всю ширь заниматься программированием.
- API хуков позволяет разработчикам создавать собственные хуки, к которым могут получить доступ другие модули, с помощью той же техники, что и в ядре Друпала.
- Многие хуки зависят от состояния и действуют по-разному в зависимости от того, что Друпал делал, когда сработал хук.

Важно понимать, что эта модель позволяет разработчикам модифицировать функционирование Друпала множеством мелких способов, вместо того, чтобы расписывать весь код в одном месте. Если API хуков под рукой — значит кто-то может добавить к Друпалу модуль и расширить функционал системы без необходимости переписывать какой-либо код. Именно это мы имеем в виду, когда говорим, что Друпал — модульный.

Теперь давайте посмотрим с другой стороны. В ядре Друпала есть хук под названием hook\_nodeapi. Он запускается всякий раз, когда на сайте загружается нода (и в других случаях, мы в них еще покопаемся). Загрузка ноды — это то, что мы назовем событием, оно заставляет hook\_nodeapi просмотреть все установленные модули и найти те, где зарегистрирован hook\_nodeapi. Как домино, Друпал будет запускать код, содержащийся в каждой функции hook\_nodeapi каждого модуля, и в результате получится полностью сформированная нода, готовая к показу пользователям.

К примеру, модуль location использует hook\_nodeapi, чтобы добавить информацию об адресах к нодам, когда они загружаются. Он также сохраняет информацию об адресах, при сохранении ноды. Именно это мы имеем в виду, когда говорим, что многие хуки зависят от состояния: они знают, сохраняете ли вы что-то, или загружаете, или удаляете и т.п.

Несмотря на важность предмета, иногда документации о многих хуках Друпала оказывается ужасно мало и это превращает изучение хуков в проблему. Для большинства людей путь к пониманию хуков лежит через исследования чужого кода и самостоятельную реализацию хуков в своих проектах. Но есть и те немногие, кто по-настоящему понимает, что делают хуки в ядре Друпала и в чем смысл их взаимодействий. Когда мы в Trellon нанимаем разработчиков, то задаем им серию вопросов о хуках с возрастающей сложностью. За шесть лет собеседований никто ни разу не дал удовлетворительного ответа о том, что делает hook\_comment — несмотря на то, что это достаточно очевидно, да и хук существует уже давно. Это подтверждает предыдущее утверждение о том, как люди изучают хуки: у них нет особых причин реализовывать hook\_comment и поэтому редко кто хоть что-то о нем знает.

###### Как распознать хук на природе

У хуков есть стандарт именования, который легко распознать, если знать как он выглядит.

Для регистрации хука разработчики используют PHP-функцию со специальным именем. Это специальное имя составляется так:

- имя модуля;
- подчеркивание;
- имя хука.

Например, если у кого-нибудь есть модуль **foo** и нужно реализовать **hook\_bar**, нужно поместить в модуль foo функцию под названием **foo\_bar**. Так что реализовать хук не очень-то и сложно.

Передача подходящих данных, однако — другой вопрос. Каждый хук — это PHP-функция, у которой есть аргументы для передачи данных. Многие хуки вдобавок передают переменные как ссылки, чтобы можно было модифицировать данные перед возвратом их Друпалу для дальнейшей обработки.

Типичная ошибка разработчика модулей — передача неправильного набора аргументов. Подсказка: очень удобно держать [api.drupal.org](http://api.drupal.org) открытым в отдельном окне и копировать прямо оттуда примеры из документации по хуку. Это может сэкономить кучу времени начинающим разработчикам.

Есть ряд хуков, используемых чаще других. Они — ключевые в создании модулей. Изучение реализаций этих хуков в любом модуле расскажет вам немало о том, что модуль делает и как. В их ряд входят:

- **hook\_help** — создает текст подсказки, связанный с вашим модулем.
- **hook\_perm** — создает набор разрешений для модуля.
- **hook\_menu** — определяет пункты меню и функции обратного вызова для генерации страниц.
- **hook\_menu\_alter** — позволяет изменять данные, сохраняемые в таблицу {menu\_router} после вызова хуков hook\_menu.
- **hook\_nodeapi** — срабатывает при работе с нодами, определенными в других модулях.
- **hook\_block** — создает блок или набор блоков.
- **hook\_cron** — используется для выполнения тех или иных действий при запуске крона.
- **hook\_user** — срабатвает при совершении действий с учетными записями пользователей.
- **hook\_form** — генерирует форму редактирования ноды.
- **hook\_form\_alter** — осуществляет изменения формы перед ее отображением.
- **hook\_form\_FORM\_ID\_alter** — позволяет вместо глобальных изменений изменять каждую форму отдельно.
- **hook\_theme** — регистрирует реализации темизирующих функций модуля (или темы).
- **hook\_link** — задает внутренние ссылки Друпала.
- **hook\_link\_alter** — производит изменения перед отображением ссылок ноды.

По адресу [http://api.drupal.org/api/group/hooks](http://api.drupal.org/api/group/hooks "http://api.drupal.org/api/group/hooks") доступен полный список хуков ядра Друпала. Для продвинутых разработчиков также очень важно понимать взаимодействия некоторых хуков. Например, hook\_db\_rewrite\_sql — используется достаточно часто и, в общем, реализуется для поддержки ограничений доступа к содержимому сайта. Это один из тех хуков, за которыми нужно приглядывать, так как модули, реализующие его, могут оказаться несовместимыми друг с другом. Например, и Organic Groups, и Domain Access используют hook\_db\_rewrite\_sql для управления доступом к содержимому, и есть ситуации, в которых эти модули не могут работать вместе. Именно это мы имеем в виду под «взаимодействиями» — то, что два установленных модуля, создающих ограничения доступа, могут серьезно запутать работу.

###### Как реализовать хук

Реализовать хук довольно легко — нужно просто дать ему правильное имя, передать правильные аргументы и запустить в нем необходимый вам код. Некоторые модули могут собрать для вас начальный набор хуков (мы не хотим их здесь рассматривать, погуглите — найдете).

Вот несколько примеров хуков в действии.

**Пример 1**: Изменение ссылок ноды перед их отображением:

```php
/**
* Реализация hook_link_alter().
*/
function mymodulename_link_alter(&$links, $node) {
  global $user;
  // Модифицируем ссылку forward для нод текущего пользователя
  if ($user->uid == $node->uid && isset($links['forward_links'])) {
    $links['forward_links']['href'] = 'resume/'. $node->nid .'/send';
    $links['forward_links']['query'] = '';
  }
}
```

**Пример 2**: реализация функции обратного вызова для генерации страницы в модуле Contact:

```php
/**
* Реализация hook_menu().
*/
function contact_menu() {
  $items['contact'] = array(
    'title' => 'Contact',
    'page callback' => 'contact_site_page',
    'access arguments' => array('access site-wide contact form'),
    'type' => MENU_SUGGESTED_ITEM,
    'file' => 'contact.pages.inc',
  );
  return $items;
}
```

###### Создание собственных хуков

Вы можете дать другим разработчикам возможность расширять функционал вашего модуля, с помощью создания в нем собственных хуков. Самый простой способ создать собственный хук — это вызов функции **module\_invoke\_all('имяхука')** ([http://api.drupal.org/api/function/module\_invoke\_all/6](http://api.drupal.org/api/function/module_invoke_all/6 "http://api.drupal.org/api/function/module&#95;invoke&#95;all/6")) в своем модуле. Тогда коллеги-разработчики смогут реализовать этот хук в модуле, создав там функцию **имямодуля\_имяхука()**. Давайте продемонстрируем все на примере.

**Файл**: mymodulename.module

```php
/**
* Пример функции модуля с моим собственным хуком.
*/
function mymodulename_foo() {
  //переменная $layouts - для примера
  $layouts = array();
  //давайте вызовем хук 'mymodulename_layouts'
  //данный хук прицепит к нашей функции другие модули
  $layouts = module_invoke_all('mymodulename_layouts');
  //возвращенное значение - массив возвратов каждой вызванной реализации хука
  foreach($layouts as $layout){
    // Делаем всякое с возвращенными переменными
    print $layout;
  }
}
```

Как вы наверное заметили, мы назвали наш хук **mymodulename\_layouts** — включая строку 'mymodulename'. Это позволит нам избежать конфликтов с другими модулями — если бы мы назвали хук просто 'layouts', мог бы найтись другой модуль, который уже ввел хук с таким же именем.

Чтобы протестировать созданный нами хук, мы сделаем другой модуль, назовем его 'test' и поместим в него эту функцию:

**Файл**: test.module

```php
/**
* Реализация hook_mymodulename_layouts().
*/
function test_mymodulename_layouts() {
  $layout = 'testing';
  return $layout;
}
```

Функция **module\_invoke\_all()** возвращает массив значений, возвращенных реализациями хука. Если реализации в модулях возвращают массивы, они будут склеены в один массив. Вы можете также включать в свои хуки аргументы и даже передавать их как ссылки. Для примера посмотрите на hook\_nodeapi: [http://api.drupal.org/api/function/hook\_nodeapi](http://api.drupal.org/api/function/hook_nodeapi "http://api.drupal.org/api/function/hook&#95;nodeapi").

При создании сложных модулей вам могут пригодиться вот эти функции:

- **module\_hook** — определяет, реализует ли модуль хук.
- **module\_implements** — определяет, какие модули реализуют хук.
- **module\_invoke** — вызывает реализацию хука в заданном модуле.
- **module\_invoke\_all** — как мы описали выше, эта функция вызывает хук во всех реализующих его установленных модулях.

Если эта часть цикла «20 API за 20 дней» была вам интересна, можете заглянуть в документацию API хуков по адресу [http://api.drupal.org/api/group/hooks](http://api.drupal.org/api/group/hooks "http://api.drupal.org/api/group/hooks"). Надеюсь, вам понравится и остальной цикл!
