XML-интерпретация
Комплексная обработка данных
🕛 01.11.2006, 15:34
XML (extensible Markup Language) - формат обмена структурированными данными в пространстве web; стандарт, определенный Консорциумом World Wide Web (W3C). Информацию о XML и связанных технологиях можно получить по адресу http://www.w3.org/XML/. Для понимания материала главы необходимо разбираться в синтаксисе XML Для работы с документами XML используются два способа.
Использование объектной модели документа (DOM), которая удобна для работы с документом в целом, но расходует больше ресурсов.
Интерпретация документа на основе модели событий, удобная при однократном просмотре документа и менее требовательная к ресурсам (что важно при работе с объемными документами).
Первый способ рассмотрен в предыдущей главе. Функции, описанные в данной главе, реализуют второй способ обработки. Событийная модель обработки (интерпретации) подразумевает следующую схему функционирования программы.
Устанавливаются функции обработки элементов документа различного типа (например, маркеров, данных,-инструкций и т. д.).
Инициализируется и запускается интерпретатор, который последовательно просматривает документ и запускает соответствующие обработчики данных.
Функции-обработчики определенным образом обрабатывают передаваемые им фрагменты документа.
Заметьте, что интерпретатор предназначен для обработки документа, но не для его проверки; документ должен соответствовать грамматике XML (и правилам DTD).
В РНР интерпретатор XML использует библиотеку expat (ее также использует Apache-1.3.9 и последующие версии), см.: http://www. jclark.com/xml/. Скомпилировать РНР с поддержкой expat можно, используя параметр -with-xml.
Для документов поддерживаются кодировки US-ASCII, ISO-8859-1 (по умолчанию), UTF-8 (UTF-16 не поддерживается). Разделяют исходную и целевую кодировки. Исходная кодировка - это набор символов документа (ее можно изменять в процессе интерпретации). (Внутри РНР символы всегда хранятся в кодировке UTF-8, позволяется использовать символы размером до 21 бита.) В функции-обработчики данные передаются в целевой кодировке (для всех типов данных). При нахождении в документе-символа, не соответствующего исходной кодировке, выдается ошибка; а если символ не может быть представлен в целевой кодировке, то он заменяется на знак вопроса.
Интерпретация (по умолчанию) не учитывает регистр имен тегов, то есть в функции обработки символы передаются преобразованными к верхнему регистру. Для отключения этого используйте:
xml_parser_set_option($xml_parser. XML_OPTION_CASE_FOLDING. 0);
Для интерпретатора можно определить следующие виды функций-обработчиков:
xml_set_element_handler() - обработчики начальных и конечных тегов;
xml_set_character_data_handler() - текст между тегами (с учетом пробелов);
xml set_processing_instruction_handler() - инструкции обработки (например, <?php ?> и подобные им; инструкция <?xml ?> зарезервирована);
xml_set_default_handler() - обработчик по умолчанию, используемый при невозможности использования иного обработчика;
xm"l_set_unparsed_entity_decl_handler() - обработчик необрабатываемых (NDATA) данных;
xml_set_notation_decljiandler() - обработчик нотаций;
xml_set_external_entity_ref_handler() - обработчик внешних ссылок.
Коды ошибок
Модулем интерпретатора XML определяются следующие константы кодов ошибок (возвращаемые функцией xml_parse()):
XML_ERROR_NONE;
XML_ERROR_NO_MEMORY;
XML_ERROR_SYNTAX;
XML ERRORJO ELEMENTS;
XML_ERROR_INVALIDJOKEN;
XML_ERROR_UNCLOSED_TOKEN;
XML_ERROR_PARTIAL_CHAR;
XMLJRRORJAG 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;
XMLJRROR_ASYNC_ENTITY;
XML_ERROR_BAD_CHAR_REF;
XML_ERROR_BINARY_ENTITY_REF;
XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF;
XML_ERROR_MISPLACED_XML_PI;
XMLJRRORJJNKNOWN ENCODING;
XML_ERRORJNCORRECT_ENCODING;
XML_ERROR_UNCLOSED_CDATA_SECTION;
XML_ERROR_EXTERNAL_ENTITY_HANDLING.
Примеры
Следующие примеры демонстрируют возможности интерпретации документов XML.
Распечатка структуры XML документа
Сценарий выводит структуру на основе поиска открывающих и закрывающих тегов, используя в качестве форматирования отступы.
Sfile = "data.xml": Sdeptti = аггау()
function startElement($parser. $name. Sattrs)
{ global Sdepth:
for ($1 = 0. $1 < $depth[$parser]; $i++) { print "". }
print "$name\n": $depth[$parser]++; }
function endElement($parscr. $name)
{ global $depth- $depth[$parser]-; }
$xml_parser = xml_parser_create().
xml_set_element_nandler
($xml_parser. "startElement", "endElement"):
if (!($fp = fopen($file. "r"») {
dieC'could not open XML input")
while «data = fread($fp. 4096)) {
ll(!xml_parse($xml_parser, $data. feof($fp)))
{ die(sprintf("XML error: Xs at line id".
xml_error_string(xml_get_error_code($xml_parser)),
xml_get_current_l ine_number($xml_parser)));
xml_parser_free($xml на-se"):
Преобразование тегов XML в HTML
$file = "data xml '. $jndp_array = arrayt
"BOLD" > "B". "EMPHASIS" => "I".
"LITERAL" => "TT' );
function startElement($parset . $пате. Sattrs)
{ global $map_array; if (ShtTiltag = $map_array[$namej)
{ print "<$htmltag>'. function endElanenttSparser. $namp)
{ global $mao_array. if (Shtmltag - $map_array[$name]) (
print "</$htmltag>"; } } function characterData(Sparser. $data) {
print Sdata: } $xml_parser = xml_parser_create();
// отключить чувствительность к регистру
xml_parser_set_option
($xml_parser. XML_OPTION_CASE_FOLDING. TRUE):
xml_set_element_handler
($xml_parser. "startElement". "endElement");
xml_set_character_data_handler
($xml_parser. "characterData");
if (!($fp = fopen($file. "r")))
{ dieC'could not open XML input"); } while (Sdata = fread(Sfp. 4096)) {
if (!xml_parse($xml_parser. Sdata. feof($fp)))
{ die(spnntf("XML error: £s at line %u".
xml_error_string(xml_get_error_code
(Sxml_parser)). xml_get_current_line_number($xml_parser))): }
} xml_parser_free($xml_parser).
Обработка специальных конструкций XML
Пример демонстрирует обработку подключенных внешних файлов и инструкций обработки. Использует при выводе разметку HTML.
$file = "xmltest.xml"; function trustedFile(Sflle) {
// исполнять код можно только в
собственных файлах if (!eregi(""([a-z]+)://". Sfile)
&& fileowner(Sfile) == getmyuid()) ( return TRUE; }
return FALSE. }
function startElement(Sparser. $name. Sattribs) {
print "<<font color=\"#OOOOcc\">Sname</font>"
: if (sizeof(Sattnbs)) { while (list(Sk. $v) = each(Sattnbs))
{ print " <font color=\"#009900\">
$k</font>=\"<font color=\"#990000\">$v</font>\"": } }
print ">": }function endElement(Sparser. Sname) {
print "</<font color=\"#0000cc\">$name</font>>": }
function characterData(Sparser. $data)
{ print "<b>Sdata</b>"; }
function PIHandler($parser. Starget. $data)
{ switch (strtolower(Starget)) { case "php":
global $parser_file.
// Проверим допустимо ли выпрямить код PHP из документа
// или ограничиться его простым отображением
if (trustedFile($parser_file[Sparser]))
eval($data). else pnntfC'Kofl PHP: <i>fc</i>".
htmlspecialchars(Sdata)): break; } }
function defaultHandler($parser, Sdata) {
if (substrtSdata, 0. 1) == "&" && substr(Sdata. -1. 1)
== ".") { pnntf ('<font color="#aaOOaa">Us</font>'.
html sped a lchars (Sdata)): } else {
printf('<font size="-l">£s</font>'.
htmlspecialchars(Sdata)); } }
function externalEntityRefHandler
(Sparser, SopenEntityNames.
$base. Ssystemld. Spublicld) { if (Ssystemld) {
if (!list($parser. Sfp) = new_xml_parser(Ssystemld)) {
printf("Необрабатываемая секция %s в позиции
Zs\n". SopenEntityNames. Ssystemld);
return FALSE; } while (Sdata = fread($fp. 4096)) {
if (!xml_parse(Sparser. Sdata. feof(Sfp))) {
pnntfC'XML error: Is в стороке %d '%s'
\n". xml_error_string(xml_get_error_code($parser))
. xml_get_currentline_number($parser).
$openEntityNames): xml_parser_free($parser);
return FALSE: } }
xml_parser_free(Sparser). return TRUE; }
return FALSE: }
function new_xml_parser($file)
{ global $parser_file:
$xml_parser = xml_parser_create():
xml_parser_set_option
($xml_parser. XML_OPTION_CASE_FOLDING. 1):
xml_set_element_handler
($xml_parser. "startElement". "endElement"):
xml_set_character_datajiandler
($xml_parser. "characterOata");
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. }
if (!is_array($parser_file)) { settype($parser_file, "array"); }
Sparser_file[$xml_parser] =
$file. return array($xml_parser. $fp);}
//-.
if (!(list($xml_parser. $fp) =
new_xml_parser($file))) { dieC'could not open XML input"); }
print "<pre>"; while (Sdata = freadUfp. 4096)) {
if (!xml_parse($xml_parser. Sdata. feof($fp)))
{ die(spnntf("XML error ^s at line *d\n". xml_error_string
(xml_get_error_code
($xml_parser)). xml_get_current_line_number($xml_parser)));} }
print "</pre>": print "parse completed";
xml_parser_free($xml_parser):
Содержимое файла xmltest.xml:
<?xml version='1.0">>
<!DOCTYPE chapter SYSTEM "/just/a/test.dtd" [
<!ENTITY plainEntity "FOO entity">
<!ENTITY systemEntity SYSTEM "xmltest2.xml"> ]>
<chapter> <TITLE>Title &plainEntity:</TITLE> <para>
<informaltable> <tgroup cols="3">
<tbody> <row><entry>al</entry>
<entry morerows="l">bl</entry>
<entry>cl</entry></row> <row><entry>a2</entry>
<entry>c2</entry></row> <row><entry>a3</entry>
<entry>b3</entryxentry>c3</entry></row> </tbody>
</tgroup> </informaltable> </para> SsystemEntity.
<sectl id="about">
<title>About this Document</title> <para>
<!- this is a comment ->
<?php print 'Hi! PHP version ' .phpverslon(): ?>
</para> </sectl> </chapter>
Подключаемый файл xmltest2.xml:
<?xml version="1.0"?> <!DOCTYPE foo [
<!ENTITY testEnt "test entity"> ]> <foo>
<element attnb="value"/> StestEnt;
<?php print "This is PHP code."; ?> </foo>
xml_parser_create
Инициализация интерпретатора XML
int xml_parser_create ([string encoding])
Необязательным аргументом можно указать кодировку, котор дует использовать: ISO-8859-1 (по умолчанию), US-ASCII, U
Возвращает дескриптор созданного интерпретатора (исполы последующими функциями) или FALSE - при ошибке.
xml_set_object
Разрешение использования интерпретатора XML внутри объекта
void xml_set_object (int parser, object Subject)
Функция позволяет использовать в качестве всех функций oi чиков методы объекта object.
<?php class xml { var Sparser.
function xml (){ // конструктор
$this->parser - xml_j>arser_create();
xml_set_object($this->parser.&$thi s):
xml_set_element_handler
($this->parser."tag_open"."tag_close"):
xml_set_character_data_handler($this->parser."cdata"); }
function parse($data) ( xml parse(5this->parser.$data). }
function tag_oper,(Sparser.Stag.tattnbutes) {
echo "** Tag open: ": var dump($tag.$attributes). }
function cdata($parser.$cdata) {
echo " ++ Data: ": varjJump($cdita): }
function tag_close(Sparser.$tag) {
echo "** Tag close: "; var_dump($tag): }
} // end of class xml $xml_parser = new xml():
$xml_parser-parse('<A ID="99">PHP
<z a="d"/>aaa</A>') ?>
При запуске пример выведет:
** Tag open: stnng(l) "A" array(l) { f"IO"]=> stnng(2) "99"}
++ Data stnng(4) "PHP "
** Tag open: stnng(l) "Z" array(l) { ["A">>
stnng(l) "d" } ** Tag close: stnng(l) "Z"
t-+ Data: stnng(3) "aaa" ** Tag close: stnng(l) "A"
xml_set element_handler
Назначение обработчиков открывающего и закрывающего тега
int xml_set_element_hand1er (int parser, string startElementHandler, string endElementHandler)
В аргументах startElementHandler и endElementHandler указываются имена пользовательских функций, вызываемых во время интерпретации (при вызове xml_parse()), когда интерпретатор встречает открывающие и закрывающие теги.
Функции должны принимать следующие аргументы:
startElementHandler (Int parser, string name, array attribs)
endElementHandler (int parser, string name)
Аргумент name содержит имя тега, attribs - ассоциативный массив, содержащий атрибуты тега (если они имеются).
xml_set_character_data_handler
Назначение обработчика данных
int xml_set_character_data_handler (int parser, string handler)
Устанавливает пользовательскую функцию с именем handler как обработчик данных документа. Данными считается все то, что находится между тегами, включая пробелы. Эта функция будет вызываться во время интерпретации (при вызове xml_parse()). Она должна соответствовать прототипу:
handler (int parser, string data).
В аргументе data функция получает текущий блок данных.
xml_set_processing_instruction_handler
Назначение обработчика инструкций обработки
int xml_set_processing_instruction_handler (int parser, string handler)
Инструкции обработки имеют следующий формат:
<?target data . ?>
Используя этот формат, можно вставлять в XML документы РНР-кода, но последовательность, обозначающая конечный тег (?>), не должна встречаться в середине кода, иначе оставшаяся часть кода будет рассматриваться интерпретатором как обычные данные. Функция, устанавливаемая в качестве обработчика, должна соответствовать следующему прототипу:
handler (int parser, string target, string data)
В аргументе target функция получает маркер, определяющий тип кода (это может быть не только «php», но и другой, кроме зарезервированного типа «xml»). В аргументе data передается текст всего кода, который содержится внутри тега.
xml_set_default_handler
Установка обработчика по умолчанию
int xml_set_default_handler (int parser, string handler)
Устанавливаемым обработчиком будут обрабатываться все данные, которые не могут быть обработаны иным зарегистрированным обработчиком. Функция обработчика должна соответствовать следующему прототипу:
handler (int parser, string data).
В аргументе data функция получает блок данных для обработки.
xml_set_unparsed_entity_decl_handler
Установка обработчика необрабатываемых данных
int xml_set_unparsed_entity_decl_handler (int parser, string handler)
Данные такого типа (NDATA) определяются спецификацией XML 1.0 (раздел 4.2.2) и имеют подобный следующему формат:
<!ENTITY name {publicld | systemld} NDATA notationName>
Функция обработчика должна соответствовать следующему прототипу:
handler (int parser, string entityNarae, string base, string systemld, string publicld, string notationName).
В аргументе entityName функция получает тип тега, в base в настоящее время всегда содержится пустая строка. В аргументах systemld и publicld содержатся соответственно системный и публичный внешние идентификаторы. Аргумент notationName содержит имя нотации (см. функцию xml_set_notation_decl_handler()).
xml_set notation decl_handler
Установка обработчика объявлений нотаций
int xml_set_notation_decl_handler (int parser, string handler)
Нотации (являющиеся частью документов DTD) описаны в спецификации XML 1.0 (раздел 4.7) и имеют следующий формат:
<!NOTATION na'ie [systemlcl \ publ,cld}>
Функция обработчика должна соответствовать следующему прототипу:
handler (int parser, string notationName, string base, string systemld, string publicld)
В аргументе notationName функция получает имя нотации, в base в настоящее время всегда содержится пустая строка. В аргументах systemld и publicld содержатся соответственно системный и публичный внешние идентификаторы.
xml_set_external_entity_ref_handler
Установка обработчика внешних ссылок
int xml_set_external_entity_ref_handler (int parser, string handler)
Функция обработчика нотаций, содержащих внешние ссылки, должна возвращать целочисленное значение и соответствовать следующему прототипу:
int handler (int parser, string openEntityNames, string base, string systemld, string publicld).
Если обработчик возвращает значение FALSE (или не возвращает никакого), интерпретатор XML прекращает интерпретацию и функция xml_get_error_code() возвращает значение XML_ERROR_EXTERNAL_ENTITY_ HANDLING.
В аргументе openEntityNames функция получает список имен, открываемых для рекурсивной интерпретации (в виде строки, где разделителями являются пробелы), в base в настоящее время всегда содержится пустая строка. В аргументах systemld и publicld содержатся соответственно системный и публичный внешние идентификаторы.
xml_parse
Начало интерпретации документа XML
int xml_parse (int parser, string data [, int isFinal])
Функция позволяет обрабатывать документ XML по частям (многократно вызывая данную функцию и передавая каждый блок данных в аргументе data), тогда при обработке последней части документа в аргументе ispinal следует передать значение TRUE.
До начала интерпретации следует инициализировать интерпретатор parser и установить все функции-обработчики, которые будет вызывать данная функция. Функция возвращает значение TRUE, если интерпретация блока документа прошла успешно. В случае ошибки возвращается значение FALSE, и затем может быть получена информация об ошибке с помощью функций xml_get_error_code(), xml_error_string(), xml _get_current_line_number (), xml _get_current_col umnjiumber (), xml _get_ current_ byte_index().
xml_get_error_code
Получение кода ошибки интерпретатора XML
int xml_get_error_code (int parser)
Перечисление возвращаемых кодов ошибок (констант) см. в начале главы.
xml_error_string
Получение описания ошибки по ее коду
string xml_error_string (int code)
Функция позволяет получить в строке описание кода ошибки (возвращаемое функцией xml_get_error_code()). Если код недействителен, возвращается FALSE.
xml_get_current_line_number
Получение номера текущей интерпретируемой строки документа
int xml_get_current_line_number (int parser)
xml_get_current_column_number
Получение номера обрабатываемого байта в текущей интерпретируемой строке документа
int xml_get_current_column_number (int parser)
xml_get_current_byte_index
Получение позиции текущего байта документа
int xml_get_current_byte_index (int parser)
Нумерация байтов начинается с 0.
xml_parse_into_struct
Занесение документа XML в структурированный массив
int xml_parse_into_struct (int parser, string data, array &values, array &index)
Функция заносит XML-документ в 2 параллельные структуры: массив values содержит структуру тегов и их содержимое, a Index содержит вспомогательные индексы для облегчения нахождения начальных и конечных тегов в первом массиве (массивы следует передавать по ссылке).
$simple = "<para><note>simple note</note>
</para>"; $р = xml_parser_create();
xml_parse_i nto_struct($p.Ssimple.&$vais.&$т ndex);
xml_parser_free($p). echo "Index "; pnnt_r($index),
echo "\nVals ": pnnt_r($vals);
При запуске сценарий выводит:
Index Arrayf [PARA] » Array ( [0] => 0 [1] => 2 )
[NOTE] => Array ( [0] => 1 ) ) Vals Array(
[0] => Array ([tag] => PARA
[type] => open [level] => 1 ) [1] -> Array ([tag] => NOTE
[type] => complete [level] => 2 [value] => simple note )
[2] => Array ([tag] => PARA k [type] => close
[level] => 1 ; }
Интерпретация сложных документов становится громоздкой при использовании обработчиков. Хотя эта функция и не создает объект, подобный DOM, она генерирует структуру, которую легко преобразовать в древовидную. Можно, например, создавать объекты, представляющие данные. Следующий пример демонстрирует создание объектов из xml базы данных химических элементов.
Файл, moldb.xml
<?xml version="1.0"7> <moldb>
<molecule> <name>Al am ne</name>
<symbol>ala</symbol> <code>A</code>
<type>hydrophobic</type> </molecule>
<molecule> <name>Lysine</name>
<symbol>lys</symbol> <code>K</code>
<type>cha rged</type> </molecule> </moldb>
Сценарий parsemoldb.php, интерпретатор файла moldb.xml:
<?php class AminoAcid { // класс аминокислот
var Sname; // aa имя var Ssymbol:
// трехбуквенный символ var Scode:
// однобуквенный код var Stype: // свойства
function AminoAcid ($aa) { foreach ($aa as $k=>$v)
Sthis->$k = $aa[$k]; } }
function readDatabase(Sfllename) {
// read the xml database of arrrinoacids
Sdata = implode("",file(Sfilename));
Sparser = xml_parser_create();
xml_parser_set_option
($parser.XML_OPTION_CASE_FOLDING,0):
xml_parser_set_option
($parser.XML_OPTION_SKIP_WHITE.l).
xml_parse_into_struct(Sparser,Sdata,&$values.&$tags),
xml_parser_free($parser).
// loop through the structures foreach
($tags as $key=>$val) { if ($key == "molecule")
{ $molranges = $val;
for ($i=0. $1 < count($molranges):
$1+=2) { Soffset = $molranges[$i] + 1;
$len = $molranges[$i + 1] - Soffset: $tdb[]
= parseMol(array_slice($values. Soffset. Slen)): }
} else { continue: } } return $tdb: }
function parseMol($mvalues) {
for ($i=0; $1 < count($mvalues); $i++)
$mol[$mvalues[$i]["tag"]] = $mvalues[$i]["value"]:
return new AminoAcid($mol): }
$db = readDatabaseC'moldb.xml"):
echo "** Database of AminoAcid objects: ";
pnnt_r($db): ?>
После запуска сценария переменная $db будет содержать массив AminoAcid объектов и будет выведено:
** Database of AminoAcid objects: Array (
[0] => aminoacid Object (
[name] => Alamne [symbol] => ala
=> A [type] => hydrophobic ) [1] => aminoacid Object ( ~\ [name] => Lysine \ [symbol] => lys [code] => К [type] => charged j ) xml_parser_free Закрытие интерпретатора XML string xml_parser_free (int parser) xml_parser_set_option Установка параметра XML-интерпретатора int xml_parser_set_option (int parser, int option, mixed value) В аргументе option константой задается устанавливаемый параметр, а в value - ее новое значение. Возможные параметры: XML_OPTION_CASE_FOLDING (тип значения параметра - integer) -управляет преобразованием имен тегов в верхний регистр (по умолчанию - 1); XML_OPTION_TARGET_ENCODIN6 (тип значения параметра - string) -целевая кодировка: «ISO-8859-1», «US-ASCII» или «UTF-8». По умолчанию та же, что и установленная функцией xml_parser_ create(). xml_parser_get_option Получение параметра интерпретатора XML mixed xml_parser_get_option (int parser, int option) См. также: xml_parser_set_option(). utf8_decode Преобразование строки UTF-8 в ISO-8859-1. string utf8_decode (string data) См. также: utf8_encode(). utf8_encode Кодирование строки ISO-8859-1 в UTF-8 string utf8_encode (string data) Возвращает строку в формате UTF-8. UTF-8 - эго формат представления текста Unicode, позволяющий представить большее количество символов, чем кодировка ASCII. Первые 127 символов ASCII в кодировке UTF-8 имеют идентичное представление, а последующие (обычно это символы национальных языков) кодируются следующими способами: Кодировка UTF-8 Размер символа в байтах Число битов символа Кодировка 1 7 Obbbbbbb 2 И llObbbbb lObbbbbb 3 16 lllObbbb lObbbbbb lObbbbbb 4 21 llllObbb lObbbbbb lObbbbbb lObbbbbb Как видно, размер символа может быть от 1 до 4 бант; b представляет один бит данных.