
本文介绍一种高效、可复用的 PHP 递归搜索方法,用于在多层嵌套的菜单结构中查找特定 menu_link 对应的 menu_access 值,并基于预定义权限规则(如 'create' => ['F','W'])判断当前用户是否具备相应操作权限。
本文介绍一种高效、可复用的 php 递归搜索方法,用于在多层嵌套的菜单结构中查找特定 `menu_link` 对应的 `menu_access` 值,并基于预定义权限规则(如 `'create' => ['f','w']`)判断当前用户是否具备相应操作权限。
在构建基于角色的菜单权限系统时,常需动态校验用户对某 URL(如 dashboard/menu)是否拥有 create、update 或 delete 等操作权限。由于菜单数据通常为深度嵌套结构(含 menu_child 子数组),简单 array_search 或 foreach 无法覆盖全层级,必须采用递归遍历 + 权限映射验证策略。
✅ 核心设计原则
- 解耦配置与逻辑:将权限映射表 $dataParams 作为参数传入,避免硬编码和重复初始化;
- 精准匹配优先:先检查当前层级 menu_link 是否匹配目标值,再递归子菜单;
- 短路返回机制:一旦找到匹配项且 menu_access 在对应权限组中,立即返回 true;全部遍历无果则返回 false;
- 安全容错处理:使用空合并操作符 ?? [] 防止 menu_child 不存在导致的 Notice 错误。
? 推荐实现函数
function userHasAccess(array $menuRules, array $dataParams, string $action, string $targetLink): bool
{
foreach ($menuRules as $item) {
// 检查当前层级 menu_link 匹配且 menu_access 符合 action 要求
if (
isset($item['menu_link']) &&
$item['menu_link'] === $targetLink &&
isset($dataParams[$action]) &&
in_array($item['menu_access'] ?? '', $dataParams[$action], true)
) {
return true;
}
// 递归检查 menu_child(若存在)
if (isset($item['menu_child']) && is_array($item['menu_child'])) {
if (userHasAccess($item['menu_child'], $dataParams, $action, $targetLink)) {
return true;
}
}
}
return false;
}
? 使用示例
$dataParams = [
'create' => ['F', 'W'],
'update' => ['F', 'W'],
'delete' => ['F'],
];
// 用户尝试访问 /dashboard/menu/create → 对应权限动作:'create'
var_dump(userHasAccess($arrayStr['user_rules'], $dataParams, 'create', 'dashboard/menu')); // bool(false)
// 因为 'dashboard/menu' 的 menu_access 是 'R',不在 ['F','W'] 中
// 若检查 'read' 权限(需扩展 $dataParams 支持 'read' => ['F','R'])
$dataParams['read'] = ['F', 'R'];
var_dump(userHasAccess($arrayStr['user_rules'], $dataParams, 'read', 'dashboard/menu')); // bool(true)
⚠️ 注意事项
- 函数不返回路径或键名,仅返回布尔结果,符合权限校验场景的语义(“能否操作”而非“在哪定义”);
- 若需获取完整匹配项(如用于日志或调试),可修改函数返回匹配的 $item 数组;
- 对于超深嵌套(>100 层),建议增加递归深度限制或改用栈式迭代,防止栈溢出;
- 生产环境建议配合 OPcache 及类型声明(PHP 8+)提升性能与健壮性。
该方案简洁、可测试、易扩展,适用于 Laravel、CodeIgniter 等框架的中间件或服务层权限校验逻辑。











