WHMCS限制特定产品/客户组的钩子失效问题排查
你的WHMCS钩子代码存在的问题及修复方案
1. 钩子注册方式错误:嵌套钩子无法正常生效
你把AdminProductsServices和AdminClientsSearch钩子嵌套在了AdminAreaHeadOutput钩子内部。WHMCS的钩子注册是全局生效的,这种嵌套方式会导致:
- 每次
AdminAreaHeadOutput触发(几乎每个管理员页面加载时)都会重复注册内部钩子,引发逻辑混乱 - 内部钩子的注册时机晚于实际需要执行的时机,导致过滤逻辑根本没被触发
正确的做法是直接全局注册所有钩子,不要嵌套。
2. 产品/服务过滤逻辑完全错误
你当前的代码查询的是tblproducts(产品模板表)的ID,但AdminProductsServices钩子的$vars['products']实际是客户的服务数据(来自tblhosting表),服务的id和产品模板的id不是同一个概念。而且你需要关联服务对应的产品模板所属的分组,而不是直接过滤产品模板ID。
另外,你只处理了产品服务页面的展示,但管理员可能通过其他入口访问到未授权服务,还需要覆盖更多相关钩子。
3. 客户过滤范围不全
AdminClientsSearch钩子仅处理搜索结果的过滤,但管理员直接访问客户列表页面时,这个钩子不会触发,导致未授权客户依然能被看到。你需要添加AdminClientsList钩子来处理客户列表的过滤,同时还要限制单个客户详情页面的访问。
4. 变量获取与修改的问题
- 直接读取
$_SESSION['adminid']不安全,WHMCS钩子的$vars参数已经包含adminid,可以直接用$vars['adminid'] - 修改
$vars数组时,unset元素后数组会留下空键,可能导致页面渲染异常,建议过滤后重新生成数组而不是直接删除元素
修复后的示例代码
<?php use WHMCS\Database\Capsule; // 定义权限配置 $restrictedAdminId = 3; $allowedProductGroupId = 2; $allowedClientGroupId = 1; // 检查当前管理员是否是受限用户 function isRestrictedAdmin() { global $restrictedAdminId; return isset($_SESSION['adminid']) && $_SESSION['adminid'] == $restrictedAdminId; } // 过滤产品服务列表 add_hook('AdminServicesList', 1, function($vars) use ($allowedProductGroupId) { if (!isRestrictedAdmin()) return $vars; // 获取允许的产品模板ID $allowedProductIds = Capsule::table('tblproducts') ->where('gid', $allowedProductGroupId) ->pluck('id') ->toArray(); // 过滤服务:只保留属于允许产品模板的服务 $filteredServices = array_filter($vars['services'], function($service) use ($allowedProductIds) { return in_array($service['pid'], $allowedProductIds); }); $vars['services'] = array_values($filteredServices); return $vars; }); // 过滤客户列表 add_hook('AdminClientsList', 1, function($vars) use ($allowedClientGroupId) { if (!isRestrictedAdmin()) return $vars; // 获取允许的客户ID $allowedClientIds = Capsule::table('tblclients') ->where('groupid', $allowedClientGroupId) ->pluck('id') ->toArray(); // 过滤客户列表 $filteredClients = array_filter($vars['clients'], function($client) use ($allowedClientIds) { return in_array($client['id'], $allowedClientIds); }); $vars['clients'] = array_values($filteredClients); return $vars; }); // 过滤客户搜索结果 add_hook('AdminClientsSearch', 1, function($vars) use ($allowedClientGroupId) { if (!isRestrictedAdmin()) return $vars; $allowedClientIds = Capsule::table('tblclients') ->where('groupid', $allowedClientGroupId) ->pluck('id') ->toArray(); $filteredResults = array_filter($vars['results'], function($result) use ($allowedClientIds) { return in_array($result['id'], $allowedClientIds); }); $vars['results'] = array_values($filteredResults); return $vars; }); // 限制访问未授权客户详情页面 add_hook('AdminClientEdit', 1, function($vars) use ($allowedClientGroupId) { if (!isRestrictedAdmin()) return $vars; $client = Capsule::table('tblclients')->find($vars['userid']); if (!$client || $client->groupid != $allowedClientGroupId) { header("Location: index.php?module=clients"); exit; } return $vars; });
内容的提问来源于stack exchange,提问作者whoo
相关产品推荐
相关产品推荐

