web 网站,几乎都是以路径为基础,废话,没路径用户咋访问呢?所以,hook_menu 的重要性不言而喻了。几乎每个模块,都会使用到这个钩子,纯 api 式的模块除外。
hook_menu 相对 form 来说,简单许多,没有那么多参数,但也不容小视。需要特别注意的就是权限验证,权限是一个站点的重中之重,稍有不慎,就有安全问题。
本篇教程,以说明 hook_menu 的常见使用方法为目的,不需要数据库,所有页面回调函数,返回的内容也是随意而写。
模块名称:mymenu。
实现功能:演示 hook_menu 的基本使用方法
权限设置:有
第一步:模块信息,创建 mymenu.info 文件
1// $Id$
2name = "自定义路径"
3description = hook_menu 实例模块
4core = "6.x"
第二步: 模块主文件,创建 mymenu.module 文件
模块重在演示,所有回调函数并没有太多内容,所以均写在模块主文件中。
提示:重点部分在于回调函数和传递参数,通配符的使用,以及 menu 类型的应用。
注意:hook_menu 之内的代码,有任何更改,请清除缓存。
001<?php
002// $Id$
003
004/**
005 * @Implement of hook_menu()
006 */
007function mymenu_menu() {
008 $item = array();
009 /**
010 * 一个完整的路径包含以下参数:
011 * 页面标题(<title></title>)部分:
012 * title:页面标题,可选,
013 * title callback:页面标题回调函数,可选
014 * title arguments:传递给标题回调函数的参数,可选
015 * 例如:
016 * array(
017 * 'title' => '页面标题测试', // 不需要使用 t() 函数,menu 将缓存,显示时,自动调用 t()
018 * );
019 *
020 * 访问该页面时,不出意外的话,<title></title> 部分将显示“页面标题测试”
021 * 意外是什么?我们可以在页面回调函数中使用 drupal_set_title() 重新指定页面标题
022 *
023 * 或者:
024 * array(
025 * 'title callback' => 'mymenu_menu_title', // 回调函数
026 * 'title arguments' => array('test'), // 传递的参数
027 * );
028 *
029 * 访问该页面时,将以回调函数的返回值做为 title,即: mymenu_menu_title($var)
030 *
031 * 页面 content 区域:
032 * page callback:页面回调函数,若没有,则调用父级,一直往上
033 * page arguments:传递给页面回调函数的参数,可选
034 *
035 * 例如:
036 * array(
037 * 'page callback' => 'mymenu_test',
038 * 'page arguments' => array('test', 'abcd')
039 * );
040 *
041 * 访问该页面时,将以回调函数的返回值做为页面主体内容,即 page.tpl.php 中的 $content 变量。
042 * 以上例子中的回调函数这样定义即可: mymenu_test($var1, $var2)
043 *
044 * 页面权限判断:
045 * access callback:权限回调函数,若不指定,则默认使用 user_access,这是 Drupal 权限系统的基础函数,可选
046 * access arguments:传递给权限回调函数的参数,可选
047 *
048 * 例如:
049 * array(
050 * 'access arguments' => array('node view'),
051 * );
052 *
053 * 没有权限回调函数,默认使用 user_access,即:user_access('node view')
054 * 判断用户是否具有 node view权限,如有,则可以访问该页面。
055 *
056 * 或者:
057 * array(
058 * 'access callback' => 'mymenu_is_access',
059 * 'access arguments' => array('node view'),
060 * );
061 *
062 * 回调指定函数,即:mymenu_is_access($var),自定义判断,有权限访问则返回 true 即可
063 *
064 * 包含指定文件:
065 * file:文件名称,相对于模块目录,或者指定目录。在 myform 教程中,我们讨论了这样做的好处。可选
066 * file path:指定待包含文件的路径
067 *
068 * 在访问该页面时,将包含指定文件,所以页面回调函数可写在指定文件中。
069 *
070 * menu 类型:
071 * type:此参数将决定 menu 是否显示在导航中或tabs之上等等,Drupal 定义了几个常量
072 * MENU_CALLBACK:只是注册一个地址,访问页面时,回调函数显示。不显示在其它任何地方。
073 * MENU_NORMAL_ITEM:做为一个普通菜单,管理员可以在菜单中移动或隐藏等
074 * MENU_SUGGESTED_ITEM:做为一个普通菜单,默认隐藏,管理员可启用
075 * MENU_LOCAL_TASK:做为一个 tabs 选项
076 * MENU_DEFAULT_LOCAL_TASK:做为 tabs 的默认页面
077 * 留空则默认为 MENU_NORMAL_ITEM
078 *
079 * 其它参数:
080 * description:路径描述,可选
081 * weight:路径权重,如果两个模块定义了相同的路径,以权重来决定使用谁的配置,可选。
082 * menu_name:设置为自定义菜单,该路径将不会出现在导航列表中
083 *
084 * 以上就几乎是一个 menu 的全部参数,几乎,也就是说还有几个参数,api 上没有列出来,实际开发中也很少用到
085 * 比如:load_functions、load_arguments、to_arg_functions,这几个都是和占位符、回调函数传递参数有关
086 *
087 * 回调函数传递的参数,实际上也是 hook_menu 的一大难点,理解了回调函数的参数传递,基本就算熟悉 hook_menu 了。
088 * 参数可以是字符串、对象、数组,但注意:
089 * 1、不能是变量。所有模块的 hook_menu 配置都会缓存, 所以,如果是变量,下一次从数据库中读取出来时,就会出现差异。
090 * 更严重的就是安全问题,假设使用变量做为参数:$user->uid,在清除缓存时当前用户是管理员,
091 * 在下一次访问该页面时,参数仍然是管理员 uid
092 * 2、不能用正整数(int),因为默认将正整数当成通配符处理,对应路径中的层级
093 * 比如:$item['test/abcd'],回调函数中传递参数:array(0, 1),实际上传递过去的就是 array('test', 'abcd')
094 * 关于 Drupal 中的路径层级处理,请参见 arg() 函数。
095 *
096 * 这是较为常见的 hook_menu 定义方式,比如:$item['node/%node']
097 *
098 * 以下是一些实例
099 *
100 */
101
102 // demo 1:使用 user_access() 来判断权限
103 $item['mymenu'] = array(
104 'description' => 'test1',
105 'page callback' => 'mymenu_test',
106 'access arguments' => array('mymenu add'), // 使用 user_access(),权限必须存在,用 hook_perm 定义一个权限
107 'type' => MENU_CALLBACK,
108 );
109
110 // demo 2:使用自定义权限回调函数
111 $item['mymenu/access'] = array(
112 'description' => 'test1',
113 'page callback' => 'mymenu_test_access',
114 'access callback' => 'mymenu_is_test_access', // 自定义 回调函数
115 'type' => MENU_CALLBACK,
116 );
117
118 // demo 3:定义为菜单
119 $item['mymenu/menu'] = array(
120 'description' => 'test1',
121 'page callback' => 'mymenu_test_access',
122 'access callback' => 'mymenu_is_test_access',
123 'type' => MENU_NORMAL_ITEM, // 菜单
124 );
125
126 // demo 4:使用通配符
127 $item['mymenu/user/%user'] = array(
128 'description' => '我的管理中心',
129 'page callback' => 'mymenu_test_user',
130 'page arguments' => array(2), // 注意 mymenu_test_user 函数
131 'access callback' => 'mymenu_is_test_user', // 权限验证函数,假设只允许浏览自己的页面
132 'access arguments' => array(2), // 传递 $ac 给回调函数
133 'type' => MENU_NORMAL_ITEM, // 菜单
134 );
135
136 return $item;
137}
138
139/**
140 * @Implement of hook_perm()
141 * 自定义权限,启用后,在用户权限里,就可以自由分配,拥有该权限的用户,即可访问 mymenu
142 */
143function mymenu_perm() {
144 return array(
145 'mymenu add'
146 );
147}
148
149/**
150 * 自定义回调函数
151 * 假设允许登录用户访问,实际上有现成的函数
152 */
153function mymenu_is_test_access() {
154 return $GLOBALS['user']->uid > 0;
155}
156
157/**
158 * 验证函数
159 * @param (object) $ac
160 */
161function mymenu_is_test_user($ac) {
162 return $GLOBALS['user']->uid == $ac->uid;
163}
164
165/**
166 * mymenu
167 */
168function mymenu_test() {
169 $output = '你拥有 mymenu add 权限';
170 return $output;
171}
172
173/**
174 * mymenu/access
175 */
176function mymenu_test_access() {
177 $output = '你已登录';
178 return $output;
179}
180
181/**
182 * 传递过来的参数是一个用户对象
183 * @param (object) $ac
184 */
185function mymenu_test_user($ac) {
186 $output = '欢迎你,'.$ac->name;
187 return $output;
188}
以上就是简单的 hook_menu 应用,一时之间难以理解的话,可以先试着定义较为简单的 menu,即只需要页面回调函数和权限验证函数的 menu,可以将上两个教程中的路径,加上权限控制,慢慢尝试各个参数的作用。下一篇教程将具体介绍 hook_menu 的读取顺序以及覆写方法。
最后,再次提醒, hook_menu 之内的代码,有任何更改,请清除缓存。