You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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的层级深度

我整理了一个更清晰的实现思路,分三步来处理:

  1. 先把每个OU的DN解析成完整的OU路径数组,同时保留原始ID和DN
  2. 构建一个OU路径到ID的映射表,方便快速查找父级OU的ID
  3. 遍历所有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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.08 10:40:29