Работа с XML средствами PHP
Давайте начнем с самого простого: загрузки и выгрузки XML-документа с использованием DOM. Основной класс, который мы будем использовать, это DOMDocument. Через него можно создавать новые документы, сохранять их, а также загружать существующие.
// Создаём XML-документ версии 1.0 с кодировкой utf-8 $dom = new DOMDocument('1.0', 'utf-8'); // Текст XML-документа $strXML = '- Первый
- Второй
'; // Загружаем XML-документ из строки $dom->loadXML($strXML); /* // Загружаем XML-документ из файла $dom->load('doc.xml'); // Убираем лишние пробелы и отступы $dom->preserveWhiteSpace = false; */ // Сохраняем XML-документ как строку и выводим в браузер $xml = $dom->saveXML(); echo htmlspecialchars($xml); // Сохраняем XML-документ в файл $dom->save('doc.xml'); ?>
Как получить список дочерних узлов
// Создаём XML-документ $dom = new DOMDocument('1.0', 'utf-8'); // Текст XML-документа $strXML = '- Первый
- Второй
'; // Загружаем XML-документ из строки $dom->loadXML($strXML); // Получаем корневой элемент $root = $dom->documentElement; // Получаем список дочерних узлов $childs = $root->childNodes; for ($i = 0; $i $childs->length; $i++) $child = $childs->item($i); // Выводим информацию об узле echo $child->nodeName . ' : ' . $child->nodeValue . '
'; > ?>
Свойство nodeName содержит название элемента (в данном случае, item ), а nodeValue содержимое элемента (в данном случае, « Первый » и « Второй »).
Работа с атрибутами узлов
Давайте посмотрим, как получить значение атрибута узла, устанавить новое значение, создать и удалить атрибут:
// Создаём XML-документ $dom = new DOMDocument('1.0', 'utf-8'); // Текст XML-документа $strXML = '- Первый
- Второй
- Третий
'; // Загружаем XML-документ из строки $dom->loadXML($strXML); // Получаем корневой элемент $root = $dom->documentElement; // Первый дочерний элемент $first = $root->firstChild; // Следующий сестринский элемент $second = $first->nextSibling; // Последний дочерний элемент $third = $root->lastChild; // Выводим информацию о первом дочернем элементе echo 'Элемент ' . $first->nodeName . ', значение «' . $first->nodeValue . '», атрибут id'); // Устанавливаем атрибут id для второго дочернего элемента $second->setAttribute('id', 2); // Устанавливаем атрибут id для третьего дочернего элемента $attr = $dom->createAttribute('id'); $attr->value = 3; $third->appendChild($attr); // Удаляем атрибут id для третего дочернего элемента if ( $third->hasAttribute('id') ) $third->removeAttribute('id'); ?>
Создание XML-документа
// Создаём XML-документ $dom = new DOMDocument('1.0', 'utf-8'); // Создаём корневой элемент $root = $dom->createElement('users'); $dom->appendChild($root); $users = array( array('id' => 1, 'name' => 'Николай Петров', 'email' => 'petrov.n@mail.ru'), array('id' => 2, 'name' => 'Петр Иванов', 'email' => 'ivanov.p@mail.ru'), array('id' => 3, 'name' => 'Сергей Смирнов', 'email' => 'smirnov.s@mail.ru') ); foreach ( $users as $value ) // Создаём узел $user = $dom->createElement('user'); // Добавляем дочерний элемент для $root->appendChild($user); // Устанавливаем атрибут id для узла $user->setAttribute('id', $value['id']); // Создаём узел с текстом внутри $name = $dom->createElement('name', $value['name']); // Добавляем дочерний элемент для $user->appendChild($name); // Создаём узел $email = $dom->createElement('email'); // Создаем текстовой узел $text = $dom->createTextNode($value['email']); // Создаем текстовой узел внутри конструкции // $text = $dom->createCDATASection($value['email']); // Добавляем текстовой узел для $email->appendChild($text); // Добавляем дочерний элемент для $user->appendChild($email); > // Сохраняем полученный XML-документ в файл $dom->save('users.xml'); ?>
Содержимое файла users.xml:
Николай Петров petrov.n@mail.ru Петр Иванов ivanov.p@mail.ru Сергей Смирнов smirnov.s@mail.ru
Извлечение данных из xml-документа
Давайте теперь извлечем данные из документа users.xml обратно в массив:
// Создаём XML-документ $dom = new DOMDocument('1.0', 'utf-8'); // Загружаем XML-документ из файла $dom->load('users.xml'); // Получаем корневой элемент $root = $dom->documentElement; // Получаем дочерние элементы корневого элемента $users = $root->childNodes; // Перебираем полученные элементы foreach ($users as $user) // Получаем значение атрибута id узла $id = $user->getAttribute('id'); // Получаем дочерние элементы узла $info = $user->childNodes; // Получаем значение узла $name = $info->item(0)->nodeValue; // Получаем значение узла $email = $info->item(1)->nodeValue; // Записываем данные в массив $array[] = array('id' => $id, 'name' => $name, 'email' => $email); > print_r($array); ?>
Array ( [0] => Array ( [id] => 1 [name] => Николай Петров [email] => petrov.n@mail.ru ) [1] => Array ( [id] => 2 [name] => Петр Иванов [email] => ivanov.p@mail.ru ) [2] => Array ( [id] => 3 [name] => Сергей Смирнов [email] => smirnov.s@mail.ru ) )
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
- 1С:Предприятие (31)
- API (29)
- Bash (43)
- CLI (100)
- CMS (139)
- CSS (50)
- Frontend (75)
- HTML (66)
- JavaScript (150)
- Laravel (72)
- Linux (147)
- MySQL (76)
- PHP (125)
- React.js (66)
- SSH (27)
- Ubuntu (68)
- Web-разработка (509)
- WordPress (73)
- Yii2 (69)
- БазаДанных (95)
- Битрикс (66)
- Блог (29)
- Верстка (43)
- ИнтернетМагаз… (84)
- КаталогТоваров (87)
- Класс (30)
- Клиент (27)
- Ключ (28)
- Команда (69)
- Компонент (60)
- Конфигурация (62)
- Корзина (32)
- ЛокальнаяСеть (28)
- Модуль (34)
- Навигация (31)
- Настройка (140)
- ПанельУправле… (29)
- Плагин (33)
- Пользователь (26)
- Практика (99)
- Сервер (74)
- Событие (27)
- Теория (105)
- Установка (66)
- Файл (47)
- Форма (58)
- Фреймворк (192)
- Функция (36)
- ШаблонСайта (68)
Работаем с XML как с массивом, на PHP
Всем привет. Хочу поделиться своим опытом в парсинге XML, хочу рассказать об инструменте который мне в этом помогает.
XML ещё жив и иногда его приходиться парсить. Особенно если вы работаете со СМЭВ (привет всем ребятам для которых «ФОИВ» не пустой звук 🙂 ).
Цели у такого парсинга могут быть самые разные, от банального ответа на вопрос какое пространство имён используется в xml-документе, до необходимости получить структурированное представление для документа вцелом.
Инструмент для каждой цели будет свой. Пространство имён можно найти поиском подстроки или регулярным выражением. Что бы сделать из xml-документа структурированное представление (DTO) — придётся писать парсер.
Для работы с XML в PHP есть пара встроенных классов. Это XMLReader и SimpleXMLElement.
XMLReader
С помощью XMLReader парсинг будет выглядеть примерно так :
$reader = (new XMLReader()); $reader->XML($content); while ($reader->read()) < $this->parse($reader); >
Внутри метода parse(XMLReader $xml) будут бесконечные:
$name = $xml->name; $value = $xml->expand()->textContent; $attrVal = $xml->getAttribute('attribute'); $isElem = $xml->nodeType === XMLReader::ELEMENT;
Для небольших документов или когда нам из всего документа надо только пару элементов, это приемлемо, на больших объёмах — начинает в глазах рябить от однообразного кода, плюс совесть грызёт за оверхэд от перебора всех элементов документа.
SimpleXMLElement
Провести анализ только нужных элементов помогает SimpleXMLElement. Этот класс из XML-документа делает объект, у которого все элементы и атрибуты становятся свойствами, то есть появляется возможность работать только с определёнными элементами, а не со всеми подряд, пример:
$document = new SimpleXMLElement($content); /* имя корневого элемента */ $name = $document->getName(); /* получить произвольный элемент */ $primary = $document ->Message ->ResponseContent ->content ->MessagePrimaryContent ?? null; /* получить элементы определённого пространства имён */ $attachment = $primary ->children( 'urn://x-artefacts-fns-zpvipegr/root/750-08/4.0.1' ) ->xpath('tns:Вложения/fnst:Вложение')[0]; /* получить значение элемента */ $fileName = $attachment ->xpath('//fnst:ИмяФайла')[0] ->__toString();
Удобно, да не совсем. Если имя элемента на кириллице, то обратиться к нему через свойство не получиться, придётся использовать SimpleXMLElement::xpath(). С множественными значениями так же приходиться работать через SimpleXMLElement::xpath(). Кроме того SimpleXMLElement имеет свои особенности и некоторые вещи далеко не очевидны.
Converter
Есть способ проще. Достаточно XML-документ привести к массиву. В работе с массивами нет ни каких подводных камней. Массив из XML делается в пару строчек кода:
$xml= ccc 0000 XML; $fabric = (new NavigatorFabric())->setXml($xml); $converter = $fabric->makeConverter(); $arrayRepresentationOfXml = $converter->toArray();
Каждый XML-элемент будет представлен массивом, состоящим в свою очередь, из трёх других массивов.
- массив с индексом ‘*value’ содержит значение элемента,
- ‘*attributes’ — атрибуты элемента,
- ‘*elements’ — вложенные элементы.
/* 'b' => array ( '*value' => '0000', '*attributes' => array ( 'attr4' => '55', ), '*elements' => array ( 'c' => array ( ), ), ), */
Если элемент множественный, то есть встречается в документе несколько раз подряд, то все его вхождения будут в массиве с индексом ‘*multiple’.
$xml= first occurrence second occurrence XML; /* 'doc' => array ( 'qwe' => array ( '*multiple' => array ( 0 => array ( '*value' => 'first occurrence', ), 1 => array ( '*value' => 'second occurrence', ) ) ) ) */
XmlNavigator
Если от работы с XML-документов как с массивом, у вас в глазах рябит от квадратных скобочек, то XmlNavigator — это ваш вариант, создаётся так же в две строки кода.
/* документ */ $xml = 666 element value 0 XML; $fabric = (new NavigatorFabric())->setXml($xml); $navigator = $fabric->makeNavigator();
XmlNavigator делает, то же самое что и Converter, но предоставляет API, и с документом мы работаем как с объёктом.
Имя элемента, метод name()
/* Имя элемента */ echo $navigator->name(); /* doc */
Значение элемента, метод value()
/* Значение элемента */ echo $navigator->value(); /* 666 */
Список атрибутов, метод attribs()
/* get list of attributes */ echo var_export($navigator->attribs(), true); /* array ( 0 => 'attrib', 1 => 'option', ) */
Значение атрибута, метод get()
/* get attribute value */ echo $navigator->get('attrib'); /* a */
Список вложенных элементов, метод elements()
/* Список вложенных элементов */ echo var_export($navigator->elements(), true); /* array ( 0 => 'base', 1 => 'valuable', 2 => 'complex', ) */
Получить вложенный элемент, метод pull()
/* Получить вложенный элемент */ $nested = $navigator->pull('complex'); echo $nested->name(); /* complex */ echo var_export($nested->elements(), true); /* array ( 0 => 'a', 1 => 'different', 2 => 'b', 3 => 'c', ) */
Перебрать все вхождения множественного элемента, метод next()
/* Получить вложенный элемент вложенного элемента */ $multiple = $navigator->pull('complex')->pull('b'); /* Перебрать все вхождения множественного элемента */ foreach ($multiple->next() as $index => $instance) < echo " name()>[$index]" . " => get('val')>;"; > /* b[0] => x; b[1] => y; b[2] => z; */
Все методы класса XmlNavigator
Класс XmlNavigator реализует интерфейс IXmlNavigator.
Из названий методов очевидно их назначение. Не очевидные были рассмотрены выше.
Как установить?
composer require sbwerewolf/xml-navigator
Заключение
В работе приходиться использовать сначала SimpleXMLElement — с его помощью из всего документа получаем необходимый элемент, и уже с этим элементом работаем через XmlNavigator.
$document = new SimpleXMLElement($content); $primary = $document ->Message ->ResponseContent ->content ->MessagePrimaryContent; $attachment = $primary ->children( 'urn://x-artefacts-fns-zpvipegr/root/750-08/4.0.1' ) ->xpath('tns:Вложения')[0]; $fabric = (new NavigatorFabric())->setSimpleXmlElement($attachment); $navigator = $fabric->makeNavigator();
Желаю вам приятного использования.
Эпилог
Конечно у вас могут быть свои альтернативы для работы с XML. Предлагаю поделиться в комментариях.
Конечно, не могу сказать, что XmlNavigator поможет с любым XML — не проверял, но с обычными документами, без хитростей в схеме документа, проблем не было.
Если вам важен порядок следования элементов, то придётся пользоваться XMLReader. Потому что SimpleXMLElement приводит документ к объекту, а у объекта нет такого понятия как порядок следования элементов.