Zunächst einmal muss ich zugeben, dass ich Computerstandards liebe. Wenn sich alle an die Standards der Branche halten würden, wäre das Internet ein besseres Medium. Der Einsatz standardisierter Datenaustauschformate ermöglicht offene und plattformunabhängige Rechenmodelle. Deshalb bin ich ein XML-Enthusiast.
Glücklicherweise unterstützt meine Lieblingsskriptsprache nicht nur XML, sondern unterstützt es zunehmend. Mit PHP kann ich XML-Dokumente schnell im Internet veröffentlichen, statistische Informationen über XML-Dokumente sammeln und XML-Dokumente in andere Formate konvertieren. Beispielsweise verwende ich häufig die XML-Verarbeitungsfunktionen von PHP, um Artikel und Bücher, die ich in XML schreibe, zu verwalten.
In diesem Artikel werde ich jede Verwendung des in PHP integrierten Expat-Parsers zur Verarbeitung von XML-Dokumenten diskutieren. Anhand von Beispielen werde ich die Verarbeitungsmethode von Expat demonstrieren. Gleichzeitig kann Ihnen das Beispiel zeigen, wie Sie:
Ihre eigene Verarbeitungsfunktion erstellen
Konvertieren von XML-Dokumenten in Ihre eigenen PHP-Datenstrukturen
Einführung
Der Parser von Expat XML, auch XML-Prozessor genannt, ermöglicht Programmen den Zugriff auf die Struktur und den Inhalt von XML-Dokumenten. Expat ist ein XML-Parser für die Skriptsprache PHP. Es wird auch in anderen Projekten wie Mozilla, Apache und Perl verwendet.
Was ist ein ereignisbasierter Parser?
Es gibt zwei grundlegende Arten von XML-Parsern:
Baumbasierte Parser: Konvertieren Sie XML-Dokumente in Baumstrukturen. Dieser Parsertyp analysiert den gesamten Artikel und stellt gleichzeitig eine API für den Zugriff auf jedes Element des resultierenden Baums bereit. Sein gemeinsamer Standard ist DOM (Document Object Model).
Ereignisbasierter Parser: Behandeln Sie XML-Dokumente als eine Reihe von Ereignissen. Wenn ein besonderes Ereignis auftritt, ruft der Parser die vom Entwickler bereitgestellte Funktion auf, um damit umzugehen.
Der ereignisbasierte Parser verfügt über eine datenorientierte Ansicht des XML-Dokuments, was bedeutet, dass er sich auf den Datenteil des XML-Dokuments und nicht auf seine Struktur konzentriert. Diese Parser verarbeiten das Dokument von Anfang bis Ende und melden Ereignisse wie Elementanfang, Elementende, Beginn von Feature-Daten usw. über Rückruffunktionen an die Anwendung. Das Folgende ist ein Beispiel-XML-Dokument für „Hello-World“:
<greeting>
Hallo Welt
</greeting>
Der ereignisbasierte Parser meldet drei Ereignisse:
Startelement: Begrüßung
Der Anfang des CDATA-Elements, der Wert ist: Hello World
Endelement: Begrüßung
Im Gegensatz zu baumbasierten Parsern erzeugen ereignisbasierte Parser keine Struktur, die das Dokument beschreibt. In CDATA-Elementen können Sie mit dem ereignisbasierten Parser die Begrüßungsinformationen des übergeordneten Elements nicht abrufen.
Es bietet jedoch einen Zugriff auf niedrigerer Ebene, was eine bessere Nutzung der Ressourcen und einen schnelleren Zugriff ermöglicht. Auf diese Weise muss nicht das gesamte Dokument im Speicher untergebracht werden; das gesamte Dokument kann sogar größer sein als der tatsächliche Speicherwert.
Expat ist ein solcher ereignisbasierter Parser. Wenn Sie Expat verwenden, kann es bei Bedarf natürlich auch eine vollständige native Baumstruktur in PHP generieren.
Das obige Hello-World-Beispiel enthält das vollständige XML-Format. Es ist jedoch ungültig, da ihm weder eine DTD (Document Type Definition) noch eine eingebettete DTD zugeordnet ist.
Für Expat macht das keinen Unterschied: Expat ist ein Parser, der die Gültigkeit nicht prüft und daher alle mit dem Dokument verknüpften DTDs ignoriert. Es ist jedoch zu beachten, dass das Dokument noch vollständig formatiert sein muss, da sonst Expat (wie andere XML-kompatible Parser) mit einer Fehlermeldung abbricht.
Als Parser, der keine Gültigkeitsprüfung durchführt, ist Exapt aufgrund seiner Geschwindigkeit und seines geringen Gewichts gut für Internetanwendungen geeignet.
Kompilieren von Expat
Expat kann in die PHP3.0.6-Version (oder höher) kompiliert werden. Ab Apache 1.3.9 ist Expat als Teil von Apache enthalten. Auf Unix-Systemen können Sie es in PHP kompilieren, indem Sie PHP mit der Option -with-xml konfigurieren.
Wenn Sie PHP als Apache-Modul kompilieren, wird Expat standardmäßig als Teil von Apache eingebunden. Unter Windows müssen Sie die XML-Dynamic-Link-Bibliothek laden.
XML-Beispiele: XMLstats
Eine Möglichkeit, die Funktionen von Expat kennenzulernen, sind Beispiele. Das Beispiel, das wir besprechen werden, ist die Verwendung von Expat zum Sammeln von Statistiken zu XML-Dokumenten.
Für jedes Element im Dokument werden folgende Informationen ausgegeben:
die Häufigkeit, mit der das Element im Dokument verwendet wird
Die Menge der Zeichendaten in diesem Element
das übergeordnete Element des Elements
untergeordnete Elemente des Elements
Hinweis: Zur Demonstration verwenden wir PHP, um eine Struktur zum Speichern des übergeordneten Elements und des untergeordneten Elements des Elements zu generieren.
zum Generieren der XML-Parser-Instanz
vorbereitete
Funktion ist xml_parser_create().Diese Instanz wird für alle zukünftigen Funktionen verwendet. Diese Idee ist dem Verbindungs-Tag der MySQL-Funktion in PHP sehr ähnlich. Vor dem Parsen des Dokuments müssen Sie bei ereignisbasierten Parsern normalerweise eine Rückruffunktion registrieren, die aufgerufen wird, wenn ein bestimmtes Ereignis auftritt. Expat hat keine Ausnahmeereignisse. Es definiert die folgenden sieben möglichen Ereignisse:
Objekt XML-Parsing-Funktionsbeschreibung
Element xml_set_element_handler() Start- und
Endzeichendaten des Elements xml_set_character_data_handler() Beginn der Zeichendaten
externe Entität xml_set_external_entity_ref_handler() externe Entität Ungeparste
externe Entität xml_set_unparsed_entity_decl_handler (
) Das Auftreten einerVerarbeitungsanweisung
für eine nicht aufgelöste externe Entität
xml_set_processing_instruction_handler() Das Auftreteneiner Notationsdeklaration einer Verarbeitungsanweisung xml_set_notation_decl_handler() Das Auftreten einer Standardnotationsdeklaration xml_set_default_handler
() Andere Ereignisse ohne angegebene Handlerfunktion
Alle Rückruffunktionen müssen eine Instanz von verwenden Parser als erster Parameter (es gibt zusätzlich weitere Parameter).
Für das Beispielskript am Ende dieses Artikels. Zu beachten ist, dass sowohl Elementverarbeitungsfunktionen als auch Zeichendatenverarbeitungsfunktionen verwendet werden. Die Callback-Handler-Funktion des Elements wird über xml_set_element_handler() registriert.
Diese Funktion benötigt drei Parameter:
eine Instanz des Parsers
Der Name der Rückruffunktion, die das Startelement verarbeitet
Der Name der Rückruffunktion, die das schließende Element verarbeitet
Die Rückruffunktion muss vorhanden sein, wenn das Parsen des XML-Dokuments beginnt. Sie müssen im Einklang mit den im PHP-Handbuch beschriebenen Prototypen definiert werden.
Beispielsweise übergibt Expat drei Argumente an die Handlerfunktion für das Startelement. Im Skriptbeispiel ist es wie folgt definiert:
function start_element($parser, $name, $attrs)
Der erste Parameter ist die Parser-ID, der zweite Parameter ist der Name des Startelements und der dritte Parameter enthält alle Attribute und Werte des Elementarrays.
Sobald Sie mit dem Parsen des XML-Dokuments beginnen, ruft Expat Ihre start_element()-Funktion auf und übergibt die Parameter, wann immer es auf das Startelement trifft.
Die Fallfaltungsoption von XML
verwendet die Funktion xml_parser_set_option(), um die Fallfaltungsoption zu deaktivieren. Diese Option ist standardmäßig aktiviert und bewirkt, dass an Handlerfunktionen übergebene Elementnamen automatisch in Großbuchstaben umgewandelt werden. Bei XML wird jedoch die Groß-/Kleinschreibung beachtet (daher ist die Groß-/Kleinschreibung für statistische XML-Dokumente sehr wichtig). Für unser Beispiel muss die Option zum Falten des Koffers deaktiviert sein.
Das Dokument analysieren
Nachdem alle Vorbereitungen abgeschlossen sind, kann das Skript nun endlich das XML-Dokument analysieren:
Xml_parse_from_file(), eine benutzerdefinierte Funktion, öffnet die im Parameter angegebene Datei und analysiert sie in einer Größe von 4 KB
xml_parse() gibt wie xml_parse_from_file() false zurück, wenn ein Fehler auftritt, das heißt, wenn das XML-Dokument nicht vollständig formatiert ist.
Sie können die Funktion xml_get_error_code() verwenden, um den numerischen Code des letzten Fehlers abzurufen. Übergeben Sie diesen numerischen Code an die Funktion xml_error_string(), um die Fehlertextinformationen abzurufen.
Gibt die aktuelle Zeilennummer von XML aus, was das Debuggen erleichtert.
Während des Parsing-Vorgangs wird die Callback-Funktion aufgerufen.
Beschreiben der Dokumentstruktur
Beim Parsen eines Dokuments muss sich Expat mit der Frage befassen: Wie kann eine grundlegende Beschreibung der Dokumentstruktur beibehalten werden?
Wie bereits erwähnt, erzeugt der ereignisbasierte Parser selbst keine Strukturinformationen.
Die Tag-Struktur ist jedoch ein wichtiges Merkmal von XML. Beispielsweise bedeutet die Elementfolge <book><title> etwas anderes als <figure><title>. Allerdings wird Ihnen jeder Autor sagen, dass Buchtitel und Bildtitel nichts miteinander zu tun haben, obwohl sie beide den Begriff „Titel“ verwenden. Um XML effizient mit einem ereignisbasierten Parser zu verarbeiten, müssen Sie daher Ihre eigenen Stapel oder Listen verwenden, um Strukturinformationen über das Dokument zu verwalten.
Um die Dokumentstruktur widerzuspiegeln, muss das Skript mindestens das übergeordnete Element des aktuellen Elements kennen. Dies ist mit der API von Exapt nicht möglich. Sie meldet nur Ereignisse des aktuellen Elements ohne Kontextinformationen. Daher müssen Sie Ihre eigene Stapelstruktur erstellen.
Das Skriptbeispiel verwendet eine FILO-Stapelstruktur (First-In-Last-Out). Über ein Array speichert der Stapel alle Startelemente. Für die Funktion „Elementverarbeitung starten“ wird das aktuelle Element durch die Funktion array_push() an die Spitze des Stapels verschoben. Dementsprechend entfernt die Endelementverarbeitungsfunktion das oberste Element durch array_pop().
Für die Sequenz <book><title></title></book> wird der Stack wie folgt gefüllt:
Startelement book: Weisen Sie „book“ dem ersten Element des Stacks ($stack[0]) zu.
Titel des Startelements: Weisen Sie „title“ der Oberseite des Stapels zu ($stack[1]).
Endelementtitel: Entfernen Sie das oberste Element vom Stapel ($stack[1]).
Endelementtitel: Entfernen Sie das oberste Element vom Stapel ($stack[0]).
PHP3.0 implementiert das Beispiel, indem es die Verschachtelung von Elementen manuell über eine $ Depth-Variable steuert. Dadurch wirkt das Skript komplexer. PHP4.0 verwendet die Funktionen array_pop() und array_push(), um das Skript prägnanter aussehen zu lassen.
Daten sammeln
Um Informationen zu jedem Element zu sammeln, muss sich das Skript die Ereignisse für jedes Element merken. Speichern Sie alle verschiedenen Elemente im Dokument, indem Sie eine globale Array-Variable $elements verwenden. Die Elemente des Arrays sind Instanzen der Elementklasse und verfügen über 4 Eigenschaften (Variablen der Klasse)
$count – die Häufigkeit, mit der das Element im Dokument gefunden wurde
$chars – Anzahl der Bytes von Zeichenereignissen im Element
$parents – übergeordnetes Element
$childs – untergeordnete Elemente
Wie Sie sehen, ist das Speichern von Klasseninstanzen in einem Array ein Kinderspiel.
Hinweis: Eine Funktion von PHP besteht darin, dass Sie die gesamte Klassenstruktur durch eine while(list() = every())-Schleife durchlaufen können, genau wie Sie das gesamte entsprechende Array durchlaufen. Alle Klassenvariablen (und Methodennamen bei Verwendung von PHP3.0) werden als Strings ausgegeben.
Wenn ein Element gefunden wird, müssen wir den entsprechenden Zähler erhöhen, um zu verfolgen, wie oft es im Dokument vorkommt. Das count-Element im entsprechenden $elements-Element wird ebenfalls um eins erhöht.
Wir müssen dem übergeordneten Element auch mitteilen, dass das aktuelle Element sein untergeordnetes Element ist. Daher wird der Name des aktuellen Elements dem Element im $childs-Array des übergeordneten Elements hinzugefügt. Schließlich sollte sich das aktuelle Element merken, wer sein übergeordnetes Element ist. Daher wird das übergeordnete Element dem Element im $parents-Array des aktuellen Elements hinzugefügt.
Statistiken anzeigen
Der verbleibende Code durchläuft das Array $elements und seine Unterarrays, um seine Statistiken anzuzeigen. Dies ist die einfachste verschachtelte Schleife. Obwohl sie die richtigen Ergebnisse ausgibt, ist der Code weder prägnant noch verfügt er über besondere Fähigkeiten. Es handelt sich lediglich um eine Schleife, die Sie jeden Tag verwenden können, um Ihre Arbeit abzuschließen.
Die Skriptbeispiele sind so konzipiert, dass sie über den CGI-Ansatz von PHP über die Befehlszeile aufgerufen werden können. Daher ist das Ausgabeformat der statistischen Ergebnisse das Textformat. Wenn Sie das Skript im Internet verwenden möchten, müssen Sie die Ausgabefunktion ändern, um ein HTML-Format zu generieren.
Zusammenfassung
Exapt ist ein XML-Parser für PHP. Als ereignisbasierter Parser erstellt er keine strukturelle Beschreibung des Dokuments. Durch die Bereitstellung eines Zugriffs auf niedriger Ebene wird jedoch eine bessere Nutzung der Ressourcen und ein schnellerer Zugriff ermöglicht.
Als Parser, der keine Gültigkeitsprüfung durchführt, ignoriert Expat an XML-Dokumente angehängte DTDs, stoppt jedoch mit einer Fehlermeldung, wenn das Dokument nicht wohlgeformt ist.
Stellen Sie Event-Handler zur Verarbeitung von Dokumenten bereit
Erstellen Sie Ihre eigenen Ereignisstrukturen wie Stapel und Bäume, um die Vorteile des XML-Markups für strukturierte Informationen zu nutzen.
Täglich erscheinen neue XML-Programme und die XML-Unterstützung von PHP wird ständig verbessert (z. B. wurde die Unterstützung für den DOM-basierten XML-Parser LibXML hinzugefügt).
Mit PHP und Expat können Sie sich auf die kommenden Standards vorbereiten, die gültig, offen und plattformunabhängig sind.
Beispiel
<?
/***************************************************** ***** ******************************
* Name: XML-Parsing-Beispiel: XML-Dokumentinformationsstatistik
* beschreiben
* In diesem Beispiel wird der Expat-Parser von PHP verwendet, um XML-Dokumentinformationen zu sammeln und zu zählen (z. B. die Anzahl der Vorkommen jedes Elements, der übergeordneten Elemente und der untergeordneten Elemente).
* XML-Datei als Parameter./xmlstats_PHP4.php3 test.xml
* $Requires: Expat-Anforderungen: Expat PHP4.0 ist im CGI-Modus kompiliert
************************************************** * ***************************/
// Der erste Parameter ist die XML-Datei
$file = $argv[1];
// Initialisierung von Variablen
$elements = $stack = array();
$total_elements = $total_chars = 0;
//Grundlegende Elementklasse
Klassenelement
{
var $count = 0;
var $chars = 0;
var $parents = array();
var $childs = array();
}
// Funktion zum Parsen von XML-Dateien
Funktion xml_parse_from_file($parser, $file)
{
if(!file_exists($file))
{
die("Datei „$file" kann nicht gefunden werden.“);
}
if(!($fp = @fopen($file, "r")))
{
die("Datei „$file" kann nicht geöffnet werden.“);
}
while($data = fread($fp, 4096))
{
if(!xml_parse($parser, $data, feof($fp)))
{
return(false);
}
}
fclose($fp);
return(true);
}
// Ergebnisfunktion ausgeben (Kastenform)
Funktion print_box($title, $value)
{
printf("n+%'-60s+n", "");
printf("|%20s", "$title:");
printf("%14s", $value);
printf("%26s|n", "");
printf("+%'-60s+n", "");
}
// Ergebnisfunktion ausgeben (Zeilenform)
Funktion print_line($title, $value)
{
printf("%20s", "$title:");
printf("%15sn", $value);
}
// Sortierfunktion
Funktion my_sort($a, $b)
{
return(is_object($a) && is_object($b) ? $b->count - $a->count: 0);
}
Funktion start_element($parser, $name, $attrs)
{
global $elements, $stack;
// Befindet sich das Element bereits im globalen $elements-Array?
if(!isset($elements[$name]))
{
// Nein – eine Klasseninstanz eines Elements hinzufügen
$element = neues Element;
$elements[$name] = $element;
}
// Erhöhe den Zähler dieses Elements um eins
$elements[$name]->count++;
// Gibt es ein übergeordnetes Element?
if(isset($stack[count($stack)-1]))
{
// Ja – Weisen Sie das übergeordnete Element $last_element zu
$last_element = $stack[count($stack)-1];
// Wenn das übergeordnete Elementarray des aktuellen Elements leer ist, initialisieren Sie es auf 0
if(!isset($elements[$name]->parents[$last_element]))
{
$elements[$name]->parents[$last_element] = 0;
}
// Erhöhe den übergeordneten Elementzähler dieses Elements um eins
$elements[$name]->parents[$last_element]++;
// Wenn das untergeordnete Elementarray des übergeordneten Elements des aktuellen Elements leer ist, wird es auf 0 initialisiert
if(!isset($elements[$last_element]-> Kinder[$ Name]))
{
$elements[$last_element]->childs[$name] = 0;
}
// Eins zum untergeordneten Elementzähler des übergeordneten Elements des Elements hinzufügen.
$elements[$last_element]->childs[$name]++;
}
//Das aktuelle Element zum Stapel hinzufügen
array_push($stack, $name);
}
Funktion stop_element($parser, $name)
{
global $stack;
// Das oberste Element vom Stapel entfernen
array_pop($stack);
}
Funktion char_data($parser, $data)
{
global $elements, $stack, $ Depth;
// Erhöhen Sie die Anzahl der Zeichen des aktuellen Elements
$elements[$stack][count($stack)-1]]->chars += strlen(trim($data));
}
// Parser-Instanz generieren
$parser = xml_parser_create();
// Verarbeitungsfunktion festlegen
xml_set_element_handler($parser, "start_element", "stop_element");
xml_set_character_data_handler($parser, "char_data");
xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
// Datei analysieren
$ret = xml_parse_from_file($parser, $file);
if(!$ret)
{
die(sprintf("XML-Fehler: %s in Zeile %d",
xml_error_string(xml_get_error_code($parser)),
xml_get_current_line_number($parser)));
}
// Den Parser freigeben
xml_parser_free($parser);
// Das Hilfselement freigeben
unset($elements["current_element"]);
unset($elements["last_element"]);
// Nach der Anzahl der Elemente sortieren
uasort($elements, "my_sort");
// Schleife durch $elements, um Elementinformationen zu sammeln
while(list($name, $element) = every($elements))
{
print_box("Elementname", $name);
print_line("Elementanzahl", $element->count);
print_line("Character count", $element->chars);
printf("n%20sn", "* Parent elements");
// Schleife durch das übergeordnete Element und Ausgabe des Ergebnisses
while(list($key, $value) = every($element->parents))
{
print_line($key, $value);
}
if(count($element->parents) == 0)
{
printf("%35sn", "[Stammelement]");
}
// Durchlaufe das untergeordnete Element dieses Elements und gebe das Ergebnis aus
printf("n%20sn", "* Untergeordnete Elemente");
while(list($key, $value) = every($element->childs))
{
print_line($key, $value);
}
if(count($element->childs) == 0)
{
printf("%35sn", "[keine Kinder]");
}
$total_elements += $element->count;
$total_chars += $element->chars;
}
// Endergebnis
print_box("Gesamtzahl der Elemente", $total_elements);
print_box("Gesamtanzahl Zeichen", $total_chars);
?>