基于PHP递归函数构建多级权限结构
在后台管理系统开发中,权限管理是非常核心的模块。多级权限通常表现为树形结构,比如父级菜单包含子菜单,子菜单下还可以有更多层级的操作权限。这种层级关系天然适合用递归逻辑处理,PHP的递归函数可以高效地完成权限数据的整理与构建。
多级权限的数据结构
通常我们会把权限数据存储在数据库的权限表中,表结构一般包含权限ID、权限名称、父级权限ID等字段。下面是一个典型的权限表结构示例:
| 字段名 | 类型 | 说明 |
|---|---|---|
| id | int | 权限唯一标识 |
| name | varchar | 权限名称 |
| parent_id | int | 父级权限ID,顶级权限为0 |
| url | varchar | 权限对应的访问地址 |
假设我们从数据库中查询出的原始权限数据如下:
$permissionList = [ ['id' => 1, 'name' => '系统管理', 'parent_id' => 0, 'url' => ''], ['id' => 2, 'name' => '用户管理', 'parent_id' => 1, 'url' => 'https://www.ipipp.com/user/index'], ['id' => 3, 'name' => '添加用户', 'parent_id' => 2, 'url' => 'https://www.ipipp.com/user/add'], ['id' => 4, 'name' => '编辑用户', 'parent_id' => 2, 'url' => 'https://www.ipipp.com/user/edit'], ['id' => 5, 'name' => '权限管理', 'parent_id' => 1, 'url' => 'https://www.ipipp.com/permission/index'], ['id' => 6, 'name' => '角色管理', 'parent_id' => 1, 'url' => 'https://www.ipipp.com/role/index'], ['id' => 7, 'name' => '内容管理', 'parent_id' => 0, 'url' => ''], ['id' => 8, 'name' => '文章管理', 'parent_id' => 7, 'url' => 'https://www.ipipp.com/article/index'], ['id' => 9, 'name' => '发布文章', 'parent_id' => 8, 'url' => 'https://www.ipipp.com/article/publish'], ];
递归函数构建多级权限树
递归函数的核心逻辑是:先找到所有顶级权限(parent_id为0的权限),然后为每个顶级权限递归查找它的子权限,直到没有更下级的权限为止。下面是具体的递归函数实现:
/**
* 递归构建权限树
* @param array $permissionList 原始权限列表
* @param int $parentId 当前查找的父级ID
* @return array 构建好的多级权限树
*/
function buildPermissionTree($permissionList, $parentId = 0) {
$tree = [];
foreach ($permissionList as $permission) {
// 找到当前父级下的子权限
if ($permission['parent_id'] == $parentId) {
// 递归查找该权限的子权限
$children = buildPermissionTree($permissionList, $permission['id']);
// 如果有子权限,添加到当前权限的children字段
if (!empty($children)) {
$permission['children'] = $children;
}
$tree[] = $permission;
}
}
return $tree;
}
// 调用函数构建权限树
$permissionTree = buildPermissionTree($permissionList);上述函数中,我们首先遍历原始权限列表,筛选出父级ID等于当前传入$parentId的权限,然后对每个筛选出的权限,再次调用buildPermissionTree函数,传入当前权限的ID作为新的父级ID,查找它的子权限。如果递归后找到了子权限,就为当前权限添加children字段存储子权限数组,最终返回构建好的树形结构。
权限树的输出与使用示例
构建好权限树之后,我们可以将其输出为JSON格式方便前端渲染,也可以直接在后端进行权限校验。下面是将权限树输出为JSON的示例:
// 输出权限树的JSON格式 echo json_encode($permissionTree, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
如果只需要在页面中展示权限结构,也可以用递归的方式遍历输出:
/**
* 递归输出权限列表
* @param array $tree 权限树
* @param int $level 当前层级,用于缩进显示
*/
function printPermissionTree($tree, $level = 0) {
$indent = str_repeat(' ', $level);
foreach ($tree as $item) {
echo $indent . $item['name'] . '<br/>';
if (isset($item['children'])) {
printPermissionTree($item['children'], $level + 1);
}
}
}
// 调用函数输出权限结构
printPermissionTree($permissionTree);上述输出函数会根据权限的层级进行缩进,清晰展示多级权限的从属关系。在实际的权限校验场景中,我们可以递归遍历权限树,判断用户是否拥有某个权限,或者获取用户的所有可访问权限列表。
递归函数的注意事项
使用递归处理权限结构时,需要注意两点:一是如果权限层级过深,可能会导致递归栈溢出,不过通常后台权限的层级不会超过5层,这种情况很少出现;二是原始权限数据如果量很大,每次递归都遍历整个数组会有性能损耗,可以先将原始数据按照父级ID分组,减少每次查找的遍历次数,优化后的递归函数如下:
/**
* 优化后的递归构建权限树函数
* @param array $permissionGroup 按父级ID分组的权限数据
* @param int $parentId 当前父级ID
* @return array 构建好的权限树
*/
function buildPermissionTreeOptimized($permissionGroup, $parentId = 0) {
$tree = [];
// 如果当前父级ID下没有权限,直接返回空数组
if (!isset($permissionGroup[$parentId])) {
return $tree;
}
foreach ($permissionGroup[$parentId] as $permission) {
// 递归查找子权限
$children = buildPermissionTreeOptimized($permissionGroup, $permission['id']);
if (!empty($children)) {
$permission['children'] = $children;
}
$tree[] = $permission;
}
return $tree;
}
// 先按parent_id分组原始权限数据
$permissionGroup = [];
foreach ($permissionList as $permission) {
$permissionGroup[$permission['parent_id']][] = $permission;
}
// 调用优化后的函数构建权限树
$optimizedPermissionTree = buildPermissionTreeOptimized($permissionGroup);通过提前分组,每次递归只需要遍历当前父级ID下的权限,不需要遍历整个原始数组,在权限数据量较大时能显著提升性能。