Come creare una hook?

7 contenuti / 0 new
Ultimo contenuto
Come creare una hook?

Ho provato a creare una hook attraverso un modulo e ho notati certi comportamenti "strani" che vorrei capire meglio. La hook deve interrogare i moduli che la implementano restituendo una serie di valori, in dettaglio directory, file etc.
Il test eseguito non utilizza il database ma, quando viene chiamata una funzione, scatta il meccanismo che richiamerebbe tutte le hook implementate.

Senza dilungarmi troppo, è come se ad ogni chiamata delle hook, Drupal si fermasse al modulo che per primo viene incluso (in senso crnologico) e che per primo ne invoca l'utilizzo. Se io con un modulo implemento la hook solamente e con un modulo che viene invocato successivamente a questi implementassi la hook e ne invocassi l'utilizzo, allora noto che Drupal passa per il primo modulo e ne chiama la hook, poi passa al secondo e si ferma, anche se dopo di questo secondo modulo ne avessi un terzo che implementa hook e ne invoca l'utilizzo. Per poter utilizzare il terzo modulo, il secondo modulo si dovrebbe limitare ad implementare la hook, così come nel primo.

Come mai questo comportamento? Non dovrebbero esser richiamate tutte le hook ad ogni richiesta anziché fermarsi al primo modulo che ne invoca l'utilizzo?

Avrei già pronta un'alternativa che utilizza il database e una diversa elaborazione, ma prima vorrei capire questo comportamento che ho riscontrato nei miei test.

Grazie.

Come hai implementato l'hook?

Prima di questo, stai cercando di implementare un hook di sistema (per esempio hook_user(), hook_taxonomy(), ...) o di creare un tuo hook (per esempio hook_nuovapagina() ) per poi estenderlo attraverso altri moduli?

Nel primo caso trovi in giro valangate di documentazione, nel secondo caso dai un occhio a module_invoke_all.

Ciao
Marco
--
My blog
Working at @agavee

Sto creando un mio hook che poi altri moduli possono estendere. Avevo dato un'occhiata a quella funzione e avevo quella sorta di bug - che forse è il suo comportamento naturale -.

Ho risolto, richiamando così l'hook:

<?php
// ...
 
foreach (module_list() as $module) {
   
$hooks[] = module_invoke($module, 'nome_hook');
  }
// ...
?>

Così forzo per ogni modulo a cercare l'hook implementato. La variabile $hooks potrebbe infatti avere un array di array o null, che filtro dopo tranquillamente.

Grazie :)

Imho hai qualche cosa che non funziona nei tuoi hook, se puoi passarci un implementazione ci diamo un occhio, in ogni caso il codice:

<?php
$risultato
= module_invoke_all('nome_hook', $parametro, $altro_parametro);
?>

funziona, viene usato in tutto Drupal e serve a far funzionare il sistema di base.

Ciao
Marco
--
My blog
Working at @agavee

Si avevo notato questa cosa e anche altri blog indicavano questo come metodo basilare per l'invocazione degli hook implementati. Però se guardi la funzione _block_rehash, troverai il frammento che mi ha risolto il problema.
Non so che accade di preciso, ma sembra che con il primo metodo applichi qualche filtro di sorta, con il secondo forza un'interrogazione a tutti i moduli presenti, restituendo NULL qualora quell'hook non sia implementato, altrimenti i dati resi dall'hook.

Qui c'è un modulo che implementa la hook:

<?php
 
function test_dir_path() {
  return array(array(
   
'module' => 'test',
   
'path' => 'include',
   
'grade' => IMPLEMENTER_INCLUDE
 
));
}
?>

Restituisce un semplicissimo array.
La funzione che ne richiama le hook farebbe questo:
<?php
 
function implementer_include_dir($file) {
   
$file = implementer_get_pathname_from_hook($file);
    if (
$file) {
      switch (
$file->grade) {
        case
IMPLEMENTER_REQUIRE_ONCE:
          @require_once
$file->pathname;
          return;
        case
IMPLEMENTER_INCLUDE_ONCE:
          @include_once
$file->pathname;
          return;
        case
IMPLEMENTER_REQUIRE:
          @require
$file->pathname;
          return;
        case
IMPLEMENTER_INCLUDE:
        default:
          @include
$file->pathname;
          return;
      }
    }
  }
function
implementer_get_pathname_from_hook($file) {
 
$files = _implementer_invoke();
  return
_implementer_get_files($files, $file);
}
function
_implementer_invoke() {
 
$tmp_modules_files = array();
  foreach (
module_list() as $module) {
   
$tmp = module_invoke($module, 'dir_path');
    if (
is_null($tmp) || !is_array($tmp)) {
      continue;
    }
   
$tmp_modules_files[] = $tmp;
  }
// poi filter from per vedere che ha prelevato dagli hook, per poi rendere l'array
 
return $files;
}
function
_implementer_get_files($files, $file) {
  if (!
is_array($files)) {
    return
false;
  }
  foreach (
$files as $file_) {
    if (!
is_object($file_)) {
      continue;
    }
   
$pathname = $file_->path. "/". $file;
    if (
file_exists($pathname)) {
      return (object)array(
'pathname' => $pathname, 'grade' => $file_->grade);
    }
  }
  return
false;
}
?>

È ancora incompleto nelle sue parti, ma grosso modo avevo realizzato questo.

mm.. così ad occhio il tuo hook restituisce un array senza chiave (o meglio con chiave sempre uguale a 0) e quindi quando fa il merge degli array viene tenuto solo l'ultimo hook invocato. Per capirci, il tuo hook ritorna:

<?php
$ret
= array(
 
0 => array(
   
'module' => 'test',
   
'path' => 'include',
   
'grade' => IMPLEMENTER_INCLUDE,
  ),
);
?>

per ogni modulo, quindi nel merge la chiave è sempre la stessa e di conseguenza non hai un merge ma un replace. Puoi risolvere usando una struttura di ritorno un pò diversa:
<?php
$ret
= array(
 
'test' => array(
   
'path' => 'include',
   
'grade' => IMPLEMENTER_INCLUDE,
  ),
);
?>

in questo modo nel return del module invoke hai un array con tutti i moduli che hanno invocato (dato che la chiave varierà per ogni modulo, nel merge verranno mantenuti).

Ciao
Marco
--
My blog
Working at @agavee

mmm proverò come dici tu, magari sfruttando la chiave inserita anziché passarla come parametro.

Grazie