MezData-Logo Lösungen Button :WEBPUBLISH: XML Parsen mit PHP

Wie krieg ich meine Bookmarks aus dem Browser auf meine Web-Site?

Synopsis: [URL Manager Pro 3.4.1] [XML Bookmark Exchange Language (XBEL)] [XBEL Resource Page] [O'Reilly Programmieren mit PHP]

Browser können eine HTML-Ausgabe der Bookmarks erzeugen -aber das ist zur Verwaltung nicht praktisch. Für Mac OS X gibt es eine Software URL Manager Pro, die eine systemweite Verwaltung von URL erlaubt, besonders praktisch ist die Organisation in Ordnern mit Komentaren usw.. Als Export kommt neben HTML auch Text mit Tab und XBEL in Frage.

Erster Versuch mit Text mit Tab und regulären Ausdrücken

Bookmark-Quelledatei: elektro.text / PHP-Skript von Daniel Beck:

<?php
/* Datei laden. Format:
"TITEL	URL	BESCHREIBUNG" (trennung durch tabs) oder "TITEL	URL" oder "-	" ("minus tab" wird zu ner <hr/>)
für php < 4.3.0: join('', file('text.txt'));
*/
$text = file_get_contents('text.txt');
$text = str_replace("\r", "\n", $text); // Mac macht \r statt \n
$text = str_replace("\n-\t", "\n<hr/>\n", $text); // Erstmal alle "- " zu <hr/> machen:
/* Links mit Titel und Beschreibung matchen und umwandeln. Keine Zeilenumbrüche in der Beschreibung */
$text = preg_replace('#(.+)\\t(.+)\\t(.+)#i', '<a href="\\2">\\1</a> \\3</p>' . "\n", $text);
/* Links mit Titel matchen und umwandeln, die mit Beschreibung wurden ja schon geändert. */
$text = preg_replace('#(.*)\\t(.*)#i', '<p><a href="\\2">\\1</a></p>' . "\n", $text);
echo $text;// Ausgabe
?>

Ergebnis des PHP-Skripts

Elektro-Bastel 5 Watt Lumileds Luxeon Star von Diana Electronic Superhelle LEDs

ElektrowiderstŠnde - Zusatzinfos WiderstŠnde im KFZ

ElektrowiderstŠnde Tankanzeige KFZ Messgeber im Tank

Fuellstandanzeige Digitale Erfassung des FŸllstandes

Ist leider nicht so berauschend, vor allem die Umlaute machen Ärger trotz UTF-8. Ist auch nicht besonders flexibel. Daher nun die volle Ladung:

Zweiter Versuch XML-Export: XBEL

Bookmark-Quelledatei: elektro.xbel

<?xml version="1.0"?>
<!DOCTYPE xbel>
<!-- Created by URL Manager Pro - http://www.url-manager.com -->
<xbel version="1.0">
<folder folded="no">
<title>Elektro-Bastel</title>
	<bookmark href="http://www.dianaelectronic.de/led/led20.htm">
	<title>5 Watt Lumileds Luxeon Star von Diana Electronic</title>
	<desc>Superhelle LEDs</desc>
	</bookmark>
	<bookmark href="http://www.physik-am-auto.de/elwi/elwi_z1.htm">
	<title>Elektrowiderst&#228;nde - Zusatzinfos</title>
	<desc>Widerst&#228;nde im KFZ</desc>
	</bookmark>
	<bookmark href="http://www.physik-am-auto.de/elwi/">
	<title>Elektrowiderst&#228;nde</title>
	<desc>Tankanzeige KFZ Messgeber im Tank</desc>
	</bookmark>
	<bookmark href="http://homepages.fh-regensburg.de/~hem39093/fuflab/Fuellstandsanzeige.htm">
	<title>Fuellstandanzeige</title>
	<desc>Digitale Erfassung des F&#252;llstandes</desc>
	</bookmark>
</folder>
</xbel>

Schickes Format XML Bookmark Exchange Language (XBEL) nur wie krieg ich das nun auf meine Seite?

Lesen hilft: Buch O'Reilly "Programmieren mit PHP" im Beispielteil gibt es Quelltexte zum Download -Kapitel 11 Vers 10 PHP! Beispiel für einen XML-Parser mit PHP..

Beim Parsen zuschauen

xparse.php

class BookList {
  var $parser;
  function BookList ($filename) {
    $this->parser = xml_parser_create();
    xml_set_object($this->parser, &$this);
    xml_set_element_handler($this->parser, 'start_element', 'end_element');
    xml_set_character_data_handler($this->parser, 'cdata');
    xml_parser_set_option($this->parser, XML_OPTION_TARGET_ENCODING, 'UTF-8');
    $x = join("", file($filename));
    xml_parse($this->parser, $x);
    xml_parser_free($this->parser);
  }
  function start_element ($p, $element, &$attributes) {
    $element = strtolower($element);
    foreach ($attributes as $key => $v){
      echo "$element : $key = $v <br>";
    }    
  }
  function end_element ($p, $element) {
    $element = strtolower($element);
    echo 'end: ' . $element . '<br>';
  }
  function cdata ($p, $text) {
    if (strlen($text) > 1 ) echo 'cdata: ' . $text . ' ';    
  }
};
$my_library = new BookList ("elektro.xbel"); // main program code

Beim Parsen gleich formatieren

Problem: Das Tag title wird in zwei verschiedenen Zusammenhängen (Kontexten) verwendet:

Somit muss man sich beim Parsen merken was gerade bearbeitet wird.

xparse-direct.php

<?php

class BookList {
  var $parser;
  var $bookfolder = '';
  var $href;
  var $titel;
  var $desc;
  var $cur_tag;

  function BookList ($filename) {
    $this->parser = xml_parser_create();
    xml_set_object($this->parser, &$this);
    xml_set_element_handler($this->parser, 'start_element', 'end_element');
    xml_set_character_data_handler($this->parser, 'cdata');
    xml_parser_set_option($this->parser, XML_OPTION_TARGET_ENCODING, 'UTF-8');
    xml_parse($this->parser, file_get_contents($filename));
    xml_parser_free($this->parser);
  }
  function start_element ($p, $element, &$attributes) {
    $element = strtolower($element);
    $this->cur_tag = $element;
    switch($element){
      case 'folder':
        $this->bookfolder = 'folder';
        break;
      case 'bookmark':
        $this->bookfolder = 'bookmark';
        $this->href = $attributes['HREF'];
        break; 
      case 'title' :
        $this->titel='';
        break;
      case 'desc' :
        $this->desc='';
        break;     
    }    
  }
  function end_element ($p, $element) {
    $element = strtolower($element);
    if ($this->bookfolder == 'folder' && $element == 'title')
      echo '<h3>'. $this->titel .'</h3>';
    if ($element == 'bookmark'){
      echo '<p> <a href= "' . $this->href . '">'; 
      if (trim($this->titel)) echo $this->titel;
      else echo  $this->href;
      echo'</a> ' . $this->desc . '</p>';
      $this->titel='';
      $this->desc='';
    }    
  }
  function cdata ($p, $text) {
    if(trim($text)){
      switch($this->cur_tag){
        case 'title':
          $this->titel .= $text;    
          break;
        case 'desc':
          $this->desc .= $text;
          break;  
      }
    }    
  }
};
$my_library = new BookList ("elektro.xbel");
?>

OOA: Mit Generalisierung zum Klassendiagramm

Nun möchte ich mehr Komfort, die Datenstruktur soll OO sein, zunächst eine Analyse der Klassen [http://pyxml.sourceforge.net/topics/xbel/docs/html/public-text.html].

Diese Klassendiagramm-Skizze lässt sich so leider nicht in PHP realisieren, keine abstrakten Klassen...

OOP: Die Lösung

xparse-oop.php

/* hier sollte die Klasse Xmain als abstrakte Mutter einiger Klassen stehen, scheint aber in PHP nicht vorgesehen zu sein */
class Separator {
  function display(){
   echo '<hr>';
  }
}
class Xnode { // abstrakte Klasse
  var $title;
  //var $info;
  var $desc;
  //var $added; 
}
class Folder extends Xnode {
  var $folded;
  var $nodes=array(); // Liste der Unterknoten
  function display(){
    echo '<h3>[' . $this->title . ']  ' . $this->desc . '</h3>';
    if ($this->nodes)
     foreach ($this->nodes as $n)
       $n->display();
  }
}
class Bookmark extends Xnode {
  var $href;
  //var $visited;
  //var $modified;
  function display(){
    echo '<p> <a href= "' . $this->href . '"> '; 
    if (trim($this->title)) echo $this->title;
    else echo  $this->href;
    echo'</a> ' . $this->desc . '</p>';
  }
}
class Xbel extends Xnode {
  var $parser;
  var $nodes = array();
  var $cur_tag;

  function Xbel ($filename) {
    $this->parser = xml_parser_create();
    xml_set_object($this->parser, &$this);
    xml_set_element_handler($this->parser, 'start_element', 'end_element');
    xml_set_character_data_handler($this->parser, 'cdata');
    xml_parser_set_option($this->parser, XML_OPTION_TARGET_ENCODING, 'UTF-8');
    xml_parse($this->parser, file_get_contents($filename));
    xml_parser_free($this->parser);
  }
  function start_element ($p, $element, &$attributes) {
    $element = strtolower($element);
    $this->cur_tag = $element;
    switch($element){
      case 'folder':
      case 'xbel':
        array_push($this->nodes, new Folder());
        break;
      case 'bookmark':
        $this->bookfolder = 'bookmark';
        $bo = new Bookmark();
        $bo->href = $attributes['HREF'];
        array_push($this->nodes, $bo);
        break; 
      case 'separator':
        array_push($this->nodes, new Separator());
        break;    
    }    
  }
  function end_element ($p, $element) {
    $element = strtolower($element);
    switch($element){
      case 'folder':
      case 'xbel':
      case 'bookmark':
      case 'separator':
        $this->nodes[count($this->nodes)-2]->nodes[] = $this->nodes[count($this->nodes)-1];
        array_pop($this->nodes);
    }        
  }
  function cdata ($p, $text) {
    if(trim($text)){
      switch($this->cur_tag){
        case 'title':
          $this->nodes[count($this->nodes)-1]->title .= $text;    
          break;
        case 'desc':
          $this->nodes[count($this->nodes)-1]->desc .= $text;
          break;  
      }   
    }    
  }
  function display(){ // Zeigt alles an
    echo "<h2> $this->title $this-desc </h2>";
    if ($this->nodes) foreach ($this->nodes as $n)
      $n->display(); 
  }
  function display_top(){ // Zeigt nur Tiefe 1 an und Ordner werden als Links anzeigt
    $bu = $this->nodes[0];
    foreach ($bu->nodes as $n){
      if (get_class($n)=='folder'){
        echo '<p> [<a href="'. $_SERVER['PHP_SELF'] . '?sub=' .  $n->title . '">'. $n->title . '</a>] ' . $n->desc . '</p>';
      }
      else $n->display();
    }
  }
  function display_sub($tit){ // Zeigt Unterordner namens $tit an
    $bu = $this->nodes[0];
    foreach ($bu->nodes as $n){
      if (get_class($n)!='folder' || $n->title != $tit){
        continue;
      }
      else{
        $n->display();
        break;
      }  
    }
  }
};

To be..