Drupal的鉤子系統

Drupal的不少功能都是能夠定製的。以導航菜單爲例,blog模塊須要在菜單上添加一些功能,comment模塊須要在菜單上添加一些功能,咱們開發的自定義模塊也須要在菜單上添加一些功能。Drupal開發者爲了達到這樣的擴展目的,設計了鉤子系統,導航菜單就是其中一個名爲menu的鉤子。有了鉤子系統,開發人員就能夠在blog模塊定義一個鉤子函數從而實現menu鉤子。Drupal要求鉤子函數的命名必需要求以模塊名開始,以鉤子名爲後綴。less

function block_menu() {
  $items['admin/structure/block/manage/%/%'] = array(
    'title' => 'Configure block',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('block_admin_configure', 4, 5),
    'access arguments' => array('administer blocks'),
    'file' => 'block.admin.inc',
  );
  $items['admin/structure/block/manage/%/%/configure'] = array(
    'title' => 'Configure block',
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'context' => MENU_CONTEXT_INLINE,
  );
  $items['admin/structure/block/manage/%/%/delete'] = array(
    'title' => 'Delete block',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('block_custom_block_delete', 4, 5),
    'access arguments' => array('administer blocks'),
    'type' => MENU_LOCAL_TASK,
    'context' => MENU_CONTEXT_NONE,
    'file' => 'block.admin.inc',
  );
  
  ... ...
  
  return $items;
}

 

module_hook_info()函數查詢全部能夠實現的鉤子,實質上也是使用的鉤子方式:函數

function module_hook_info() {
  $hook_info = array();

  foreach (module_list() as $module) {
    $function = $module . '_hook_info'; // hook_info鉤子
    if (function_exists($function)) {
      $result = $function();
      if (isset($result) && is_array($result)) {
        $hook_info = array_merge_recursive($hook_info, $result);
      }
    }
  }
  
  // We can't use drupal_alter() for the same reason as above.
  foreach (module_list() as $module) {
    $function = $module . '_hook_info_alter'; // hook_info_alter鉤子
    if (function_exists($function)) {
      $function($hook_info);
    }
  }

  return $hook_info;
}

 

自定義的鉤子能夠在hook_info中註冊,也能夠不註冊。註冊的好處是能夠爲鉤子實現人員提供更詳細的詳細,另一個目的是能夠爲鉤子分組。例如,定義了不少admin有關的鉤子,這些鉤子函數咱們想把它們單獨放在一個PHP文件裏面,而不用都擠在模塊主文件。oop

 

module_implements()函數返回實現某個鉤子的全部函數,它用module_list()遍歷當前激活的所喲模塊:ui

function module_implements($hook, $sort = FALSE, $reset = FALSE) {
  $implementations = &$drupal_static_fast['implementations'];

  if (!isset($implementations[$hook])) {
  
    $hook_info = module_hook_info(); // 查詢鉤子信息
    
    $implementations[$hook] = array();
    $list = module_list(FALSE, FALSE, $sort);
    foreach ($list as $module) {
    
      // 注意這裏額外載入了鉤子分組文件
      $include_file = isset($hook_info[$hook]['group']) && module_load_include('inc', $module, $module . '.' . $hook_info[$hook]['group']);
      
      // Since module_hook() may needlessly try to load the include file again,
      // function_exists() is used directly here.
      if (function_exists($module . '_' . $hook)) {
        $implementations[$hook][$module] = $include_file ? $hook_info[$hook]['group'] : FALSE;
      }
    }
    
    // Allow modules to change the weight of specific implementations but avoid
    // an infinite loop.
    if ($hook != 'module_implements_alter') {
      drupal_alter('module_implements', $implementations[$hook], $hook);
    }
  }
  else {
    foreach ($implementations[$hook] as $module => $group) {
      // If this hook implementation is stored in a lazy-loaded file, so include
      // that file first.
      if ($group) {
        // 注意這裏額外載入了鉤子分組文件
        module_load_include('inc', $module, "$module.$group");
      }
      
      // It is possible that a module removed a hook implementation without the
      // implementations cache being rebuilt yet, so we check whether the
      // function exists on each request to avoid undefined function errors.
      // Since module_hook() may needlessly try to load the include file again,
      // function_exists() is used directly here.
      if (!function_exists($module . '_' . $hook)) {
        // Clear out the stale implementation from the cache and force a cache
        // refresh to forget about no longer existing hook implementations.
        unset($implementations[$hook][$module]);
        $implementations['#write_cache'] = TRUE;
      }
    }
  }

  return array_keys($implementations[$hook]);
}

 

module_invoke_all()則調用全部實現某個鉤子的全部函數,這是Drupal鉤子系統的最外層接口:this

function module_invoke_all($hook) {
  $args = func_get_args();
  // Remove $hook from the arguments.
  unset($args[0]);
  $return = array();
  foreach (module_implements($hook) as $module) {
    $function = $module . '_' . $hook;
    if (function_exists($function)) {
      $result = call_user_func_array($function, $args); // 調用鉤子函數
      if (isset($result) && is_array($result)) {
        $return = array_merge_recursive($return, $result); // 返回結果的處理方式
      }
      elseif (isset($result)) {
        $return[] = $result; // 返回結果的處理方式
      }
    }
  }

  return $return;
}

 

例如,咱們要調用menu鉤子,簡單的調用module_invoke_all()就能夠了:spa

$items = module_invoke_all('menu');
相關文章
相關標籤/搜索