Bernhard Peissl: XML-Parsen: Scoping in Klassen mit Hilfe von xml_set_object()

Da ist mir doch glatt der Schwanzabschneider zuvorgekommen. Während dem Tippen (!) dieses (eigentlich Antwort-)Postings auf eine Frage die ich selber aufgeworfen habe, hat er mir die Nachricht doch glatt aus dem Forum geschnitten ! *grrr*

wow, der Ursprungs-Thread steht sogar schon im Archiv:
http://www.teamone.de/selfaktuell/forum/archiv/2001/8/28153/#m146716
-> Bin begeistert :-)

Trotzkopf der ich nunmal bin schreib ich die Antwort eben als neuen Thread nochmal rein. Ich finde nämlich dass er sich ganz gut fürs Archiv eignen würde. Schliesslich werden sich bald mehr PHPler mit XML beschäftigen müssen, und der eine oder andere vielleicht vor dem selben Problem stehen ;-)

in diesem Sinne .... Fürs Protokoll: ----

Grüssi nochmal,

so, ich hab jetzt gefunden wies geht :-) PHP stellt eine eigene Methode zur Verfügung, mit der man einen Parser "Teil einer Klasse" werden lassen kann. Eine bessere Formulierung ist mir leider nicht eingefallen ;-)

Gibts vielleicht irgendein keyword, mit dem man eine Variable nach unten hin (scopemässig) freigeben kann?

Für diejenigen die mit den XML-Funktionen von PHP nicht so vertraut sind, schreib ich nen kurzen Kommentar dazu was sie - im Groben - machen:

// Die Klasse XML soll hier nur ein <Menu>-Element in einer Datei
// finden, den Wert von dessem MenuID-Attribut auslesen, und eine
// Methode zur Verfügung stellen, mit der man die MenuID abfragen
// kann.
class XML {

var $xml_file;
    var $MenuID;

// Konstruktor der Klasse
    function XML($uri="") { $this->xml_file = $uri; }

// Diese Funktion gibt die Variable $MenuID zurück;
    function getMenuID()  { return $this->MenuID; }

function parseFile($uri) {
        if(!isset($this->xml_file)) { $this->xml_file = $uri; }

// xml_parse() will die XML Daten in einem String zu bekommen
        $fp = fopen($this->xml_file, "r");
        $string = fread($fp, 4096);
        fclose($fp);

// Erzeugt eine Instanz des XML-Parsers
        $parser = xml_parser_create();

// Das war der Clou an der Sache!! mit diesem Befehl kann man
        // den Parser an eine Klasse binden. Somit erkennt er die als
        // Methoden deklarierten Handler-Funktionen für Start-/End-
        // Tag als seine eigenen Handlerfunktionen. Ansonsten müsste
        // man die Handlerfunktionen *innerhalb* der Funktion
        // deklarieren, die das Parsing übernimmt!
        xml_set_object($parser,&$this);

// Parser soll Case-Folding "einschalten", also <Menu> nicht
        // intern in <MENU> umwandeln!
        xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);

// Hier werden die Methoden tag_open() und tag_close() als
        // interne Handler-Funktionen des XML-Parsers definiert (die
        // er dank xml_set_object() jetzt auch als solche erkennt *g*)
        xml_set_element_handler($parser,"tag_open","tag_close");

// Hier wird dann schlussendlich geparst
        if(!xml_parse($parser , $string , true)) {
                      die(sprintf( "XML error: %s at line %d\n\n",
                      xml_error_string(xml_get_error_code($parser)),
                      xml_get_current_line_number($parser)));
        }

// Und dahinter wieder brav aufgeräumt ;-)
        xml_parser_free($parser);
    }

// Wenn der Parser nun auf ein <Menu> trifft, dann soll er den
    // Wert seines MenuID-Attributes der Instanzvariable $MenuID
    // zuweisen!
    // Ein Wort noch zu den Interfaces: Der PHP-Wrapper für Expat
    // bietet als Übergabe-Paramter einen "Zeiger" auf den Parser
    // ($parser), den Namen des aktuellen Elements ($name) und die in
    // einem assotiativen Array gespeicherten Attribute mitsamt
    // Werten ($attrs). Mit denen kann dann in der Funktion
    // gearbeitet werden.
    function tag_open($parser , $name , $attrs) {
        if ($name == "Menu") {
           $this->MenuID = $attrs['MenuID'];
        }
    }

// Wenn der Parser auf ein </Menu> trifft, kann er sich ausruhen ;-)
    function tag_close($parser,$name) { }

}

Ausprobieren kann man das ganze mit:

$main = new XML("menu.xml");
      $main->parseFile();
      echo $main->getMenuID();

Ich übernehme keine Garantie dass der Code 1:1 übernömmen werden kann, da bei mir die klasse anders aussieht, und ich die hier 'ad hoc' (nach bestem Wissen und Gewissen) angepasst habe ;-)

Keine Ahnung warum das 4 Tage gedauert hat, bis ich das herausgefunden habe :-(

lg bernhard