PHP/SAX

Aus Mikiwiki
< PHP
Version vom 26. Februar 2010, 22:19 Uhr von Michi (Diskussion | Beiträge)
(Unterschied) ← Nächstältere Version | Aktuelle Version (Unterschied) | Nächstjüngere Version → (Unterschied)
Zur Navigation springen Zur Suche springen

Funktionsübersicht

Die folgenden Funktionen steuern den Parser.

Funktion Beschreibung
xml_parser_create Erzeugt eine neue Instanz des XML-Parsers und gibt ein Handle zurück, das von anderen Funktionen benötigt wird.
xml_parse Startet das Parsen eines Dokuments.
xml_get_error_code Zeigt Fehlernummern an.
xml_error_string Zeigt Fehlermeldungen an.
xml_get_current_line_number Gibt die aktuell vom Parser untersuchte Zeile des XML-Dokuments aus.
xml_get_current_column_number Gibt die aktuell vom Parser untersuchte Spalte des XML-Dokuments aus.
xml_get_current_byte_index Gibt den aktuellen Byte-Index des XML-Dokuments aus.
xml_parser_free Gibt einen Parser wieder frei.
xml_parser_set_option Setzt verschiedene Optionen.
xml_parser_get_option Ermittelt die aktuellen Werte der Optionen.
utf8_decode Wandelt eine UTF-8-Zeichenkette nach ISO-8859-1 um.
utf8_encode Wandelt eine ISO-8859-1-Zeichenkette nach UTF-8 um.

Mit den folgenden Funktionen können eigene Routinen definiert werden, um bei der Verarbeitung von XML-Dokumenten auf bestimmte Daten zu reagieren. Dabei wird eine weitere - benutzerdefinierte - Rückruffunktion namentlich angegeben. Diese ist letztlich für die Verarbeitung der Ereignisse zuständig, di beim Abarbeiten des Dokuemnts auftreten.

Funktion Beschreibung
xml_set_selement_handler Die hier definierte Funktion wird ausgeführt, wenn der Parser ein XML-Element erreicht oder eines verlässt. Start- und End-Tags sowie der Inhalt dazwischen werden getrennt behandelt.
xml_set_character_data_handler Diese Funktion behandelt alle Zeichen, die nicht als Tag gelten. Das betrifft auch "weisse Leerzeichen".
xml_set_processing_instruction_handler Hiermit werden Prozessanweisungen, die wie "<?get ... ?>" aussehen, behandelt.
xml_set_default_handler Die hiermit definierte Funktion wird gestartet, wenn sich keine andere Definition für das Ereignis zuständig fühlt.
xml_set_unparsed_entity_decl_handler Diese Funktion behandelt nicht zu parsende Einheiten (NDATA).
xml_set_notation_decl_handler Eine Notations-Deklaration wird hiermit behandelt.
xml_set_external_entity_ref_handler Diese Funktion ist zuständig, wenn eine externe Referenz gefunden wird.

Eigenschaften der Funktionen

Umwandlung der Schreibweise

Die als "case folding" bezeichnete Massnahme bezieht sich auf die Anforderung der Ereignisbehandlungsroutinen, die zu untersuchenden Tags in Grossbuchstaben zu erhalten. Entsprechend werden alle XML-Elemente in Grossbuchstaben gewandelt, bevor sie gesendet werden. Der Vorgang kann allerdings mit den beiden Funktionen "xml_parser_get_option" und "xml_parser_set_option" kontrolliert werden, falls ein anderes Verhalten erwünscht ist.

Fehlercodes

Die von der Funktion "xml_parse" ausgegebenen Fehlercodes liegen als Konstante vor.

Fehlercode Beschreibung
XML_ERROR_NONE
XML_ERROR_NO_MEMORY
XML_ERROR_SYNTAX
XML_ERROR_NO_ELEMENTS
XML_ERROR_INVALID_TOKEN
XML_ERROR_UNCLOSED_TOKEN
XML_ERROR_PARTIAL_CHAR
XML_ERROR_TAG_MISMATCH
XML_ERROR_DUPLICATE_ATTRIBUTE
XML_ERROR_JUNK_AFTER_DOC_ELEMENT
XML_ERROR_PARAM_ENTITY_REF
XML_ERROR_UNDEFINED_ENTITY
XML_ERROR_RECURSIVE_ENTITY_REF
XML_ERROR_ASYNC_ENTITY
XML_ERROR_BAD_CHAR_REF
XML_ERROR_BINARY_ENTITY_REF
XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF
XML_ERROR_MISPLACED_XML_PI
XML_ERROR_UNKNOWN_ENCODING
XML_ERROR_INCORRECT_ENCODING
XML_ERROR_UNCLOSED_CDATA_SECTION
XML_ERROR_EXTERNAL_ENTITY_HANDLING

Zeichenkodierung

PHP kodiert Zeichen intern imm im Unicode-Format UTF-8 (8 bis 21 Bit in bis zu 4 Byte). Die Kodierung des Quelltexts muss vor dem Parsen festgelegt werden. Die alternativen zeichensätze US-ASCII und ISO-8859-1 sind 8-Bit-Zeichensätze.

Eine weitere Einstellung ist nach dem Parsen der Tags möglich. Diese als Zielkodierung bezeichnete Wahl des Zeichensatzes erfolgt, wenn die untersuchten Elemente an die Ereignisbehandlungsroutinen geleitet werden. Zeichen, die nicht abgebildet werden können, werden durch ein Fragezeichen ersetzt.

SAX-Anwendungen in der Praxis

Die folgenden Beispiele zeigen, wie XML-Parser selbst geschrieben werden und wie damit Zugriff auf XML-Daten möglich ist. Als Beispieldatei wird dazu die folgende Datei "file.xml" verwendet.

<?xml version="1.0" ?>
<!DOCTYPE FirstXML [
  <!ELEMENT AUSGABE (ANZEIGE)>
  <!ELEMENT ANZEIGE (#PCDATA)>
  ]>
<AUSGABE>
  <ANZEIGE>Jetzt wird's spannend!</ANZEIGE>
</AUSGABE>

Das folgende Beispiel zeigt einen einfachen Parser, der nur Tags verarbeitet. Attribute und Daten innerhalb des Containers werden nicht beachtet.

<?php
## Die zur Anzeige gebrachte XML-Datei.
$file  = "data/file.xml";
$depth = 0;

## Die eigentliche Funktionalität liegt in den folgenden beiden
## Ereignisbehandlungsroutinen. Jedes öffnende Element wird eingerückt und
## entsprechend beim schliessenden Element wieder ausgerückt. Die Anzahl
## der Einrückungen wird in der globalen Variable "$depth" gespeichert.

function startElement($parser, $name, $attrs) 
{
  global $depth;
  ## Wird ein öffnendes Element gefunden, so wird eine Anzahl Leerzeichen
  ## ausgegeben.
  echo str_repeat("&nbsp;", $depth * 4);
  echo "&lt;$name&gt;<br />\n";
  #3 Danach wird für die nächste Stufe die Tiefe erhöht.
  $depth++;
}

function endElement($parser, $name) 
{
  global $depth;
  ## Zurücknehmen der Einrückung
  $depth--;
  echo str_repeat("&nbsp;", $depth * 4);
  echo "&lt;/$name&gt;<br />\n";
}

## Erzeugen eines neuen Parsers, die Referenz "$xml_parser" wird in allen
## anderen Funktionen zum Zugriff genutzt.
$xml_parser = xml_parser_create();
## Zuweisung der Ereignisbehandlungsroutinen. Im einfachsten Fall werdenm 
## Start- und End-Tags behandelt. Als Parameter werden die selbst-
## definierten Funktionsnamen angegeben.
xml_set_element_handler($xml_parser, "startElement", "endElement");
if (!($fp = fopen($file, "r"))) 
{
  die("Kann XML-Datei nicht öffnen");
}
## Einlesen der XML-Datei.
while ($data = fread($fp, 4096)) 
{
  ## Zeilenweise Interpretation. Die Ausgabe an den Webbrowser erfolgt
  ## automatisch.
  if (!xml_parse($xml_parser, $data, feof($fp))) 
  {
    die(sprintf("XML-Fehler: %s aus Zeile %d",
    xml_error_string(xml_get_error_code($xml_parser)),
    xml_get_current_line_number($xml_parser)));
  }
}
## Zerstörung des Parsers, um die belegten Ressourcen freizugeben.
xml_parser_free($xml_parser);
?>
<p>So zeigt der Webbrowser diese XML-Datei direkt an: 
<a href="data/file.xml">file.xml</a></p>

<AUSGABE>
    <ANZEIGE>
    </ANZEIGE>
</AUSGABE>

So zeigt der Webbrowser diese XML-Datei direkt an: file.xml

Externe Entitäten

Im Gegensatz zum vorigen Beispiel wird im folgenden der Code konkret untersucht und durch farbige Hervorhebung die Reaktion des Parsers kenntlich gemacht. Die im Beispiel verwendete Datei "data.xml" hat folgenden Inhalt.

<?xml version='1.0' standalone="yes"?>
<!DOCTYPE chapter [
  <!-- Aufforderung zum Lesen eines weiteren Dokuments -->
  <!ENTITY systemEntity SYSTEM "data/external.xml">
  <!ENTITY title "Das ist der Titel">  
]>
<chapter>
  <TITLE>&title;</TITLE>
  <para>
    <informaltable>
      <tgroup cols="3">
        <tbody>
          <row>
            <entry>a1</entry>
            <entry morerows="1">b1</entry>
            <entry>c1</entry>
          </row>
          <row>
            <entry>a2</entry>
            <entry>c2</entry>
          </row>
          <row>
            <entry>a3</entry>
            <entry>b3</entry>
            <entry>c3</entry>
          </row>
        </tbody>
      </tgroup>
    </informaltable>
  </para>
  &systemEntity;
  <sect1 id="about">
    <title>Über diesen Text</title>
    <para>
      <!-- Dies ist ein Kommentar -->
      <?php echo "Hallo! Dies ist PHP Version " . phpversion(); ?>
    </para>
  </sect1>
</chapter>

Die in der vorigen Datei "data.xml" genannte Datei "external.xml" hat folgenden Inhalt.

<?xml version="1.0" ?>
<foo>
  <element attrib="value"/>
  <?php 
  $check = phpversion();
  print "Hier wurde PHP-Code ausgeführt. PHP-Version: $check"; 
  ?>
</foo>

Mit Hilfe des folgenden Skripts kann praktisch gezielt auf alle Elemente der XML-Datei zugegriffen werden. Allerdings wird immer - von oben nach unten - die gesamte Datei verarbeitet. Eine Analyse von Teilen bzw. die gezielte Extraktion von Daten ist nicht ohne weiteres möglich. In der Praxis können derartige Methoden verwendet werden, um fremde Daten in ein eigenes Format oder in eine relationale Datenbank zu überführen. Für die Navigation innerhalb von XML wurde XPath entwickelt, das den Einsatz anderer Module voraussetzt.

Bei der Darstellung im Webbrowser ist hier allerdings etwas nicht sauber...

<?php
## Die zur Anzeige gebrachte XML-Datei.
$file = "data/data.xml";

## Über das ganze Dokument mitgeführte Einrückung. Damit wird die Struktur
## des Dokuments bei der Anzeige besser lesbar.
function indent($addsub = 0) 
{
  static $depth;
  if ($addsub < 0) 
  {
    $indent  = str_repeat("&nbsp;", $depth * 4);
    $depth  += $addsub;
    return $indent;
  }
  else
  {
    $depth  += $addsub;
    $indent  = str_repeat("&nbsp;", $depth * 4);
    return $indent;
  }
}

function startElement($parser, $name, $attr) 
{
  echo indent(1);
  print "&lt;<span class=tag>$name</span>";
  if (is_array($attr))
  {
    foreach ($attr as $k => $v) 
    {
      echo " <span class=attr>$k</span>=";
      echo "'<span class=parm>$v</span>'";
    }
  }
  print "&gt;<br />\n";
}

function endElement($parser, $name) 
{
  echo indent(-1);
  print "&lt;/<span class=tag>$name</span>&gt;<br />\n";
}

## Allle Zeichen, die nicht vom Parser behandelt werden, werden fett
## ausgegeben.
function characterData($parser, $data)
{
  $data = chop($data);
  $indent = indent();
  print strlen($data) > 0 ?  "$indent<b>$data</b><br />\n" : NULL;
}

## Prozessbehanldung
function PIHandler($parser, $target, $data) 
{
  ## Überprüfung, ob PHP-Code vorhanden ist
  switch (strtolower($target)) 
  {
    case "php":
      ## Falls es zulässiger PHP-Code ist, erfolgt die Abarbeitung
      ## mit der Funktion "eval".
      eval($data);
      break;
    default:
      ## Ansonsten wird der PHP-Code ienfach ausgegeben, ohne abgearbeitet
      ## zu werden.
      echo htmlspecialchars($data);
  }
}

function defaultHandler($parser, $data) 
{
  $data = chop($data);
  echo indent();
  if (preg_match("|^&.*;$|U", $data)) 
  {
    echo "<u>" . htmlspecialchars($data) . "</u><br />\n";
  }
  elseif (strlen($data) > 0)
  {
    echo htmlspecialchars($data);
    echo "<br />\n";
  }
}

## Die externen Referenzen werden getrennt von der Basisdatei behandelt.
function externalEntityRefHandler($parser, $openEntityNames, $base, $systemId, $publicId) 
{
  if ($systemId) 
  {
    ## Start eines neuen Parsers
    if (!list($parser, $fp) = new_xml_parser($systemId)) 
    {
      printf("Kann Entity %s in Zeile %s nicht öffnen<br />\n", $openEntityNames, $systemId);
      return false;
    }
    while ($data = fread($fp, 4096)) 
    {
      if (!xml_parse($parser, $data, feof($fp))) 
      {
        printf("XML-Fehler: %s in Zeile %d während der Verarbeitung von %s<br />\n",
               xml_error_string(xml_get_error_code($parser)),
               xml_get_current_line_number($parser), 
               $openEntityNames);
        xml_parser_free($parser);
        return false;
      }
    }
    xml_parser_free($parser);
    return true;
  }
  return false;
}

function new_xml_parser($file) 
{
  $xml_parser = xml_parser_create("ISO-8859-1");
  ## Definition der Behandlungsroutinen.
  xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, 0);
  xml_set_element_handler($xml_parser, "startElement", "endElement");
  xml_set_character_data_handler($xml_parser, "characterData");
  xml_set_processing_instruction_handler($xml_parser, "PIHandler");
  xml_set_default_handler($xml_parser, "defaultHandler");
  xml_set_external_entity_ref_handler($xml_parser, "externalEntityRefHandler");
   
  if (!($fp = @fopen($file, "r"))) 
  {
    return false;
  }
  return array($xml_parser, $fp);
}

if (!(list($xml_parser, $fp) = new_xml_parser($file))) 
{
  die("Kann XML-Datei nicht öffnen");
}

print "<code>";
while ($data = fread($fp, 4096)) 
{
  if (!xml_parse($xml_parser, $data, feof($fp))) 
  {
    die(sprintf("XML-Fehler: %s in Zeile %d<br />\n",
    xml_error_string(xml_get_error_code($xml_parser)),
    xml_get_current_line_number($xml_parser)));
  }
}
print "</code>";
print "<hr>\nParser beendet\n";
xml_parser_free($xml_parser);
?>

<p>Schauen Sie sich hier an, wie der Browser die XML-Dateien selbst darstellt:</p>
<ul>
  <li><a href="data/data.xml">data.xml</a></li>
  <li><a href="data/external.xml">external.xml</a></li>
</ul>

    <chapter>
        <TITLE>
        &title;
        </TITLE>
        <para>
            <informaltable>
                <tgroup cols='3'>

                    <tbody>
                        <row>
                            <entry>
                            a1
                            </entry>
                            <entry morerows='1'>

                            b1
                            </entry>
                            <entry>
                            c1
                            </entry>
                        </row>
                        <row>

                            <entry>
                            a2
                            </entry>
                            <entry>
                            c2
                            </entry>
                        </row>

                        <row>
                            <entry>
                            a3
                            </entry>
                            <entry>
                            b3
                            </entry>
                            <entry>

                            c3
                            </entry>
                        </row>
                    </tbody>
                </tgroup>
            </informaltable>

        </para>
        <foo>
            <element attrib='value'>
            </element>
Hier wurde PHP-Code ausgeführt. PHP-Version: 5.2.4-2ubuntu5.3        </foo>

        <sect1 id='about'>
            <title>
            Über diesen Text
            </title>
            <para>
            <!-- Dies ist ein Kommentar -->

Hallo! Dies ist PHP Version 5.2.4-2ubuntu5.3            </para>
        </sect1>
    </chapter>


Parser beendet

Schauen Sie sich hier an, wie der Browser die XML-Dateien selbst darstellt:

  • data.xml
  • external.xml