Drupal hookok és alterek

Amikor ismerkedünk a Drupallal, hamar elkezdjük használni a népszerű kiegészítő modulokat. Aztán további oldalakat készítve megtapasztaljuk, hogy ezek a modulok remekül építenek egymásra, mint egy kirakóban az elemek.
Majd ha eljutunk arra a szintre, hogy önállóan modulokat készítsünk, akkor ezt a szemléletmódot hajlamosak vagyunk elfelejteni, mintha a modulunknál fel sem merülne, hogy illeszkedjen ebbe az elgondolásba. Pedig nem kellene sok hozzá, csak néhány extra sor a megfelelő helyen.

Minden extra sallang helyett:

$result = module_invoke_all('teszt', $data);

Ezzel a sorral meghívtuk a bekapcsolt modulok hook_teszt() függvényét, a függvények paraméterként megkapják a $data változót, a függvényhívások visszatérési értékei a $result tömbbe kerülnek.
Ha az „alma” nevű modulunkban ezt a hook függvényt fel szeretnénk használni, akkor kell készítenünk benne egy function alma_teszt($data) { } függvényt. Fontos, hogy ez a függvény nem tudja módosítani a $data értékét úgy, hogy az a többi hívásra is hatással legyen.

Ha olyan hook függvényre van szükségünk, amelyik az előző függvény által összegyűjtött értéket módosítja, más varázslat kell:

drupal_alter('teszt', $result);

Ez a függvény „alterezi” a már meglévő értékeket, az „alma” modulunkban function alma_teszt_alter(&$result) { } függvény elkészítésével defiálhatjuk. Itt a változó referenciaként adódik át, ezért az értéke szabadon módosítható úgy, hogy a változtatás a többi alter függvényben is elérhető.

Hogy jobban megértsük a hook függvények és az alterek viszonyát, íme egy szép példa az actions_list() függvényben:

$actions = module_invoke_all('action_info');
drupal_alter('action_info', $actions);

Itt az első sorban meghívódnak a hook_action_info()-val definiált hookok, amelyek mindegyike egy-egy tömböt ad vissza. Ezek összegyűjtésre kerülnek a $actions változóban, majd a második sorban meghívott hook_action_info_alter() függvénnyel a modulok módosíthatják ennek a változónak az értékét.

Ha a hook függvények meghívásakor további műveleteket szeretnénk végezni, használjuk a module_implements() függvényt. A következő kód minden hook_teszt() implementációt meghív, kivéve az alma modulét:

foreach (module_implements('teszt') as $module) {
  if ($module != 'alma') {
    module_invoke($module, 'teszt')
  }
}

Az általunk készített hook függvényeket a modul .module kiterjesztésű állományába kell elhelyezni. Ha más fájlba szeretnénk, akkor a hook_hook_info() van segítségünkre, természetesen ennek is van alter párja, amennyiben más modulokra szeretnénk hatással lenni: hook_hook_info_alter(). A saját hookjainkat érdemes dokumentálni a modul nevével megegyező api.php kiterjesztésű fájlban.

Fenti kis elmélkedés bemutatására összeraktam egy mintakódot, ami a https://github.com/nevergone/drupal_hook_test oldalon érhető el. A hookok alapjain kívül tartalmaz kódrészletet a sminkfüggvények és sminkfájlok használatára is. Érdemes a modult commitonként tanulmányozni, a megjegyzések és commit-üzenetek szándékosan magyar nyelvűek a könnyebb érthetőség kedvéért.