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

PHP嵌套短代码转数组函数异常:多子元素解析错误排查

短代码转嵌套数组的解析问题排查与修复

问题现象

正常情况

解析单层嵌套短代码时,函数可生成正确的嵌套数组:
输入短代码:

[column]
    [row]
        [column][/column]
    [/row]
[/column]

生成的正确数组结构:

Array
(
    [0] => Array
        (
            [tag] => column
            [attributes] => Array
                (
                )

            [content] => Array
                (
                    [0] => Array
                        (
                            [tag] => row
                            [attributes] => Array
                                (
                                )

                            [content] => Array
                                (
                                    [0] => Array
                                        (
                                            [tag] => column
                                            [attributes] => Array
                                                (
                                                )

                                            [content] => 
                                        )

                                )

                        )

                )

        )

)

错误情况

当子元素包含多个同标签短代码时,第二个标签会被错误解析为根级别元素:
输入短代码:

[column]
    [row]
        [column][/column]
        [column][/column]
    [/row]
[/column]

生成的错误数组结构:

Array
(
    [0] => Array
        (
            [tag] => column
            [attributes] => Array
                (
                )

            [content] => Array
                (
                    [0] => Array
                        (
                            [tag] => row
                            [attributes] => Array
                                (
                                )

                            [content] => Array
                                (
                                    [0] => Array
                                        (
                                            [tag] => column
                                            [attributes] => Array
                                                (
                                                )

                                            [content] => 
                                        )

                                )

                        )

                )

        )

    [1] => Array
        (
            [tag] => column
            [attributes] => Array
                (
                )

            [content] => 
        )
)

问题代码

protected function shortCodeToArray($inputString)
{
    $itemArray = [];
    $openingTag = '/\[(\w+)(?:\s+([^\]]*))?\](.*?)(\[\/\1\]|$)/s';
    preg_match_all($openingTag, $inputString, $matches, PREG_SET_ORDER);
    foreach ($matches as $match) {
        $tagName = $match[1];
        $paramString = isset($match[2]) ? $match[2] : '';
        $content = $match[3];
        $nestedShortcodes = $this->shortCodeToArray($content);
        $itemArray[] = [
            'tag' => $tagName,
            'attributes' => $this->parseShortcodeParameters($paramString),
            'content' => is_array($nestedShortcodes) && !empty($nestedShortcodes) ? $nestedShortcodes : $content,
        ];
    }
    return $itemArray;
}

protected function parseShortcodeParameters($paramString)
{
    $params = [];
    preg_match_all('/(\w+)\s*=\s*["\']([^"\']+)["\']/', $paramString, $matches);
    for ($i = 0; $i < count($matches[0]); $i++) {
        $paramName = $matches[1][$i];
        $paramValue = $matches[2][$i];
        $params[$paramName] = $paramValue;
    }
    return $params;
}

问题根源

核心问题在于正则表达式/\[(\w+)(?:\s+([^\]]*))?\](.*?)(\[\/\1\]|$)/s的非贪婪匹配逻辑:

  • .*?会在遇到第一个匹配的闭合标签[/tag]时立即停止匹配,无法识别嵌套层级。
  • 以错误场景为例,最外层的[column]会被匹配到第一个子[/column]就结束,剩下的第二个[column][/column]和外层的[/row][/column]被截断,导致第二个[column]被当成独立的根级别标签匹配。

修复方案

修改正则表达式为支持嵌套标签的递归匹配模式,确保能正确匹配到对应层级的闭合标签:

protected function shortCodeToArray($inputString)
{
    $itemArray = [];
    // 支持嵌套标签的递归正则
    $openingTag = '/\[(\w+)(?:\s+([^\]]*))?\]((?:[^[]+|\[(?!\/\1\])|(?R))*)\[\/\1\]/s';
    preg_match_all($openingTag, $inputString, $matches, PREG_SET_ORDER);
    foreach ($matches as $match) {
        $tagName = $match[1];
        $paramString = isset($match[2]) ? $match[2] : '';
        $content = trim($match[3]); // 去除首尾空白,避免空内容干扰
        $nestedShortcodes = $this->shortCodeToArray($content);
        $itemArray[] = [
            'tag' => $tagName,
            'attributes' => $this->parseShortcodeParameters($paramString),
            'content' => !empty($nestedShortcodes) ? $nestedShortcodes : $content,
        ];
    }
    return $itemArray;
}

// parseShortcodeParameters 函数保持不变
protected function parseShortcodeParameters($paramString)
{
    $params = [];
    preg_match_all('/(\w+)\s*=\s*["\']([^"\']+)["\']/', $paramString, $matches);
    for ($i = 0; $i < count($matches[0]); $i++) {
        $paramName = $matches[1][$i];
        $paramValue = $matches[2][$i];
        $params[$paramName] = $paramValue;
    }
    return $params;
}

正则说明

新正则/\[(\w+)(?:\s+([^\]]*))?\]((?:[^[]+|\[(?!\/\1\])|(?R))*)\[\/\1\]/s的关键部分:

  • (?:[^[]+|\[(?!\/\1\])|(?R))*:匹配标签内容,包含三种情况:
    1. [^[]+:匹配非[开头的文本
    2. \[(?!\/\1\]):匹配不是当前标签闭合标签的[开头内容
    3. (?R):递归匹配整个正则,处理嵌套的短代码
  • 确保只会匹配到与当前开标签对应的闭合标签,不会被嵌套的同标签干扰。

修复效果

重新解析错误场景的短代码,会生成正确的嵌套数组:第二个column会被包含在row的content中,根级别只有一个column元素。

内容的提问来源于stack exchange,提问作者Ibrahim Azhar Armar

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 00:57:47