PHP处理LDAP OU列表时无法根据DN正确生成层级结构及parent_id的问题修复求助
PHP处理LDAP OU列表时无法根据DN正确生成层级结构及parent_id的问题修复求助
我现在遇到个棘手的问题:我写的PHP代码没法正确处理LDAP返回的OU列表,不仅排序混乱,过段时间还会输出错误的parent_id数据。我的目标是根据OU的DN值,找出它的父级OU对应的ID,把这个ID设为正确的parent_id,最终把无序的列表整理成按层级排列的结构。
原始OU数据
我拿到的原始数组是这样的:
[ { "id": 0, "dn": "OU=Microsoft Exchange Security Groups,DC=local,DC=com" }, { "id": 1, "dn": "OU=Domain Controllers,DC=local,DC=com" }, { "id": 2, "dn": "OU=DIT,OU=DVIOU,DC=local,DC=com" }, { "id": 3, "dn": "OU=Service Accounts,DC=local,DC=com" }, { "id": 4, "dn": "OU=STT,OU=DIT,OU=DVIOU,DC=local,DC=com" }, { "id": 5, "dn": "OU=STX,OU=DIT,OU=DVIOU,DC=local,DC=com" }, { "id": 6, "dn": "OU=DVIOU,DC=local,DC=com" }, { "id": 7, "dn": "OU=DPW,OU=DVIOU,DC=local,DC=com" }, { "id": 8, "dn": "OU=STT,OU=DPW,OU=DVIOU,DC=local,DC=com" }, { "id": 9, "dn": "OU=OLG,OU=DVIOU,DC=local,DC=com" }, { "id": 10, "dn": "OU=DIT_Consultants,OU=DIT,OU=DVIOU,DC=local,DC=com" }, { "id": 11, "dn": "OU=911,OU=DVIOU,DC=local,DC=com" }, { "id": 12, "dn": "OU=Users,OU=911,OU=DVIOU,DC=local,DC=com" }, { "id": 13, "dn": "OU=PERB,OU=DVIOU,DC=local,DC=com" }, { "id": 14, "dn": "OU=STX,OU=PERB,OU=DVIOU,DC=local,DC=com" } ]
期望的结果示例
比如其中一个条目我期望是这样的:
{ "name": "STT", "id": 4, "level": 2, "parent_id": 2, "parent_name": "DIT", "dn": "OU=STT,OU=DIT,OU=DVIOU,DC=local,DC=com" }
(注:这里修正了示例里的parent值,DIT的ID是2,应该是笔误)
但目前我的代码根本没法根据DN值正确排序和关联父级,我知道得重写部分逻辑,但实在摸不着头绪,下面是我现在的代码:
$fl; $fltree; $n = 1; $subparent; $prevparent; $prevDN; foreach ($values as $key => $value) { if($key == 'count') continue; $d = explode(',', str_replace(",DC=local,DC=com", "", $value['dn'])); $fl = (str_replace("OU=", "", ($d))); $fo; foreach ($fo as $ke => $va) { $r = str_replace("OU=".$fl[0].",", "", $value['dn']); if ($r == $va['dn']) $prevparent = array($ke,$va); } //get level $level = 1; foreach (array_reverse($fl) as $k => $v) { //$p = $fl[0]; if ($fl[0] == $v) { $level = $k; //$prevparent = $p; break; } } if (count($fl) == 1) { //echo ('key ' . $key .' name: ' . $fl[0] .' parent_id: ' .null . ' dn: ' . ($value['dn'])."</br>"); $fo[$key] = (array('name' => $fl[0], 'id' => $key, 'parent_id' => 0, 'dn' => $value['dn'])); //$n-1; } else { //echo ('key ' . $v .' name: ' . $fl[0] .' parent_id: ' . $key . ' dn: ' . ($value['dn'])."</br>"); $fo[$key] = (array('name' => $fl[0], 'id' => $key, 'level' => $level, 'parent_id' => ($subparent[0] == $fl[1] ? ($subparent[1]) : $prevparent[0]), 'parent_name' => $prevparent[1]['name'], 'dn' => $value['dn'])); //echo json_encode($subparent)." === ".json_encode($fl[1])."</br>"; $n++; } $subparent = array($fl[0], $key); } } } return $fo;
问题分析与修复方案
你的代码逻辑绕了很多弯路,核心问题在于:
- 没有先从DN解析出完整的层级路径,直接处理单个OU时很难准确找到父级
- 依赖循环中的临时变量(比如
$subparent、$prevparent)来记录父级,这种方式很容易因为遍历顺序混乱而出错 - 层级计算的逻辑也有问题,没有正确对应DN的层级深度
我整理了一个更清晰的实现思路,分三步来处理:
- 先把每个OU的DN解析成完整的OU路径数组,同时保留原始ID和DN
- 构建一个OU路径到ID的映射表,方便快速查找父级OU的ID
- 遍历所有OU,根据解析出的父级路径找到对应的parent_id,同时计算层级
修复后的完整代码
function processOUs($values) { $ouList = []; $pathToIdMap = []; $baseDomain = ",DC=local,DC=com"; // 第一步:预处理所有OU,解析路径并构建映射表 foreach ($values as $key => $value) { if ($key === 'count') continue; // 移除域名部分,拆分DN为OU片段 $dnWithoutDomain = str_replace($baseDomain, "", $value['dn']); $ouSegments = array_reverse(explode(',', $dnWithoutDomain)); // 反转后从根到子的顺序 $cleanSegments = array_map(function($segment) { return str_replace("OU=", "", $segment); }, $ouSegments); // 构建完整路径字符串(比如"DVIOU/DIT/STT") $fullPath = implode('/', $cleanSegments); $ouList[$key] = [ 'id' => $key, 'dn' => $value['dn'], 'name' => $cleanSegments[count($cleanSegments)-1], // 最后一个是当前OU的名称 'pathSegments' => $cleanSegments, 'fullPath' => $fullPath ]; // 把当前路径和ID存入映射表 $pathToIdMap[$fullPath] = $key; } // 第二步:遍历每个OU,计算parent_id和层级 $result = []; foreach ($ouList as $ou) { $level = count($ou['pathSegments']) - 1; // 根OU层级为0,子OU依次加1 $parentId = 0; $parentName = null; if ($level > 0) { // 去掉最后一个片段,得到父级路径 $parentSegments = array_slice($ou['pathSegments'], 0, -1); $parentPath = implode('/', $parentSegments); // 从映射表中获取父级ID $parentId = $pathToIdMap[$parentPath] ?? 0; // 获取父级名称 $parentName = $parentSegments[count($parentSegments)-1]; } $result[$ou['id']] = [ 'name' => $ou['name'], 'id' => $ou['id'], 'level' => $level, 'parent_id' => $parentId, 'parent_name' => $parentName, 'dn' => $ou['dn'] ]; } // 可选:按层级和名称排序,让结果更规整 usort($result, function($a, $b) { if ($a['level'] !== $b['level']) { return $a['level'] - $b['level']; } return strcmp($a['name'], $b['name']); }); return $result; }
代码关键部分解释
- 预处理阶段:先把每个OU的DN拆分成从根到子的片段(比如
OU=STT,OU=DIT,OU=DVIOU,...会被拆成['DVIOU', 'DIT', 'STT']),然后构建路径字符串和ID的映射,这样后续查找父级时直接通过路径就能拿到ID,不用再循环遍历。 - 父级关联阶段:对于每个OU,只要去掉路径的最后一个片段就是父级的完整路径,直接从映射表取ID即可,逻辑非常清晰,不会出错。
- 层级计算:层级就是路径片段的数量减一,根OU(只有一个片段)层级为0,子OU依次递增,完全符合DN的层级结构。
- 可选排序:最后用
usort按层级和名称排序,让结果更规整,符合直观的树状顺序。
你可以测试一下这个代码,应该能正确生成你想要的parent_id和层级结构,而且不会因为遍历顺序导致错误。
内容来源于stack exchange
相关产品推荐
相关产品推荐

