Error message

User warning: The following theme is missing from the file system: responsive_green. For information about how to fix this, see the documentation page. in _drupal_trigger_error_with_delayed_logging() (line 1143 of /var/www/moscovie.com/includes/bootstrap.inc).

Add new comment

Создаем свой хук в drupal7

Перейдем более подробно к сути вопроса.

Создадим два модуля

mkdir sites/all/modules/modulea


touch sites/all/modules/modulea/modulea.info
touch sites/all/modules/modulea/modulea.module

Содержимое файла modulea.info

name = Module A
description = First Example Module
package = Alanstormdotcom
version = VERSION
core = 7.x
files[] = modulea.module

Создадим второй модуль

mkdir sites/all/modules/moduleb

touch sites/all/modules/moduleb/moduleb.info
touch sites/all/modules/moduleb/moduleb.module

Содержимое файла moduleb.info

name = Module B
description = Second Example Module
package = Alanstormdotcom
version = VERSION
core = 7.x

files[] = moduleb.module

Для простоты работы, создадим третий файл test.php, в корне нашего сайта, с таким содержимым

define('DRUPAL_ROOT', getcwd());
require_once DRUPAL_ROOT . '/includes/bootstrap.inc';
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);

echo 'test';
###menu_execute_active_handler();

Модульная система Drupal использует систему хуков. Эта система напоминает классы на основе паттерна observer / listener.Но ООП возможности не используются. Вместо этого, система реализуется полностью с помощью обращения к функциям через переменные.

Пример
function test() {
echo "Hello world";
}
$test = 'test';
$test();

Итак что такое хуки?
Хуки позволяют распределить один вызов функции в нескольких файлах.

Представим что это простая функция php.
function foo($bar)
{

return $data
}

... Где-то еще в нашем коде ...
foo();

После того как мы написали код этой функции мы не сможем легко его изменить, код ядра изменять не нужно,но нужно обеспечить расширение функциональности сайта.
Эту проблему решают Хуки

//старый способ, вызов foo function
foo();

//новый способ: вызов foo hook
module_invoke_all('foo');

Что происходит когда вызывается хук?

  1. Получаем список всех установленных и включенных модулей.
  2. Спрашиваем у каждого модуля "Вы реализовали foo хук"?
  3. Если это так, то Drupal вызывает функцию в тех модулях, которые реализуют крюк

Таким образом вы можете запрограммировать свою логику ,в то же время позволяя другим программистам через хук дополнить ее.

Как это работает?

Сделаем следующее

  1. Напишем код, который вызывает хук по имени HelloWorld
  2. Напишем код, который реализует HelloWorld хук в модуле A
  3. Напишемь код, который реализует HelloWorld хук в модуле B

Изменим файл test.php в корне сайта
define('DRUPAL_ROOT', getcwd());
require_once DRUPAL_ROOT . '/includes/bootstrap.inc';
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
module_invoke_all('helloworld');

Реализуем хук в нашем модуле.
Откроем файл первого модуля.
#File: sites/all/modules/modulea/modulea.module
function modulea_helloworld()
{
echo "

Our friends at ".__FUNCTION__." want to say Hello World

";
}

Для вызова хука в своем модуле нам нужно несколько вещей.

  1. Создать функцию в .module файла модуля
  2. Имя функции должно начинаться с имени вашего модуля, а затем подчеркивания (modulea_)
  3. Имя функции должно продолжаться с именем хука (HelloWorld) для его реализации
  4. Реализовать любой код, в вашей новой функции

Повторим то же самое во втором модуле
#File: sites/all/modules/moduleb/moduleb.module
function moduleb_helloworld()
{
echo "

Our friends at ".__FUNCTION__." want to say Hello World

";
}

Увидим на экране вывод
Our friends at modulea_helloworld want to say, Hello World

Our friends at moduleb_helloworld want to say, Hello World

Возврат значений Хуками.
Изменим наш вызов в файле test.php
$results = module_invoke_all('helloworld');
var_dump($results);

Получаем
Our friends at modulea_helloworld want to say, Hello World

Our friends at moduleb_helloworld want to say, Hello World

array
empty
Мы получили пустой массив. Давайте немного изменим наши функции в модулях

#File: sites/all/modules/modulea/modulea.module
...
function modulea_helloworld()
{
return "

Our friends at ".__FUNCTION__." want to say, Hello World

";
}
...
#File: sites/all/modules/moduleb/moduleb.module
function moduleb_helloworld()
{
return "

Our friends at ".__FUNCTION__." want to say, Hello World

";
}

Теперь мы осуществляем возврат наших значений
array
0 => string '

Our friends at modulea_helloworld want to say, Hello World

' (length=65)
1 => string '

Our friends at moduleb_helloworld want to say, Hello World

' (length=65)


Вызов будет всегда возвращать массив ,если реализация конкретных функций ничего не возвращает массив будет пуст.

Изменим наш код чтобы он возвращал массив а не строку

function modulea_helloworld()
{
return array("one","two","three");
}

Теперь мы увидим

array
0 => string 'one' (length=3)
1 => string 'two' (length=3)
2 => string 'three' (length=5)
3 => string '

Our friends at moduleb_helloworld want to say, Hello World

' (length=65)

Что произойдет если произойдет совпадение ключей в двух функциях? Проверим


#File: sites/all/modules/modulea/modulea.module
...
function modulea_helloworld()
{
return array ("foo"=>"one","baz"=>"two","bar"=>"three","function"=>__FUNCTION__);
}
...
#File: sites/all/modules/moduleb/moduleb.module
function moduleb_helloworld()
{
return array('function'=>__FUNCTION__);
}

Смотрим на результат

array
'foo' => string 'one' (length=3)
'baz' => string 'two' (length=3)
'bar' => string 'three' (length=5)
'function' =>
array
0 => string 'modulea_helloworld' (length=18)
1 => string 'moduleb_helloworld' (length=18)

Так, если ключи совпадают, Drupal будет объединить ключи в вложенного массива в родительский массив.
Поэтому важно это помнить, чтобы избежать проблем в разработке.

Вызов хуков
Не во всех случаях нужно вызывать все реализованные хуки в системе.
Для этого друпал использует функцию module_invoke
$results = module_invoke('modulea','helloworld');

Оба module_invoke и module_invoke_all могут принять неограниченное количество дополнительных параметров.

module_invoke_all('helloworld','one','two','three');
module_invoke('modulea','helloworld','one','two','three');

Эти параметры в свою очередь передаются на реализацию хука
Пример
function modulea_helloworld($param, $another_param, $third)
{
return array('joined'=>implode('##',array($param, $another_param, $third)));
}

Строки "один", "два", и "три" передаются в виде $param, $another_param, $third

Изнутри это работает с помошью функции func_get_args РНР. Эта реализация представляет собой некоторые проблемы.Например - невозможность передачи по ссылке.

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

$object = new SomeClass();
module_invoke('modulea','helloworld',$object);
//we've got an object that's been modified by our hook implementations
$object;

Однако в друпал обычно используется обычные массивы или переменные,которые передются по ссылке.

Тем не менее, из-за использования func_get_args в module_invoke и module_invoke_all , невозможно передать значения с помощью вызова хука по ссылке.

Для решения этой проблемы есть два решения.

Первое функция drupal_alter, которая является альтернативным способом вызова реализации хука.
drupal_alter('helloworld',$data);
Данная функция может возвращать три значения
drupal_alter('helloworld',$data, $foo, $bar);

Нарушение инкапсуляции
Есть способ обойти ограничения drupal_alter
В друпал есть функция module_implements которая будет возвращать список модулей, реализующих определенную хук.
Вы получите массив с именами модулей

array
0 => string 'modulea' (length=7)
1 => string 'moduleb' (length=7)

Когда кто-то хочет передать переменную по ссылке в реализации хука и не использовать синтаксис _alter , вы увидите что-то вроде этого.

$hook = 'helloworld';
$etc = array('one','two');
$param_by_ref = 'four';
$all = array();
foreach(module_implements($hook) as $module)
{
$function = $module . '_' . $hook;
$single_hook_return = $function($param_by_ref, $etc);
$all = array_merge($all, $single_hook_return);
}
var_dump($all);

Приведем более конретные примеры

Создадим модуль где опишем первичныю логику.(Все что угодно, создание сущностей в базе,обработка данных и тд)
В данном примере это будет callback menu с реализацией хука

function mymodule_menu ()
{
$menu [ 'mymodule' ] = array
(
'title' => 'Some title' ,
'page callback' => 'my_page_callback' ,
'access callback' => TRUE ,
);
return $menu ;
}

function my_page_callback () {

$content = array('Line 1');

foreach (module_implements('custom_hook_name') as $module) {
$function = $module . '_custom_hook_name';
$content = $function($content);
}

$output = '' ;
foreach( $content as $c )
{
$output .= '

' . $c . '

' ;
}
return $output ;
}
Основная логика нашего хука
$content = array('Line 1');
$output = '' ;
foreach( $content as $c )
{
$output .= '

' . $c . '

' ;
}
return $output ;

Здесь мы имеем какой-то массив со строкой, и ее выводим на экран.
Вот что мы видим
Some title

Line 1

Естественно мы можем написать любую логику,все что угодно.

Теперь расширим данную логику. Реализовав хук в своем модуле. (соттветственно мы вызовем базовую логику + добавим то что нужно нам)
Возможности вашего хука определенны в базовой функции, документацию принято описывать в файле mymodule.api.php

В данном случае мы будем использовать Пример # 3 (Сбор результатов в массив) для передачи результата переменной по ссылке
foreach (module_implements('custom_hook_name') as $module) {
$function = $module . '_custom_hook_name';
$content = $function($content);
}

В итоге мы получаем вызов функции mymodule_custom_hook_name($content)
С возвратом всех определленных нами значений

Теперь реализуем нах хук
function mymodule_custom_hook_name($content)
{
$content[] = 'Line 2';
$content[] = 'Another line';
return $content;
}

В результате получим такой вывод
Some title

Line 1

Line 2

Another line

Справочные примеры из документации

Создание хуков и вызов их реализаций

Аналогии
Хук в друпал аналогичен интерфесу в ООП
Реализация хука - это реализации интерфейса в некотором классе.

Создать хук и вызвать его реализации можно сделать используя следующие функции (не все включены)
drupal_alter(), module_invoke_all(), module_invoke()

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

Чтобы быть в состоянии вызвать хук вы должны реализовать Drupal7 хук.(более подробно об этом процессе несколько ниже)

Типы хуков в Drupal7
В общей практике есть два типа хуков, которые можно создать.

  1. Изменение хуков(Alter hook) - распространенный способ редактирования содержимого конкретного объекта или переменной, получает переменные в хук по ссылке, как правило это делается, с помощью функцииdrupal_alter()
  2. Перехват хуков
    Разрешает внешним модулям совершать действия во время выполнения хука,не может получить переменные по ссылке,используются функции module_invoke_all(), module_invoke()


Примеры

Пример # 1 (простой Вызов)

// Calling all modules implementing 'hook_name':
module_invoke_all('name');
?>

Пример # 2 (Вызов одного модуля и хука)

// Calling a particular module's 'hook_name' implementation:
module_invoke('module_name', 'name');
// @note: module_name comes without '.module'
?>

Пример # 3 (Сбор результатов в массив)

$result = array();
foreach (module_implements('hook_name') as $module) {
// Calling all modules implementing hook_hook_name and
// Returning results than pushing them into the $result array:
$result[] = module_invoke($module, 'hook_name');
}
?>

module_implements - Позволяет определить, какие модули используют нужный нам хук, например, узнать, какие модули переопределяют меню через hook_menu

Пример # 4 (изменения данных с помощью drupal_alter ())

$data = array(
'key1' => 'value1',
'key2' => 'value2',
);
// Calling all modules implementing hook_my_data_alter():
drupal_alter('my_data', $data);
// You should implement hook_my_data_alter() in all other modules in which you want to alter $data
?>


Пример # 5 (передача по ссылке: нельзя использовать module_invoke ())

// @see user_module_invoke()
foreach (module_implements('hook_name') as $module) {
$function = $module . '_hook_name';
// will call all modules implementing hook_hook_name
// and can pass each argument as reference determined
// by the function declaration
$function($arg1, $arg2);
}
?>

Категория: 
The code has been tested and works

Filtered HTML

  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <blockquote> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • Lines and paragraphs break automatically.

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.