W3docs

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.

Практика

Практика
Что такое XML Parser в PHP?
Что такое XML Parser в PHP?
Was this page helpful?