xml_parse()
Функция xml_parse() — встроенная функция PHP для потокового разбора XML-данных в стиле SAX без загрузки всего документа в память.
Что такое xml_parse()?
Функция xml_parse() — это встроенная функция PHP, предназначенная для разбора XML-данных. Она относится к расширению XML Parser и реализует потоковый парсер в стиле SAX (Simple API for XML). В отличие от парсеров на основе дерева, она обрабатывает XML последовательно, вызывая функции обратного вызова по мере обнаружения элементов, атрибутов и текстовых данных. Это делает её очень эффективной при разборе больших XML-файлов без загрузки всего документа в память.
Функция xml_parse() полезна, когда необходимо разобрать XML-данные в PHP — например, для извлечения данных из XML-файла, преобразования XML-данных в другой формат или обработки XML-потоков в реальном времени.
Синтаксис
Синтаксис функции xml_parse() выглядит следующим образом:
xml_parse($parser, $data, $is_final = false): intПараметры
$parser— дескриптор XML-парсера, возвращённый функциейxml_parser_create(). Это объект, хранящий состояние разбора.$data— фрагмент (или весь) XML-текст, передаваемый в парсер.$is_final— устанавливается вtrueпри передаче последнего фрагмента данных. Пока значение равноfalse, парсер сохраняет своё состояние, что позволяет вызватьxml_parse()снова со следующим фрагментом.
Возвращаемое значение
xml_parse() возвращает 1 (истина) в случае успеха и 0 (ложь) в случае ошибки. Функция не возвращает разобранные данные — разобранное содержимое передаётся в обработчики обратного вызова, которые вы зарегистрировали. При возврате 0 проверьте ошибку с помощью xml_get_error_code() и xml_error_string().
Примеры использования
Рассмотрим несколько практических примеров использования xml_parse() в PHP.
Пример 1: Разбор XML с обработчиками событий
xml_parse() — это SAX-парсер: он не строит документ за вас, а генерирует события. Чтобы получить какой-либо вывод, необходимо зарегистрировать функции-обработчики. В приведённом примере используется встроенная XML-строка, поэтому его можно запустить без внешнего файла:
<?php
$xml = <<<XML
<?xml version="1.0"?>
<note>
<to>Tove</to>
<from>Jani</from>
</note>
XML;
$parser = xml_parser_create();
// Keep element names in their original case instead of upper-casing them.
xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
xml_set_element_handler(
$parser,
fn($p, $name, $attrs) => print("Start: $name\n"),
fn($p, $name) => print("End: $name\n")
);
xml_set_character_data_handler($parser, function ($p, $data) {
$data = trim($data);
if ($data !== "") {
echo "Text: $data\n";
}
});
if (!xml_parse($parser, $xml, true)) {
$code = xml_get_error_code($parser);
echo "Error: " . xml_error_string($code)
. " at line " . xml_get_current_line_number($parser);
}
xml_parser_free($parser);Результат вывода:
Start: note
Start: to
Text: Tove
End: to
Start: from
Text: Jani
End: from
End: noteПарсер обходит документ сверху вниз и вызывает ваши обработчики открывающего тега, текстовых данных и закрывающего тега в порядке их следования в документе. Обработчики регистрируются с помощью xml_set_element_handler() и xml_set_character_data_handler(), всё передаётся одним вызовом ($is_final = true), а парсер освобождается функцией xml_parser_free().
Пример 2: Разбор файла или потока данных по частям
Главное преимущество xml_parse() — инкрементальный разбор: документ подаётся по частям, поэтому даже многогигабайтный файл никогда не должен целиком помещаться в память. Для каждого фрагмента, кроме последнего, передавайте $is_final = false:
<?php
$parser = xml_parser_create();
xml_set_element_handler(
$parser,
fn($p, $name, $attrs) => print("<$name>\n"),
fn($p, $name) => print("</$name>\n")
);
$handle = fopen("data.xml", "r"); // or php://stdin for a stream
while (($chunk = fread($handle, 4096)) !== false) {
$isFinal = feof($handle);
if (!xml_parse($parser, $chunk, $isFinal)) {
$code = xml_get_error_code($parser);
echo "XML error: " . xml_error_string($code)
. " at line " . xml_get_current_line_number($parser);
break;
}
if ($isFinal) {
break;
}
}
fclose($handle);
xml_parser_free($parser);Поскольку парсер сохраняет состояние между вызовами, элемент может начаться в одном фрагменте и завершиться в другом — xml_parse() корректно объединяет события. Именно это делает функцию подходящей для больших XML-файлов, где подход на основе дерева, например SimpleXML, привёл бы к исчерпанию памяти.
Пример 3 (справочный): разбор файла за один вызов
Если XML-документ достаточно мал, чтобы поместиться в память, его можно прочитать с помощью file_get_contents() и передать всю строку в xml_parse() одним вызовом. В качестве обработчиков также можно использовать именованные функции (строковые имена) вместо замыканий:
$xml_parser = xml_parser_create();
xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, 0);
// Define handler functions
function startElement($parser, $name, $attrs) {
echo "Start element: $name\n";
}
function endElement($parser, $name) {
echo "End element: $name\n";
}
function characterData($parser, $data) {
echo "Data: $data\n";
}
// Set handlers
xml_set_element_handler($xml_parser, "startElement", "endElement");
xml_set_character_data_handler($xml_parser, "characterData");
$xml_data = file_get_contents("data.xml");
if (!xml_parse($xml_parser, $xml_data, true)) {
$error_message = xml_error_string(xml_get_error_code($xml_parser));
$error_line = xml_get_current_line_number($xml_parser);
echo "XML Parsing Error: $error_message at line $error_line";
}
xml_parser_free($xml_parser);Этот код создаёт XML-парсер с помощью xml_parser_create() и отключает приведение имён к верхнему регистру. Затем определяются три функции обратного вызова: startElement() для открывающих тегов, endElement() для закрывающих тегов и characterData() для текстового содержимого. Обработчики регистрируются с помощью xml_set_element_handler() и xml_set_character_data_handler().
Скрипт читает файл "data.xml" и передаёт его в xml_parse(). По мере потокового разбора XML парсер автоматически вызывает зарегистрированные обработчики. При возникновении ошибки код получает код ошибки и сообщение с помощью xml_get_error_code() и xml_error_string() и выводит описание ошибки. В конце память парсера освобождается с помощью xml_parser_free().
Распространённые ошибки
- Нет обработчиков — нет вывода.
xml_parse()вызывает только зарегистрированные обратные вызовы. Если обработчики не заданы, разбор пройдёт успешно, но ничего видимого не произойдёт. - Текстовые данные поступают по частям. Один текстовый узел может вызвать
xml_set_character_data_handlerнесколько раз (например, вокруг ссылок на сущности), поэтому накапливайте текст в буфере, а не рассчитывайте получить его целиком за один вызов. - Пробельные символы — тоже текстовые данные. Отступы между тегами вызывают обработчик текстовых данных. Используйте
trim()(как в Примере 1), если вас интересует только реальный текст. - Всегда освобождайте парсер. Вызывайте
xml_parser_free()по завершении; в PHP 8+ дескриптор является объектомXMLParser, который подлежит сборке мусора, однако явное освобождение позволяет экономно расходовать память в долго работающих скриптах.
Связанные функции
xml_parser_create()— создаёт дескриптор парсера, передаваемый вxml_parse().xml_set_element_handler()— регистрирует обратные вызовы для открывающих и закрывающих тегов.xml_set_character_data_handler()— регистрирует обратный вызов для текстового содержимого.xml_parse_into_struct()— разбирает XML в плоский массив вместо генерации событий.- SimpleXML — высокоуровневая альтернатива на основе дерева для небольших документов.
Заключение
Функция xml_parse() в PHP является ядром расширения XML Parser в стиле SAX. Вместо возврата документа она потоково обрабатывает XML и вызывает зарегистрированные обработчики, возвращая 1 в случае успеха и 0 в случае ошибки. Параметр $is_final позволяет разбирать данные по частям, что обеспечивает эффективное использование памяти даже при работе с большими файлами. В сочетании с xml_parser_create(), функциями регистрации обработчиков и xml_parser_free() она представляет собой быстрый и экономный способ обработки XML в PHP.