Xml parse: примеры (PHP)
xml_parse(resource parser, string data [, bool is_final]): intФункция xml_parse() является частью встроенного XML парсера Expat в PHP. Она выполняет разбор XML документа, передавая данные в ранее определенные функции-обработчики. Этот подход основан на событийной модели (SAX - Simple API for XML).
Использование функции актуально для последовательной обработки больших XML файлов, когда загрузка всего документа в память (как в DOM) нежелательна или невозможна.
- parser (обязательный): ресурс, созданный функцией
xml_parser_create(). Указывает на используемый XML парсер. - data (обязательный): строка, содержащая часть или весь XML документ для разбора.
- is_final (опциональный, по умолчанию
false): логическое значение. Если установлено вtrue, указывает парсеру, что это последний фрагмент данных.
Функция возвращает целое число: 1 при успешном разборе или 0 в случае ошибки.
<?php
$parser = xml_parser_create();
function startElement($parser, $name, $attrs) {
echo "Открыт тег: $name\n";
}
function endElement($parser, $name) {
echo "Закрыт тег: $name\n";
}
function charData($parser, $data) {
$data = trim($data);
if ($data !== '') echo "Данные: $data\n";
}
xml_set_element_handler($parser, "startElement", "endElement");
xml_set_character_data_handler($parser, "charData");
$xml = "<book><title>PHP 8</title><author>Иван</author></book>";
if (xml_parse($parser, $xml, true)) {
echo "Разбор завершен успешно.\n";
} else {
echo sprintf("Ошибка: %s в строке %d",
xml_error_string(xml_get_error_code($parser)),
xml_get_current_line_number($parser));
}
xml_parser_free($parser);
?>Открыт тег: BOOK Открыт тег: TITLE Данные: PHP 8 Закрыт тег: TITLE Открыт тег: AUTHOR Данные: Иван Закрыт тег: AUTHOR Закрыт тег: BOOK Разбор завершен успешно.
<?php
$parser = xml_parser_create();
xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0); // Отключаем приведение к верхнему регистру
$result = [];
$currentTag = '';
function startElement($parser, $name, $attrs) use (&$result, &$currentTag) {
$currentTag = $name;
}
function charData($parser, $data) use (&$result, &$currentTag) {
if (trim($data) && $currentTag) {
$result[$currentTag] = trim($data);
}
}
xml_set_element_handler($parser, "startElement", null);
xml_set_character_data_handler($parser, "charData");
// Имитация поступления данных частями
$chunks = [
"<user><name>Мария",
"</name><age>28",
"</age></user>"
];
foreach ($chunks as $chunk) {
xml_parse($parser, $chunk, false);
}
xml_parse($parser, '', true); // Финальный вызов
print_r($result);
xml_parser_free($parser);
?>Array
(
[name] => Мария
[age] => 28
)PHP предлагает несколько методов работы с XML, каждый со своими особенностями.
Простой объектно-ориентированный интерфейс для чтения и изменения XML. Преобразует XML в объект, с которым можно работать как с массивом. Идеален для небольших документов и простых задач.
$xml = simplexml_load_string('<book><title>Test</title></book>');
echo $xml->title;Test
Полная реализация DOM (Document Object Model). Позволяет создавать, изменять, валидировать и навигировать по XML. Требует больше памяти, но предоставляет полный контроль над документом.
$dom = new DOMDocument();
$dom->loadXML('<root><item>1</item></root>');
echo $dom->getElementsByTagName('item')->item(0)->nodeValue;1
Как и xml_parse(), использует потоковое чтение (pull-parser), но с более удобным итеративным API. Рекомендуется для обработки больших файлов вместо устаревшего Expat.
$reader = new XMLReader();
$reader->XML('<data><value>42</value></data>');
while ($reader->read()) {
if ($reader->nodeType == XMLReader::ELEMENT && $reader->name == 'value') {
echo $reader->readInnerXml();
}
}42
Модуль SAX в Python предоставляет схожую событийную модель. Обработчики определяются в классе, наследующем ContentHandler.
import xml.sax
class MyHandler(xml.sax.ContentHandler):
def startElement(self, name, attrs):
print(f"Начало: {name}")
def characters(self, content):
if content.strip():
print(f"Текст: {content}")
handler = MyHandler()
parser = xml.sax.make_parser()
parser.setContentHandler(handler)
parser.parseString("<note><body>Текст</body></note>")Начало: note Начало: body Текст: Текст
В браузерном JavaScript нет нативного SAX-парсера, но существуют библиотеки, например, sax-js для Node.js. Работа строится на подписке на события.
const sax = require('sax');
const parser = sax.parser(true);
parser.onopentag = (tag) => { console.log(`Открыт: ${tag.name}`); };
parser.ontext = (text) => { if(text.trim()) console.log(`Текст: ${text}`); };
parser.write('<doc>Привет</doc>').close();Открыт: doc Текст: Привет
MySQL предоставляет функции для извлечения данных из XML строк, такие как ExtractValue(). Однако это не полноценный парсер, а инструмент для простого XPath-запроса.
SELECT ExtractValue('<a><b>X</b></a>', '/a/b') AS result;result X
Парсер Expat по умолчанию работает с UTF-8. Если передать данные в другой кодировке без указания этого факта, возникнут ошибки разбора.
<?php
$parser = xml_parser_create();
$xml_windows1251 = mb_convert_encoding('<test>тест</test>', 'windows-1251', 'UTF-8');
// Пытаемся разобрать строку в CP1251 как UTF-8
if (!xml_parse($parser, $xml_windows1251, true)) {
echo "Ошибка: ", xml_error_string(xml_get_error_code($parser));
}
?>Ошибка: Invalid character
Решение: использовать xml_parser_create() с указанием кодировки или конвертировать данные.
Парсер строго следит за корректностью XML (закрытые теги, правильная вложенность).
<?php
$parser = xml_parser_create();
xml_set_element_handler($parser, function(){}, function(){});
$bad_xml = "<open><inner></open></inner>"; // Неправильная вложенность
if (!xml_parse($parser, $bad_xml, true)) {
echo "Строка: ", xml_get_current_line_number($parser), " Ошибка: ", xml_error_string(xml_get_error_code($parser));
}
?>Строка: 1 Ошибка: Mismatched tag
Парсер может не обработать XML сущности, если не установлен соответствующий обработчик.
<?php
$parser = xml_parser_create();
$xml_with_entity = '<!DOCTYPE test [ <!ENTITY example "Пример"> ]><test>&example;</test>';
// Без обработчика сущностей разбор может завершиться с ошибкой или проигнорировать &example;
xml_parse($parser, $xml_with_entity, true);
?>Решение: использовать xml_set_external_entity_ref_handler() или отключить обработку DTD.
Функция xml_parse() и весь модуль XML Expat остаются стабильными и практически не менялись в последних основных версиях PHP. Основные изменения касаются не самой функции, а её контекста:
- В PHP 8.0 ресурсы (resource), возвращаемые
xml_parser_create(), были заменены на объекты классаXMLParser. Это изменение прозрачно для большинства пользователей, так как объекты передаются вxml_parse()так же, как и ресурсы. Тип аргументаparserтеперь объявлен какXMLParser. - Были усилены типизация и проверки аргументов. Передача значения неправильного типа в PHP 8 вызовет TypeError, а в PHP 7 это могло вызвать предупреждение.
- Модуль Expat считается устаревшим (legacy) по сравнению с
XMLReader, но продолжает поддерживаться для обратной совместимости. Новые проекты рекомендуется использоватьXMLReaderдля потокового разбора.
<?php
$parser = xml_parser_create('UTF-8');
xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
$tree = [];
$path = [];
$current = &$tree;
function startElement($parser, $name, $attrs) use (&$current, &$path) {
$node = ['_tag' => $name, '_attrs' => $attrs, '_children' => []];
$current[] = &$node;
$path[] = &$current;
$current = &$node['_children'];
}
function endElement($parser, $name) use (&$current, &$path) {
$current = &array_pop($path);
}
function charData($parser, $data) use (&$current) {
$data = trim($data);
if ($data !== '') {
$current[] = $data;
}
}
xml_set_element_handler($parser, "startElement", "endElement");
xml_set_character_data_handler($parser, "charData");
$xml = '<catalog>
<book id="1"><title>Война и мир</title><author>Толстой</author></book>
<book id="2"><title>Преступление и наказание</title></book>
</catalog>';
if (xml_parse($parser, $xml, true)) {
// Очистка служебных ключей для наглядности
function clean(&$arr) {
foreach ($arr as &$item) {
if (is_array($item)) {
unset($item['_children'], $item['_tag']);
clean($item);
}
}
}
clean($tree);
echo "Успешно разобрано. Первая книга: ";
print_r($tree[0]['book'][0]);
}
xml_parser_free($parser);
?>Успешно разобрано. Первая книга:
Array
(
[_attrs] => Array
(
[ID] => 1
)
[0] => Война и мир
[1] => Толстой
)<?php
$parser = xml_parser_create();
xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
$recordCount = 0;
$inRecord = false;
$currentTag = '';
$currentData = [];
function startElement($parser, $name, $attrs) use (&$inRecord, &$currentTag, &$recordCount) {
if ($name === 'RECORD') {
$inRecord = true;
$recordCount++;
}
$currentTag = $name;
}
function endElement($parser, $name) use (&$inRecord, &$currentData) {
if ($name === 'RECORD') {
$inRecord = false;
// Здесь можно сохранить $currentData в БД или файл
// echo "Запись #" . $recordCount . " обработана.\n";
$currentData = [];
}
$currentTag = '';
}
function charData($parser, $data) use (&$inRecord, &$currentTag, &$currentData) {
if ($inRecord && $currentTag && trim($data)) {
@$currentData[$currentTag] .= trim($data);
}
}
xml_set_element_handler($parser, "startElement", "endElement");
xml_set_character_data_handler($parser, "charData");
// Имитация чтения большого файла
$handle = fopen('large_data.xml', 'r');
if ($handle) {
while (!feof($handle)) {
$chunk = fread($handle, 4096); // Чтение по 4KB
if (!xml_parse($parser, $chunk, feof($handle))) {
echo sprintf("Ошибка в строке %d: %s\n",
xml_get_current_line_number($parser),
xml_error_string(xml_get_error_code($parser)));
break;
}
}
fclose($handle);
echo "Обработано записей: $recordCount\n";
}
xml_parser_free($parser);
?>Обработано записей: [число записей в файле]
<?php
function defaultHandler($parser, $data) {
// Обработчик по умолчанию для сущностей и прочего
echo "[Необработанные данные: $data]";
}
$parser = xml_parser_create();
xml_set_default_handler($parser, "defaultHandler");
$xml = '<doc><copy>©</copy> текст</doc>';
xml_parse($parser, $xml, true);
xml_parser_free($parser);
?>[Необработанные данные: ©][Необработанные данные: ] текст