从平面数据在 PHP 中构建分层 html 标签

发布于 2024-07-08 18:44:28 字数 618 浏览 8 评论 0原文

如何在 PHP 中使用数据构建一组分层标签?

例如,一个嵌套列表:

<div>
    <ul>
        <li>foo
        </li>
        <li>bar
            <ul>
                <li>sub-bar
                </li>
            </ul>
        </li>
    </ul>
</div>

这将由如下所示的平面数据构建:

nested_array = array();
nested_array[0] = array('name' => 'foo', 'depth' => 0)
nested_array[1] = array('name' => 'bar', 'depth' => 0)
nested_array[2] = array('name' => 'sub-bar', 'depth' => 1)

如果它也像示例那样具有良好的格式,那就太好了。

How do you build a hierarchical set of tags with data in PHP?

For example, a nested list:

<div>
    <ul>
        <li>foo
        </li>
        <li>bar
            <ul>
                <li>sub-bar
                </li>
            </ul>
        </li>
    </ul>
</div>

This would be build from flat data like this:

nested_array = array();
nested_array[0] = array('name' => 'foo', 'depth' => 0)
nested_array[1] = array('name' => 'bar', 'depth' => 0)
nested_array[2] = array('name' => 'sub-bar', 'depth' => 1)

It would be nice if it were nicely formatted like the example, too.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

装迷糊 2024-07-15 18:44:28

编辑:添加格式

正如评论中已经说过的,您的数据结构有点奇怪。 我不喜欢使用文本操作(如 OIS),而是更喜欢 DOM:

<?php

$nested_array = array();
$nested_array[] = array('name' => 'foo', 'depth' => 0);
$nested_array[] = array('name' => 'bar', 'depth' => 0);
$nested_array[] = array('name' => 'sub-bar', 'depth' => 1);
$nested_array[] = array('name' => 'sub-sub-bar', 'depth' => 2);
$nested_array[] = array('name' => 'sub-bar2', 'depth' => 1);
$nested_array[] = array('name' => 'sub-sub-bar3', 'depth' => 3);
$nested_array[] = array('name' => 'sub-sub3', 'depth' => 2);
$nested_array[] = array('name' => 'baz', 'depth' => 0);

$doc = new DOMDocument('1.0', 'iso-8859-1');
$doc->formatOutput = true;
$rootNode = $doc->createElement('div');
$doc->appendChild($rootNode);

$rootList = $doc->createElement('ul');
$rootNode->appendChild($rootList);

$listStack = array($rootList); // Stack of created XML list elements
$depth = 0; // Current depth

foreach ($nested_array as $nael) {
    while ($depth < $nael['depth']) {
        // New list element
        if ($listStack[$depth]->lastChild == null) {
            // More than one level at once
            $li = $doc->createElement('li');
            $listStack[$depth]->appendChild($li);
        }
        $listEl = $doc->createElement('ul');
        $listStack[$depth]->lastChild->appendChild($listEl);
        array_push($listStack, $listEl);

        $depth++;
    }

    while ($depth > $nael['depth']) {
        array_pop($listStack);
        $depth--;
    }

    // Add the element itself
    $li = $doc->createElement('li');
    $li->appendChild($doc->createTextNode($nael['name']));
    $listStack[$depth]->appendChild($li);
}

echo $doc->saveXML();

您的格式约定有点奇怪。 将最后一行替换为以下内容即可实现:

printEl($rootNode);

function printEl(DOMElement $el, $depth = 0) {
    $leftFiller = str_repeat("\t", $depth);
    $name = preg_replace('/[^a-zA-Z]/', '', $el->tagName);

    if ($el->childNodes->length == 0) {
        // Empty node
        echo $leftFiller . '<' . $name . "/>\n";
    } else {
        echo $leftFiller . '<' . $name . ">";
        $printedNL = false;

        for ($i = 0;$i < $el->childNodes->length;$i++) {
            $c = $el->childNodes->item($i);

            if ($c instanceof DOMText) {
                echo htmlspecialchars($c->wholeText);
            } elseif ($c instanceof DOMElement) {
                if (!$printedNL) {
                    $printedNL = true;
                    echo "\n";
                }
                printEl($c, $depth+1);
            }
        }

        if (!$printedNL) {
            $printedNL = true;
            echo "\n";
        }

        echo $leftFiller . '</' . $name . ">\n";
    }

}

Edit: Added formatting

As already said in the comments, your data structure is somewhat strange. Instead of using text manipulation (like OIS), I prefer DOM:

<?php

$nested_array = array();
$nested_array[] = array('name' => 'foo', 'depth' => 0);
$nested_array[] = array('name' => 'bar', 'depth' => 0);
$nested_array[] = array('name' => 'sub-bar', 'depth' => 1);
$nested_array[] = array('name' => 'sub-sub-bar', 'depth' => 2);
$nested_array[] = array('name' => 'sub-bar2', 'depth' => 1);
$nested_array[] = array('name' => 'sub-sub-bar3', 'depth' => 3);
$nested_array[] = array('name' => 'sub-sub3', 'depth' => 2);
$nested_array[] = array('name' => 'baz', 'depth' => 0);

$doc = new DOMDocument('1.0', 'iso-8859-1');
$doc->formatOutput = true;
$rootNode = $doc->createElement('div');
$doc->appendChild($rootNode);

$rootList = $doc->createElement('ul');
$rootNode->appendChild($rootList);

$listStack = array($rootList); // Stack of created XML list elements
$depth = 0; // Current depth

foreach ($nested_array as $nael) {
    while ($depth < $nael['depth']) {
        // New list element
        if ($listStack[$depth]->lastChild == null) {
            // More than one level at once
            $li = $doc->createElement('li');
            $listStack[$depth]->appendChild($li);
        }
        $listEl = $doc->createElement('ul');
        $listStack[$depth]->lastChild->appendChild($listEl);
        array_push($listStack, $listEl);

        $depth++;
    }

    while ($depth > $nael['depth']) {
        array_pop($listStack);
        $depth--;
    }

    // Add the element itself
    $li = $doc->createElement('li');
    $li->appendChild($doc->createTextNode($nael['name']));
    $listStack[$depth]->appendChild($li);
}

echo $doc->saveXML();

Your formatting convention is kind of strange. Replace the last line with the following to achieve it:

printEl($rootNode);

function printEl(DOMElement $el, $depth = 0) {
    $leftFiller = str_repeat("\t", $depth);
    $name = preg_replace('/[^a-zA-Z]/', '', $el->tagName);

    if ($el->childNodes->length == 0) {
        // Empty node
        echo $leftFiller . '<' . $name . "/>\n";
    } else {
        echo $leftFiller . '<' . $name . ">";
        $printedNL = false;

        for ($i = 0;$i < $el->childNodes->length;$i++) {
            $c = $el->childNodes->item($i);

            if ($c instanceof DOMText) {
                echo htmlspecialchars($c->wholeText);
            } elseif ($c instanceof DOMElement) {
                if (!$printedNL) {
                    $printedNL = true;
                    echo "\n";
                }
                printEl($c, $depth+1);
            }
        }

        if (!$printedNL) {
            $printedNL = true;
            echo "\n";
        }

        echo $leftFiller . '</' . $name . ">\n";
    }

}
梦毁影碎の 2024-07-15 18:44:28

我有一个问题,关于你的问题,这个问题对于评论字段来说太复杂了。

您想如何将属性数据放入其中? 您需要像 Whory Table™ 一样,

array('html', null, array (
  array( 'div' , null , array( 
    array('ul', array('id'=>'foo'), array( 
      array('li', null, 'foo' ),
        array('li', null, array( 
          array(null,null, 'bar'), 
          array('ul', null, array( 
            array('li', null, 'sub-bar' )
          ))
        ))
      ))
    ))
  ))
));

因为这是以编程方式准确表示 HTML 数据集所需的最小结构。

,从而消除了对“文本节点”元素的需要,这有点作弊

我通过假设如果数组(名称,属性,孩子)

有一个字符串而不是“孩子”的数组,那么它是一个隐式文本 -节点,
name == null 的节点没有标签,因此也是文本节点。

我认为你想要的是一个合适的编程 DOM 生成工具,它将一些现有的 html 解析成树,让你的生活更轻松

FWIW,上面的结构可以很容易地序列化为 html。

function tohtml( $domtree ){ 
   if( is_null($domtree[0]) ){ 
     if( !is_array($domtree[2])){ 
         return htmlentities($domtree[2]);
     }
     die("text node cant have children!"); 
   }
   $html = "<" . $domtree[0]; 
   if( !is_null( $domtree[1] ) )
   {
     foreach( $domtree[1] as $name=>$value ){ 
       $html .= " " . $name . '="' . htmlentities($value) . '"'; 
     }
   }
   $html .= ">" ; 
   if( !is_null($domtree[2]) ){
     if( is_array($dometree[2]) ){ 
        foreach( $domtree[2] as $id => $item ){ 
          $html .= tohtml( $item ); # RECURSION
        } 
     }
     else {
       $html .= htmlentities($domtree[2]);
     }
  }
  $html .= "</" . $domtree[1] . ">"; 
  return $html; 
}

I have a question, regarding your question that is too elaborate for a comment field.

How do you want to fit attribute data in that? You'd need a Whory Table™ like

array('html', null, array (
  array( 'div' , null , array( 
    array('ul', array('id'=>'foo'), array( 
      array('li', null, 'foo' ),
        array('li', null, array( 
          array(null,null, 'bar'), 
          array('ul', null, array( 
            array('li', null, 'sub-bar' )
          ))
        ))
      ))
    ))
  ))
));

because that is the minimal structure required to accurately represent an HTML dataset programmatically.

I've cheated a bit by eliminating the need for "text-node" elements quite as much, by making the assumption if

array( name, attribute, children )

has a string instead of an array for 'children' then its an implicit text-node,
and that nodes with name == null are dont have tags and are thus also text nodes.

What I think you want is a proper programmatic DOM generation tool, which will parse some existing html into a tree to make your life easier

FWIW, the above structure can be serialised into html rather easily.

function tohtml( $domtree ){ 
   if( is_null($domtree[0]) ){ 
     if( !is_array($domtree[2])){ 
         return htmlentities($domtree[2]);
     }
     die("text node cant have children!"); 
   }
   $html = "<" . $domtree[0]; 
   if( !is_null( $domtree[1] ) )
   {
     foreach( $domtree[1] as $name=>$value ){ 
       $html .= " " . $name . '="' . htmlentities($value) . '"'; 
     }
   }
   $html .= ">" ; 
   if( !is_null($domtree[2]) ){
     if( is_array($dometree[2]) ){ 
        foreach( $domtree[2] as $id => $item ){ 
          $html .= tohtml( $item ); # RECURSION
        } 
     }
     else {
       $html .= htmlentities($domtree[2]);
     }
  }
  $html .= "</" . $domtree[1] . ">"; 
  return $html; 
}
扛起拖把扫天下 2024-07-15 18:44:28

你的意思是像

function array_to_list(array $array, $width = 3, $type = 'ul', $separator = ' ', $depth = 0)
{
    $ulSpace = str_repeat($separator, $width * $depth++);
    $liSpace = str_repeat($separator, $width * $depth++);
    $subSpace = str_repeat($separator, $width * $depth);
    foreach ($array as $key=>$value) {
        if (is_array($value)) {
        $output[(isset($prev) ? $prev : $key)] .= "\n" . array_to_list($value, $width, $type, $separator, $depth);
        } else {
            $output[$key] = $value;
            $prev = $key;
        }
    }
    return "$ulSpace<$type>\n$liSpace<li>\n$subSpace" . implode("\n$liSpace</li>\n$liSpace<li>\n$subSpace", $output) . "\n$liSpace</li>\n$ulSpace</$type>";
}

echo array_to_list(array('gg', 'dsf', array(array('uhu'), 'df', array('sdf')), 'sdfsd', 'sdfd')) . "\n";

产品

<ul>
   <li>
      gg
   </li>
   <li>
      dsf
      <ul>
         <li>

            <ul>
               <li>
                  uhu
               </li>
            </ul>
         </li>
         <li>
            df
            <ul>
               <li>
                  sdf
               </li>
            </ul>
         </li>
      </ul>
   </li>
   <li>
      sdfsd
   </li>
   <li>
      sdfd
   </li>
</ul>

这样的东西,我知道如果子列表不以解释开头,那么那里会有一点差距。

就我个人而言,我通常并不关心 HTML 的外观,只要它在 PHP 中易于使用即可。

编辑:好的,如果你先运行它,它就会起作用......:P

function flat_array_to_hierarchical_array(array &$array, $depth = 0, $name = null, $toDepth = 0)
{
    if ($depth == 0) {
        $temp = $array;
        $array = array_values($array);
    }
    if (($name !== null) && ($depth == $toDepth)) {
        $output[] = $name;
    } else if ($depth < $toDepth) {
        $output[] = flat_array_to_hierarchical_array(&$array, $depth + 1, $name, $toDepth);
    }
    while ($item = array_shift($array)) {
        $newDepth = $item['depth'];
        $name = $item['name'];
        if ($depth == $newDepth) {
            $output[] = $name;
        } else if ($depth < $newDepth) {
            $output[] = flat_array_to_hierarchical_array(&$array, $depth + 1, $name, $newDepth);
        } else {
            array_unshift($array, $item);
            return $output;
        }
    }
    $array = $temp;
    return $output;
}

$arr = flat_array_to_hierarchical_array($nested_array);
echo array_to_list($arr);

You mean something like

function array_to_list(array $array, $width = 3, $type = 'ul', $separator = ' ', $depth = 0)
{
    $ulSpace = str_repeat($separator, $width * $depth++);
    $liSpace = str_repeat($separator, $width * $depth++);
    $subSpace = str_repeat($separator, $width * $depth);
    foreach ($array as $key=>$value) {
        if (is_array($value)) {
        $output[(isset($prev) ? $prev : $key)] .= "\n" . array_to_list($value, $width, $type, $separator, $depth);
        } else {
            $output[$key] = $value;
            $prev = $key;
        }
    }
    return "$ulSpace<$type>\n$liSpace<li>\n$subSpace" . implode("\n$liSpace</li>\n$liSpace<li>\n$subSpace", $output) . "\n$liSpace</li>\n$ulSpace</$type>";
}

echo array_to_list(array('gg', 'dsf', array(array('uhu'), 'df', array('sdf')), 'sdfsd', 'sdfd')) . "\n";

produces

<ul>
   <li>
      gg
   </li>
   <li>
      dsf
      <ul>
         <li>

            <ul>
               <li>
                  uhu
               </li>
            </ul>
         </li>
         <li>
            df
            <ul>
               <li>
                  sdf
               </li>
            </ul>
         </li>
      </ul>
   </li>
   <li>
      sdfsd
   </li>
   <li>
      sdfd
   </li>
</ul>

I know theres a little gap there if a sub list don't start with an explanation.

Personally I usually don't really care how the HTML looks as long as its easy to work with in PHP.

Edit: OK, it works if you run it through this first ... :P

function flat_array_to_hierarchical_array(array &$array, $depth = 0, $name = null, $toDepth = 0)
{
    if ($depth == 0) {
        $temp = $array;
        $array = array_values($array);
    }
    if (($name !== null) && ($depth == $toDepth)) {
        $output[] = $name;
    } else if ($depth < $toDepth) {
        $output[] = flat_array_to_hierarchical_array(&$array, $depth + 1, $name, $toDepth);
    }
    while ($item = array_shift($array)) {
        $newDepth = $item['depth'];
        $name = $item['name'];
        if ($depth == $newDepth) {
            $output[] = $name;
        } else if ($depth < $newDepth) {
            $output[] = flat_array_to_hierarchical_array(&$array, $depth + 1, $name, $newDepth);
        } else {
            array_unshift($array, $item);
            return $output;
        }
    }
    $array = $temp;
    return $output;
}

$arr = flat_array_to_hierarchical_array($nested_array);
echo array_to_list($arr);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文