Verschachtelte, hierarchische UL / LI-Listen auf eine Ebene reduzieren. Flatten hierarchical LI-lists to linear list.

Für ein web2print-Projekt, das verschachtelte HTML-Listen in Tags für InDesign umwandeln sollte, sind wir auf ein Problem gestossen: InDesign stellt verschachtelte Listen nicht wirklich durch verschachtelte Tag-Elemente dar, sondern man erstellt vorab für jede Hierarchie in der Bullet-Liste ein eigenes Absatzformat. Die Herausforderung lag also darin, aus dem HTML-Input per PHP ein XML-Output zu generieren, in dem die Tiefe der Listen-Verschachtelungen zu „flachen“ (flattened) Absatzformaten mit Kennzeichnung der Tiefe umgewandelt werden.

Ein Beispiel macht die Idee deutlich:
Aus einem HTML-Dokument mit einer klassischen, verschachtelten Liste…

Das ist eine Liste:
  • Kapitel 1
  • Kapitel 2
    • Kapitel 2.1
    • Kapitel 2.2
      • Kapitel 2.2.1
  • Kapitel 3
Viel Spass damit!

…sollte etwas in dieser Art generiert werden:

Das ist eine Liste:

Kapitel 1

Kapitel 2

Kapitel 2.1

Kapitel 2.2

Kapitel 2.2.1

Kapitel 3

Viel Spass damit!

Die Lösung setzt phpQuery.php ein – eine (geniale) Adaption der (noch genialeren) jQuery-Bibliothek für PHP statt JavaScript. Die Klasse mit entsprechender Rekursion dazu sah in PHP so aus:

flatten_nested_lists_recursion( $nodes );
      return $nodes->html();
   }
   
   function flatten_nested_lists_recursion ( $el, $depth = 0) {
      $arr = [];
      foreach ($el->children('ul') as $cnt_ul=>$ul) {
       $ul = pq($ul);
        $arr = [];
        foreach ($ul->children('li') as $cnt_li=>$li) {
        $li = pq($li);
        $li->attr('depth', $depth);
        $liClone = $li->clone();
        $liClone->find('ul')->remove();
        $str = trim($liClone->attr('depth').' - '.$liClone->html());
        $arr[] = '

'.$str.'

'; $arr = array_merge($arr, $this->flatten_nested_lists_recursion($li, $depth+1)); } foreach ($arr as $n) { $ul->before($n); } $ul->remove(); } $el->children('ul')->remove(); return $arr; } }

…und in Funktion dann so:


$Engine = new renderEngine();
$input = '...hier der HTML-Code mit ul/li rein...';
echo $Engine->flatten_nested_lists($input);