PHP - 嵌套简码到数组

问题描述 投票:0回答:2

我有以下 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] => 
        )

这是我的 PHP 函数

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;
}

我哪里出错了?

php regex shortcode
2个回答
2
投票

将其转换为 HTML 标签并使用 DOM 解析器的想法怎么样?

$codes = <<<'CODES'
[column]
    [row]
        [column][/column]
        [column][/column]
    [/row]
[/column]
CODES;
$html = str_replace(['[', ']'], ['<', '>'], $codes);
libxml_use_internal_errors(true);
$dom = new \DOMDocument();
$dom->loadHTML($html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
echo $dom->saveHTML();

输出

<column>
    <row>
        <column></column>
        <column></column>
    </row>
</column>

现在你可以按照你想要的方式遍历 DOMDocument,甚至以简单的方式进行操作。

foreach($dom->getElementsByTagName('row') as $row) {
    foreach($row->getElementsByTagName('column') as $column) {
        $column->appendChild($dom->createTextNode('test'));
        $column->setAttribute('class', 'test');
    }
}

echo $dom->saveHTML();
<column>
    <row>
        <column class="test">test</column>
        <column class="test">test</column>
    </row>
</column>

更新

当将其作为 DOM 文档时,您可以遍历整个树并转换为数组。

查看演示:https://3v4l.org/h1eMR

function getTags($element, $tags = [])
{
    $tag = ['tagName' => $element->tagName];

    if ($element->hasAttributes()) {
        foreach ($element->attributes as $attribute) {
            $tag['attributes'][$attribute->name] = $attribute->value;
        }
    }

    if ('' !== ($nodeValue = trim($element->textContent)) && false === $element->hasChildNodes()) {
        $tag['nodeValue'] = $nodeValue;
    }

    if ($element->hasChildNodes()) {
        foreach ($element->childNodes as $childElement) {
            if ($childElement->nodeType !== XML_ELEMENT_NODE) {
                continue;
            }
            $tag[] = getTags($childElement, $tags);
        }
    }
    $tags[] = $tag;

    return $tags;
}

$tags = getTags($dom->documentElement);
echo var_export($tags, true);

0
投票

这就是我最终的做法,感谢@Markus Zeller 和另一篇我现在找不到的 SO 帖子。

// 1. Convert shortcode to XML like syntax
$xmlString = str_replace(['[', ']'], ['<', '>'], $shortcode);

// 2. Convert to xml using simple_xml_load_string
$xml = simplexml_load_string($html, "SimpleXMLElement", LIBXML_NOCDATA);

// 3. Convert to JSON
$json = json_encode($xml);

// 4. JSON to Array
$array = json_decode($json, true);

虽然这有效,但

xml -> json -> array
创建了自己的语法,我希望它具有特定格式的
has_children
children
键,因此我最终使用了这个函数

protected function xmlToArray($xml) {
    $result = [];
    $result[] = [
        'tag' => $xml->getName(),
        'attributes' => [],
        'text' => '',
        'has_children' => count($xml->children()) > 0,
        'children' => [],
    ];
    foreach ($xml->attributes() as $key => $value) {
        $result[0]['attributes'][$key] = (string)$value;
    }
    $children = $xml->children();
    if (count($children) === 0) {
        $result[0]['text'] = (string)$xml;
        return $result;
    }
    foreach ($children as $child) {
        $childArray = $this->xmlToArray($child);
        $result[0]['children'][] = $childArray[0];
    }
    return $result;
}

这是我得到的结果。

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

            [text] => 
            [has_children] => 1
            [children] => Array
                (
                    [0] => Array
                        (
                            [tag] => row
                            [attributes] => Array
                                (
                                )

                            [text] => 
                            [has_children] => 1
                            [children] => Array
                                (
                                    [0] => Array
                                        (
                                            [tag] => column
                                            [attributes] => Array
                                                (
                                                )

                                            [text] => 
                                            [has_children] => 
                                            [children] => Array
                                                (
                                                )

                                        )

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

                                            [text] => 
                                            [has_children] => 
                                            [children] => Array
                                                (
                                                )

                                        )

                                )

                        )

                )

        )

)
© www.soinside.com 2019 - 2024. All rights reserved.