Все знают что конечно же это XMLReader, но вдруг кто-то и не знает :)
В итоге получили множество вариантов:
1. Domit XML Class
2. Simple XML
3. DOM
4. xml_parser (SAX)
5. XMLReader
Domit XML Class
Минусы: работает очень медленно, собирает весь файл в память, дерево составляется в отдельных массив ( в итоге ошибка allowed memory size неизбежна)
Плюсы: распространённость (множество CMS, включая Joomle <= 2.5 включают его в набор), простота работы (на выходе объект)
Simple XML
Минусы: работает сбором всего файла (см. domit)
Плюсы: поддержка php 4
DOM
Минусы: см. simple xml
Плюсы: всем привычный DOM J
Предыдущие 3 нам не подходят из-за работы с целым файлом, т.к. файлы у нас бывают по 20-30 Mb, и во время работы с ними некоторые блоки образуют цепочку (массив) в 100> Mb
Теперь остановимся на следующих: xml_parser и XMLReader.
Оба способа работают чтением файла построчно что подходит идеально для поставленной задачи.
Разница между xml_parser и XMLReader в том что, в первом случае вам нужно будет писать собственные функции которые будут реагировать на начало и конец тэга.
Проще говоря, xml_parser работает через 2 триггера – тэг открыт, тэг закрыт. Его не волнует что там идёт дальше, какие данные используются и т.д. Для работы вы задаёте 2 триггера указывающие на функции обработки.
В XMLReader всё проще. Во первых, это класс. Все триггеры уже заданы константами (их всего 17), чтение осуществляется функцией read() которая читает первое вхождение подходящее под заданные триггеры. Далее мы получаем объект в который ханосится тип данны (аля триггер), название тэга, его значение. Также XMLReader отлично работает с аттрибутами тэгов.
<?php
<?
Class QuizXMLReader
{
private $reader;
private $tag;
// if $ignoreDepth == 1 then will parse just first level, else parse 2th level too
/*
* Example:
* parseBlock('quiz_question'):
*
* <quiz_question>
<question_text><![CDATA[]]></question_text>
<question_image><![CDATA[]]></question_image>
<question_info>
<question_date></question_date>
<question_rating></question_rating>
</question_info>
</quiz_question>
$ignoreDepth = 1
return
array(
[question_text]
[question_image]
)
$ignoreDepth = 0
return
array(
[question_text]
[question_image]
[question_info] = array (
[question_date]
[question_rating]
)
)
*/
private function parseBlock($name, $ignoreDepth = 1) {
if ($this->reader->name == $name && $this->reader->nodeType == XMLReader::ELEMENT) {
$result = array();
while (!($this->reader->name == $name && $this->reader->nodeType == XMLReader::END_ELEMENT)) {
//echo $this->reader->name. ' - '.$this->reader->nodeType." - ".$this->reader->depth."\n";
switch ($this->reader->nodeType) {
case 1:
if ($this->reader->depth > 3 && !$ignoreDepth) {
$result[$nodeName] = (isset($result[$nodeName]) ? $result[$nodeName] : array());
while (!($this->reader->name == $nodeName && $this->reader->nodeType == XMLReader::END_ELEMENT)) {
$resultSubBlock = $this->parseBlock($this->reader->name, 1);
if (!empty($resultSubBlock))
$result[$nodeName][] = $resultSubBlock;
unset($resultSubBlock);
$this->reader->read();
}
}
$nodeName = $this->reader->name;
if ($this->reader->hasAttributes) {
$attributeCount = $this->reader->attributeCount;
for ($i = 0; $i < $attributeCount; $i++) {
$this->reader->moveToAttributeNo($i);
$result['attr'][$this->reader->name] = $this->reader->value;
}
$this->reader->moveToElement();
}
break;
case 3:
case 4:
$result[$nodeName] = $this->reader->value;
$this->reader->read();
break;
}
$this->reader->read();
}
return $result;
}
}
private function quiz_categories() {
if ($this->reader->name == 'quiz_categories') {
// while not found end tag read blocks
while (!($this->reader->name == 'quiz_categories' && $this->reader->nodeType == XMLReader::END_ELEMENT)) {
$quiz_category = $this->parseBlock('quiz_category');
//DO SOMETHING
unset($quiz_category);
$this->reader->read();
}
$this->reader->read();
}
}
private function quiz_certificates() {
if ($this->reader->name == 'quiz_certificates') {
$quiz_certificates = $this->parseBlock('quiz_certificates');
//DO SOMETHING
}
}
public function parse($filename) {
if (!$filename) return array();
$this->reader = new XMLReader();
$this->reader->open($filename);
// begin read XML
while ($this->reader->read()) {
$this->quiz_categories();
$this->quiz_certificates();
} // while
} // func
}
$xmlr = new QuizXMLReader();
$r = $xmlr->parse('export.xml');
Согласно найденным бенчмаркам с большими файлами XMLReader справляется лучше чем xml_parser.
Требования к xml_reader:
PHP 5.0 >
libxml
Требования к XMLReader:
PHP 5.1
libxml
P.S. Советы и комментарии с удовольствием выслушаю. Прошу сильно не пинать
Комментарии (8)
kamilsk
06.05.2016 12:07+1Заметка очень поверхностная. «Плюсы/минусы» -> «Плюс/Минус», т.к. ограничивается в одну фразу (в одном случае учитывается распространенность, в другом поддержка PHP 4 [в 2016 это еще плюс??]), почему бы не систематизировать?
попросили провести исследование
если это исследование, то было бы неплохо увидеть:
1. API, которое предоставляет каждое из перечисленных решений
2. немного читаемых примеров под катом для каждого из перечисленных решений, демонстрирующих удобство/не удобство работы с ним
3. возможно приведение каких-то тестов производительности (есть 1МБ файл на входе, на выходе [после полной обработки и получения нужной информации из файла] получили такой-то overhead по памяти)
4. возможно приведение типичных кейсов, где решение лучше подходит, и анти-кейсов, где лучше это решение не использовать
5. ссылки на официальную документация/страницу, чтобы «чайники» могли ознакомиться более подробно с решением
Результат: чайники так и останутся чайниками.
valemak
В XML можно, в принципе, при записи вообще не использовать переносы строк и табуляцию. Лишь бы элементы соответствовали стандарту и их вложенность была правильной. То есть теоретически файл размером в сотни мегабай будет в одну строку и такой XML будет являться корректным.
В этом случае для больших файлов спасёт не построчное, а только посимвольное чтение.
Sect0R
Абсолютно с вами согласен.
Даже в этом случае нас спасёт XMLReader, т.к. ему абсолютно плевать на табуляцию и переносы.
valemak
То есть xml_parser также отсеивается и абсолютным победителем является XMLReader.