Drupal的钩子系统

Drupal的很多功能都是可以定制的。以导航菜单为例,blog模块需要在菜单上添加一些功能,comment模块需要在菜单上添加一些功能,我们开发的自定义模块也需要在菜单上添加一些功能。Drupal开发者为了达到这样的扩展目的,设计了钩子系统,导航菜单就是其中一个名为menu的钩子。有了钩子系统,开发人员就可以在blog模块定义一个钩子函数从而实现menu钩子。Drupal要求钩子函数的命名必须要求以模块名开始,以钩子名为后缀。

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文件里面,而不用都挤在模块主文件。

module_implements()函数返回实现某个钩子的所有函数,它用module_list()遍历当前激活的所哟模块:

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钩子系统的最外层接口:

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()就可以了:

$items = module_invoke_all('menu');
原文地址:https://www.cnblogs.com/eastson/p/3374366.html