简体   繁体   中英

php: How to convert HTML headings (h2,h3,h4) to nested unordered list?

input array with html headings from a WordPress post

<?php
$headings = array(
0 => "<h2>Number One</h2>",
1 => "<h2>Number Two</h2>",
2 => "<h2>Number Three</h2>",
3 => "<h3>Number Four</h3>",
4 => "<h2 id='iamanexistingid'>Number Five</h2>",
5 => "<h3>Number Six</h4>",
6 => "<h4>Number Seven</h2>",
7 => "<h2>Number Eight</h2>"
);

Now I want to generate a structure in a nested unordered list.

Output

<ul>
<li>Number One</li>
<li>Number Two</li>
<li>Number Three
   <ul>
      <li>Number Four</li>
   </ul>
<li>Number Five</li>
<li>Number Five
   <ul>
      <li>Number Six
         <ul>
            <li>Number Seven</li>
         </ul>
     </li>
   </ul>
</li>
<li>Number Eight</li>
</ul>

Is there simple way to generate this with Simple HTML DOM Parser? Or a trick?

My initial solution does not really work for levels deeper than one level and is not good in general. I also can't figure out how to handle a H4 following a H3 or a H3 follow a H3.

<?php

$headings = array(
0 => "<h2>H2 Number One</h2>",
1 => "<h2>H2 Number Two</h2>",
2 => "<h2>H2 Number Three</h2>",
3 => "<h3>H3 Number Four</h3>",
4 => "<h2 id='iamanexistingid'>H2 Number Five</h2>",
5 => "<h3>H3 Number Six</h4>",
6 => "<h4>H4 Number Seven</h2>",
7 => "<h3>H3 Number Eight</h2>",
8 => "<h2>H2 Number Nine</h2>"
);

$array = array();
$lastlevel = 2;

foreach ($headings as $key => $value) {

  preg_match("/\<(?<name>\w+)(?<attributes>\s+[^>]*|)>/", $value, $matches);
  $tagname = $matches[1];
  $currentlevel = str_replace("h", "", "$tagname");

  if($currentlevel == $lastlevel OR $currentlevel < $lastlevel) {
      array_push($array, $value);
  } elseif ($currentlevel > $lastlevel) {
      array_push($array, [$value]);
  }

  $lastlevel = $currentlevel;

}

echo printArrayList($array);

function printArrayList($array)
{
    echo "<ul>";

    foreach($array as $k => $v) {
        if (is_array($v)) {

            printArrayList($v);
            continue;
        }

        echo "<li>" . $v . "</li>";
    }

    echo "</ul>";
}

Found an answer myself. Uses code from https://github.com/shazahm1/Easy-Table-of-Contents

function generateToc($matches) {
    $list ='';
    $current_depth      = 7;
    $numbered_items     = array();
    $numbered_items_min = null;

    array_walk($matches,"removelines");

    // find the minimum heading to establish our baseline
    //for ( $i = 0; $i < count( $matches ); $i ++ ) {
    foreach ( $matches as $i => $match ) {
      if ( $current_depth > $matches[ $i ][2] ) {
        $current_depth = (int) $matches[ $i ][2];
      }
    }

    $numbered_items[ $current_depth ] = 0;
    $numbered_items_min               = 7;

    foreach ( $matches as $i => $match ) {

      $level = $matches[ $i ][2];
      $count = $i + 1;

      if ( $current_depth == (int) $matches[ $i ][2] ) {

        $list .= '<li>';
      }

      // start lists
      if ( $current_depth != (int) $matches[ $i ][2] ) {

        for ( $current_depth; $current_depth < (int) $matches[ $i ][2]; $current_depth++ ) {

          $numbered_items[ $current_depth + 1 ] = 0;
          $list .= '<ul><li>';
        }
      }

      $list .= strip_tags($match);

      // end lists
      if ( $i != count( $matches ) - 1 ) {

        if ( $current_depth > (int) $matches[ $i + 1 ][2] ) {

          for ( $current_depth; $current_depth > (int) $matches[ $i + 1 ][2]; $current_depth-- ) {

            $list .= '</li></ul>';
            $numbered_items[ $current_depth ] = 0;
          }
        }

        if ( $current_depth == (int) @$matches[ $i + 1 ][2] ) {

          $list .= '</li>';
        }
      }
    }

    return '<ul class="toc">' . $list . "</li></ul>";
}

echo generateToc($headings);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM