Xml set element handler: примеры (PHP)

Обработка XML с помощью функции xml_set_element_handler
Раздел: XML
xml_set_element_handler(resource parser, callable start_handler, callable end_handler): bool

Функция xml_set_element_handler

Описание и назначение

Функция xml_set_element_handler() в PHP является частью расширения XML Parser. Она устанавливает обработчики для событий начала и конца элементов XML-документа при использовании парсера на основе событий (SAX-подобный). Эта функция используется, когда необходим последовательный, потоковый парсинг XML, особенно для обработки больших файлов без загрузки всего документа в память.

Аргументы функции

Функция принимает три аргумента:

  1. parser (ресурс) — ссылка на XML-парсер, созданный функцией xml_parser_create().
  2. start_handler (callable) — имя функции, которая будет вызываться при открытии XML-элемента. Функция должна принимать три параметра: парсер, имя элемента и массив атрибутов.
  3. end_handler (callable) — имя функции, вызываемой при закрытии XML-элемента. Она принимает два параметра: парсер и имя элемента.

Примеры использования

Базовый пример парсинга

Простейший случай обработки элементов.

<?php
$xml = "<book><title>PHP Guide</title></book>";
$parser = xml_parser_create();

function startElement($parser, $name, $attrs) {
    echo "Начался элемент: $name\n";
    if (!empty($attrs)) {
        print_r($attrs);
    }
}

function endElement($parser, $name) {
    echo "Закрылся элемент: $name\n";
}

xml_set_element_handler($parser, "startElement", "endElement");
xml_parse($parser, $xml);
xml_parser_free($parser);
?>
Начался элемент: BOOK
Начался элемент: TITLE
Закрылся элемент: TITLE
Закрылся элемент: BOOK
Использование без обработчика закрытия

Можно передать null, если обработка закрывающих тегов не требуется.

<?php
$xml = "<data><item id='1'/><item id='2'/></data>";
$parser = xml_parser_create();

function startElement($parser, $name, $attrs) {
    echo "Элемент: $name, ID: " . ($attrs['ID'] ?? 'нет') . "\n";
}

xml_set_element_handler($parser, "startElement", null);
xml_parse($parser, $xml);
xml_parser_free($parser);
?>
Элемент: DATA, ID: нет
Элемент: ITEM, ID: 1
Элемент: ITEM, ID: 2

Альтернативные функции в PHP

В PHP существуют иные подходы к обработке XML.

SimpleXML

Предоставляет простой объектно-ориентированный интерфейс для работы с XML как с деревом. Удобен для чтения и изменения небольших XML-документов.

DOMDocument

Реализует DOM (Document Object Model) стандарт. Позволяет манипулировать XML-деревом, выполнять сложные запросы с помощью XPath. Требует больше памяти, чем SAX-парсер.

XMLReader

Обеспечивает последовательное, курсорное чтение XML (pull-парсер). Более современная и гибкая альтернатива старому XML Parser, часто предпочтительна для потоковой обработки больших файлов.

Аналоги в других языках

Python: xml.sax

Модуль SAX в Python работает на основе событий, аналогично xml_set_element_handler.

import xml.sax

class MyHandler(xml.sax.ContentHandler):
    def startElement(self, name, attrs):
        print(f"Начался элемент: {name}")
    def endElement(self, name):
        print(f"Закрылся элемент: {name}")

parser = xml.sax.make_parser()
parser.setContentHandler(MyHandler())
parser.parse("data.xml")
JavaScript: SAX-парсеры

В среде Node.js существуют библиотеки, например, sax-js, обеспечивающие событийный парсинг.

const sax = require('sax');
const parser = sax.parser(true);
parser.onopentag = function(node) {
    console.log(`Открыт тег: ${node.name}`);
};
parser.onclosetag = function(name) {
    console.log(`Закрыт тег: ${name}`);
};
parser.write('<root><a>test</a></root>').close();
Java: SAXParser

В Java используется интерфейс org.xml.sax.helpers.DefaultHandler и метод startElement.

Типичные ошибки

Некорректные имена функций-обработчиков

Передача несуществующей функции или метода приводит к предупреждениям и сбою обработки.

<?php
$parser = xml_parser_create();
// Функция myStartHandler не определена
xml_set_element_handler($parser, "myStartHandler", "myEndHandler");
xml_parse($parser, "<test/>"); // Вызовет Warning
?>
Проблемы с кодировкой

Несоответствие кодировки парсера и данных приводит к некорректным именам элементов.

<?php
$parser = xml_parser_create("UTF-8");
xml_set_element_handler($parser, function($p, $name) {
    echo "Элемент: $name"; // Имя может отобразиться некорректно
}, null);
// XML в кодировке Windows-1251
xml_parse($parser, "<тест>данные</тест>");
?>
Отсутствие инициализации парсера

Попытка использования освобожденного парсера.

<?php
$parser = xml_parser_create();
xml_parser_free($parser);
xml_set_element_handler($parser, "start", "end"); // Неверно
?>

Изменения в новых версиях PHP

В PHP 8, функция xml_set_element_handler() не претерпела кардинальных изменений в сигнатуре или основном поведении. Однако, в связи с ужесточением типизации в PHP 8, передача аргументов некорректного типа может приводить к более строгим ошибкам. Расширение XML Parser по-прежнему доступно в виде основного расширения. Ранее, в PHP 5, имена элементов и атрибутов по умолчанию преобразовывались в верхний регистр, но в PHP 8 это поведение можно контролировать с помощью xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0).

Расширенные примеры

Парсинг с сохранением структуры в массив
Пример php
<?php
$xml = "<catalog><book id='1'><title>Книга 1</title></book></catalog>";
$parser = xml_parser_create();
xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);

$tree = [];
$path = [];

function startElement($parser, $name, $attrs) {
    global $tree, $path;
    $node = ['attrs' => $attrs, 'children' => []];
    if (empty($path)) {
        $tree[$name] = $node;
        $path[] = &$tree[$name];
    } else {
        $parent = &$path[count($path)-1];
        $parent['children'][$name][] = $node;
        $path[] = &$parent['children'][$name][count($parent['children'][$name])-1];
    }
}

function endElement($parser, $name) {
    global $path;
    array_pop($path);
}

xml_set_element_handler($parser, "startElement", "endElement");
xml_parse($parser, $xml);
print_r($tree);
xml_parser_free($parser);
?>
Array
(
    [catalog] => Array
        (
            [attrs] => Array
                ( )
            [children] => Array
                (
                    [book] => Array
                        (
                            [0] => Array
                                (
                                    [attrs] => Array
                                        (
                                            [id] => 1
                                        )
                                    [children] => Array
                                        (
                                            [title] => Array
                                                (
                                                    [0] => Array
                                                        (
                                                            [attrs] => Array
                                                                ( )
                                                            [children] => Array
                                                                ( )
                                                        )
                                                )
                                        )
                                )
                        )
                )
        )
)
Обработка пространств имен
Пример php
<?php
$xml = "<ns:root xmlns:ns='http://example.com'><ns:child/></ns:root>";
$parser = xml_parser_create_ns();

function startElementNS($parser, $name, $attrs) {
    // $name приходит в формате "префикс:локальноеИмя"
    list($prefix, $local) = explode(':', $name);
    echo "Элемент: $local (префикс: $prefix)\n";
}

xml_set_element_handler($parser, "startElementNS", null);
xml_parse($parser, $xml);
?>
Элемент: root (префикс: ns)
Элемент: child (префикс: ns)

PHP xml_set_element_handler function comments

En
Xml set element handler Set up start and end element handlers