Работа в реальном времени с большими объемами xml данных

Время на прочтение
5 мин

Количество просмотров 82K

image

На работе попросили провести исследование какими средствами лучше разбирать объёмный XML файл (более 100Mb). Предлагаю сообществу ознакомиться с результатами.

Рассмотрим основные методы работы с XML:

1. Simple XML (documentation)
2. DOM (documentation)
3. xml_parser (SAX) (documentation)
4. XMLReader (documentation)

Simple XML

Минусы: работает очень медленно, собирает весь файл в память, дерево составляется в отдельных массив.
Плюсы: простота работы, работа «из коробки» (требует библиотеки libxml которая включена практически на всех серверах)

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

$xml = simplexml_load_file("price.xml");
echo "<table border='1'>n";

foreach ($xml->xpath('/DocumentElement/price') as $producs) { ?> 
    <tr>
        <td><?php echo $producs->name; ?></td>
        <td><?php echo $producs->company; ?></td>
        <td><?php echo $producs->city; ?></td>
        <td><?php echo $producs->amount ?></td>
    </tr> 
<?
}
echo "</table>n";

DOM

Минусы: работает очень медленно, как и все предыдущие примеры собирает весь файл в память.
Плюсы: На выходе привычный DOM с которым очень легко работать.

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

$doc = new DOMDocument();
$doc->load( 'books.xml' );
 
$books = $doc->getElementsByTagName( "book" );
foreach( $books as $book )
{
$authors = $book->getElementsByTagName( "author" );
$author = $authors->item(0)->nodeValue;
 
$publishers = $book->getElementsByTagName( "publisher" );
$publisher = $publishers->item(0)->nodeValue;
 
$titles = $book->getElementsByTagName( "title" );
$title = $titles->item(0)->nodeValue;
 
echo "$title - $author - $publishern";

xml_parser и XMLReader.

Предыдущие 2 нам не подходят из-за работы с целым файлом, т.к. файлы у нас бывают по 20-30 Mb, и во время работы с ними некоторые блоки образуют цепочку (массив) в 100> Mb

Оба способа работают чтением файла построчно что подходит идеально для поставленной задачи.

Разница между xml_parser и XMLReader в том что, в первом случае вам нужно будет писать собственные функции которые будут реагировать на начало и конец тэга.

Проще говоря, xml_parser работает через 2 триггера – тэг открыт, тэг закрыт. Его не волнует что там идёт дальше, какие данные используются и т.д. Для работы вы задаёте 2 триггера указывающие на функции обработки.

Пример работы xml_parser

class Simple_Parser 
{
    var $parser;
    var $error_code;
    var $error_string;
    var $current_line;
    var $current_column;
    var $data = array();
    var $datas = array();
    
    function parse($data)
    {
        $this->parser = xml_parser_create('UTF-8');
        xml_set_object($this->parser, $this);
        xml_parser_set_option($this->parser, XML_OPTION_SKIP_WHITE, 1);
        xml_set_element_handler($this->parser, 'tag_open', 'tag_close');
        xml_set_character_data_handler($this->parser, 'cdata');
        if (!xml_parse($this->parser, $data))
        {
            $this->data = array();
            $this->error_code = xml_get_error_code($this->parser);
            $this->error_string = xml_error_string($this->error_code);
            $this->current_line = xml_get_current_line_number($this->parser);
            $this->current_column = xml_get_current_column_number($this->parser);
        }
        else
        {
            $this->data = $this->data['child'];
        }
        xml_parser_free($this->parser);
    }

    function tag_open($parser, $tag, $attribs)
    {
        $this->data['child'][$tag][] = array('data' => '', 'attribs' => $attribs, 'child' => array());
        $this->datas[] =& $this->data;
        $this->data =& $this->data['child'][$tag][count($this->data['child'][$tag])-1];
    }

    function cdata($parser, $cdata)
    {
        $this->data['data'] .= $cdata;
    }

    function tag_close($parser, $tag)
    {
        $this->data =& $this->datas[count($this->datas)-1];
        array_pop($this->datas);
    }
}

$xml_parser = new Simple_Parser;
$xml_parser->parse('<foo><bar>test</bar></foo>');

В XMLReader всё проще. Во первых, это класс. Все триггеры уже заданы константами (их всего 17), чтение осуществляется функцией read() которая читает первое вхождение подходящее под заданные триггеры. Далее мы получаем объект в который заносится тип данных (аля триггер), название тэга, его значение. Также XMLReader отлично работает с аттрибутами тэгов.

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


<?php
<?php
Class StoreXMLReader
{
	
	private $reader;
	private $tag;
	
	// if $ignoreDepth == 1 then will parse just first level, else parse 2th level too
	
	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;
		}
	}

	public function parse($filename) {
		
		if (!$filename) return array();
		
		$this->reader = new XMLReader();
		$this->reader->open($filename);
		
		// begin read XML
		while ($this->reader->read()) {
			
			if ($this->reader->name == 'store_categories') {
			// while not found end tag read blocks
			while (!($this->reader->name == 'store_categories' && $this->reader->nodeType == XMLReader::END_ELEMENT)) {
				$store_category = $this->parseBlock('store_category');
				
				/*
					Do some code
				*/
				
				$this->reader->read();
			}
			
			$this->reader->read();
		}
			
		} // while
	} // func
}

$xmlr = new StoreXMLReader();
$r = $xmlr->parse('example.xml');

Тест производительности

Код генератора example.xml

<?php
$xmlWriter = new XMLWriter();
$xmlWriter->openMemory();
$xmlWriter->startDocument('1.0', 'UTF-8');
$xmlWriter->startElement('shop');
for ($i=0; $i<=1000000; ++$i) {
    $productId = uniqid();

    $xmlWriter->startElement('product');
    $xmlWriter->writeElement('id', $productId);
    $xmlWriter->writeElement('name', 'Some product name. ID:' . $productId);
    $xmlWriter->endElement();
    // Flush XML in memory to file every 1000 iterations
    if (0 == $i%1000) {
        file_put_contents('example.xml', $xmlWriter->flush(true), FILE_APPEND);
    }
}
$xmlWriter->endElement();
// Final flush to make sure we haven't missed anything
file_put_contents('example.xml', $xmlWriter->flush(true), FILE_APPEND);

Результаты тестирования (чтение без разбора данных)

Характеристики тестовой среды

Ubuntu 16.04.1 LTS
PHP 7.0.15
Intel® Core(TM) i5-3550 CPU @ 3.30GHz, 16 Gb RAM, 256 SSD

Метод Время выполнения (19 Mb) Время выполнения (190 Mb)
Simple XML 0.46 сек 4.56 сек
DOM 0.52 сек 4.09 сек
xml_parse 0.22 сек 2.25 сек
XML Reader 0.26 сек 2.18 сек

P.S. Советы и комментарии с удовольствием выслушаю. Прошу сильно не пинать

   ДенисЧ

20.11.21 — 19:02

Есть файлик XML 320МБ

У VSCode парсер на этом объёме падает.

Украл (временно) XMLSpy — тоже ничего внятного не показывает.

А чем вы смотрите XML таких размеров?

  

Партнерская программа EFSOL Oblako

   pechkin

1 — 20.11.21 — 19:05

Altova

   Klesk

2 — 20.11.21 — 19:36

notepad++

   Ненавижу 1С

3 — 20.11.21 — 19:38

(2) ++

   ДедМорроз

4 — 20.11.21 — 19:42

А что в них смотреть?

Посмотреть как выглядит можно и far-ом

   CepeLLlka

5 — 20.11.21 — 19:45

тоже notepad++

   ДенисЧ

6 — 20.11.21 — 19:49

(1) Altova XMLSpy? Я про него написал

(2) Он умеет сворачивать большие файлы?

   ДенисЧ

7 — 20.11.21 — 19:49

(4) 50 МБ правил нужно пропустить. И ещё N объектов дальше тоже.

Нужно свернуть и смотреть конкретные записи

   b_ru

8 — 20.11.21 — 19:51

XMLPad

   ДенисЧ

9 — 20.11.21 — 19:55

(8) Так это тот же IE. Он тупо сдохнет на таком файле

   ГеннадийУО

10 — 20.11.21 — 19:59

Vscode. Быстро и наглядно.

   pechkin

11 — 20.11.21 — 20:02

(10) лучше чем просто в хроме?

   acanta

12 — 20.11.21 — 20:05

MySql не работает с XML как с базой данных ?

   ДенисЧ

13 — 20.11.21 — 20:05

(10) Ещё раз. VSCode на таком объёме дохнет. В смысле — еёный (с) парсер.

(12) А причём тут мускул?

   ГеннадийУО

14 — 20.11.21 — 20:05

(11) Да, конечно. Но и машинка должна быть не Пентиум 3, как у Дениски.

   Йохохо

15 — 20.11.21 — 20:06

https://softwarerecs.stackexchange.com/questions/77495/is-there-a-text-editor-that-can-open-big-xml-files

There are quite a few. The universal editors that can handle huge files are Emacs and XEmacs. They are both available for most platforms and I have been using those on Linux and Windows 10

   GreenLab

16 — 20.11.21 — 20:08

EmEditor. Работает с очень большими файлами

   Klesk

17 — 20.11.21 — 20:15

(16) EmEditor платный, notepad++ умеет сворачивать

   Asmody

18 — 20.11.21 — 20:16

(0) Саблайм пробовал?

   tan76

19 — 20.11.21 — 20:55

http://www.firstobject.com/dn_editor.htm

уж много лет пользуюсь, искать замену в голову не приходило ни разу…

   vde69

20 — 20.11.21 — 21:57

для больших файлов (любых) пользую https://www.hhdsoftware.com/free-hex-editor

да, в нем разметки, зато можно редактировать реально большие файлы (лично редактировал около 5 гигов)….

   Шурик71

21 — 20.11.21 — 22:02

AK XML Editor

   GANR

22 — 20.11.21 — 22:03

(0) Да ничем, ФИАС-овский 30 Гб файл домов, например, нарезаю на файлы по 10 Мб самопальным сплиттером в директорию, потом поиск текста в этих файлах, ну и где текст нашелся браузером открываю.

   GANR

23 — 20.11.21 — 22:07

Любая попытка загрузить большой файл ХМЛ в DOM падает в ошибку нехватки памяти. Только нарезка на мелкие файлы спасает.

   VladZ

24 — 20.11.21 — 22:31

(0) Любые большие тестовые файлы смотрю FAR.

   H A D G E H O G s

25 — 20.11.21 — 22:35

Akelpad

   Stim

26 — 20.11.21 — 22:40

300мб? Легко

firstobject

еще и удобный поиск

   ДенисЧ

27 — 21.11.21 — 05:18

(24) И что, фар сворачивает элементы xml?

(25) тот же вопрос

   ДенисЧ

28 — 21.11.21 — 05:19

(19) А вот это, похоже, подойдёт. Спасибо.

   Ёпрст

29 — 21.11.21 — 06:01

Если гигов 30, то ultraedit32

   ДенисЧ

30 — 21.11.21 — 07:07

(29) Дяденька, а расскажите нам про мамонтов — их правда можно было стричь и они были выше человека?

   Djelf

31 — 21.11.21 — 07:30

Поддерживаю (19), лучше firstobject ничего не видел.

   zinzillya

32 — 21.11.21 — 09:22

Вордпад.

   ДенисЧ

33 — 21.11.21 — 09:25

(32) Ваш комментарий попадает в номинацию самых тупых, поздравляю!

   zinzillya

34 — 21.11.21 — 10:04

(33) а ты попробуй

   ДенисЧ

35 — 21.11.21 — 10:09

(34) rtf, doc, odt, txt

xml не вижу.

Если и откроет, то как текст. А мне нужно не это.

   zinzillya

36 — 21.11.21 — 10:14

(35) открывает тяжёлые xml с любого компа влёт.

вопрос был «А чем вы смотрите XML таких размеров?»

   ildary

37 — 21.11.21 — 10:14

(0) XML Explorer https://xmlexplorer.github.io — быстро открывает очень большие файлы, сворачивает, есть поиск, и вроде даже портабельный

   ildary

38 — 21.11.21 — 10:16

Кстати, кто советовал Notepad++ — я конечно его очень ценю, но на больших XML он (notepad++) может надолго зависнуть — какой тогда в нём смысл? Для небольших файлов он конечно лучше всех.

   ДенисЧ

39 — 21.11.21 — 10:17

(36) Если бы мне нужно было смотреть как plain — я взял бы far.

А так » парсер на этом объёме падает.» — нормальному человеку этого должно быть достаточно

   Гость из Мариуполя

40 — 21.11.21 — 10:34

очень долго время пользуюсь XML Notepad 2007

за гиговые файлы не скажу, не пробовал, но файлы на десятки и сотни мгбайт работает норм.

из забавного в одном обсуждении по поводу локализаций этой программки: «Автор этой программы — Chris Lovett, сотрудник (по кр.мере на тот момент 2006го года) компании Microsoft (Редмонд, штат Вашингтон, США). Программа Microsoft XML Notepad не получила локализацию ни на один язык, поскольку была предназначена для своих же разработчиков (которые, конечно, знают английский). Наружу она выложена не как «программный продукт», а как «полезняшка» без поддержки: т.е. хотите — пользуйтесь, хотите — нет, но претензии оставьте при себе.»

   Гость из Мариуполя

41 — 21.11.21 — 10:37

   Garykom

42 — 21.11.21 — 10:45

(0) свой парсер напиши аля SAX

   VladZ

43 — 21.11.21 — 12:07

(27) Нет, конечно.

Когда смотришь файл — нет смысла анализировать все данные. Обычно вопрос возникает по какому-то определенному полю. FARa для этого достаточно.

   VladZ

44 — 21.11.21 — 12:07

+43 Поправка: *Когда смотришь файл большого размера — ….

   ДенисЧ

45 — 21.11.21 — 12:43

(43) Ты ошибаешься.

   mistеr

46 — 21.11.21 — 12:52

(0) Большие Far-ом. Не очень большие Notepad++.

(7) Выгрузить минимум данных для отладки лень-матушка не дает?

   ДенисЧ

47 — 21.11.21 — 12:53

(46) Причём тут лень? Ошибка возникает только при полной выгрузке

   mistеr

48 — 21.11.21 — 12:55

(47) На каких именно данных ошибка, понятно?

   Sneer

49 — 21.11.21 — 13:31

Ради интереса попробовал на файле 1,2 гб — (37) и (41) открывают нормально, больше вроде ничего из предложенного не подходит.

   Klesk

50 — 21.11.21 — 16:07

если xml ~30 Гб, по крайней мере в рамках 1С, то это уже клиника

   NorthWind

51 — 21.11.21 — 16:23

(42) а зачем писать свой, их уже написано как грязи. Но есть проблема. (0) от просмотрщика XML требуется сворачивать узлы, но когда вы парсите XML по методу SAX, вы не знаете, когда узел закончится. Он может быть и размером около всего файла. В результате разбивка на узлы и постепенное показывание файла оказывается нетривиальной задачей…

   Ёпрст

52 — 21.11.21 — 17:23

(30) дык к нему тоже есть плаггины для «удобного» просмотра xml, и сворачивает он там их.. и т.д

   Конструктор1С

53 — 21.11.21 — 18:10

Между прочим, в конфигураторе можно открыть гигантский XML

   Sneer

54 — 21.11.21 — 18:46

(53) Неа, падает

   Конструктор1С

55 — 21.11.21 — 19:27

(54) лично открывал под 4 гига файл. Правда конфигуратор оперативы сожрал over 10 Гб, несколько минут открывал, но открыл

   Sneer

56 — 21.11.21 — 19:40

(55) А, если как текст открывать, то да, открывает, но это неинтересно. Я думал он именно как xml может.

   Bigbro

57 — 22.11.21 — 06:12

несколько гигов нотепад у меня отказался открывать

поставил Liquid studio, идеальная прога. быстро красиво в нескольких вариантах просмотра.

пишут что любого размера файлы открывает хоть в терабайты.

   osa1C

58 — 22.11.21 — 06:50

(57) и сколько это стоит? по мне notepad ++ и Far  вполне хватает. нафейхуа дял этого целую студию покупать?

   RomaH

59 — 22.11.21 — 07:34

(37) поиск только XPath ?

Открыл (отформатировал) конечно быстро

   Bigbro

60 — 22.11.21 — 09:15

(58) «Я старый пират, и не знаю слов лицензионного соглашения!» (с) ))

   Kassern

61 — 22.11.21 — 09:24

(0) тотал коммандер норм открывает большие файлы, 300мб это вообще ерунда

   Kassern

62 — 22.11.21 — 09:25

(61) точнее не он сам, а встроенный Far в нем.

   ДенисЧ

63 — 22.11.21 — 09:25

(61) Да как же вы ТЗ-то читаете… (((

   Garykom

64 — 22.11.21 — 09:27

(62) far в total’е?

вы там совсем?

   Garykom

65 — 22.11.21 — 09:28

   Garykom

66 — 22.11.21 — 09:31

и да far хрень

total лучше

   mikecool

67 — 22.11.21 — 09:33

(0) firstobject xml editor

   NorthWind

68 — 22.11.21 — 09:34

(66) денешек стоит. И не вполне понятно, что там принципиально такого, за что платить, в отличие от халявного фара.

   Garykom

69 — 22.11.21 — 09:41

(68) так сложно кнопочку 1, 2 или 3 при запуске нажать?

   sikuda

70 — 22.11.21 — 09:49

(42) «свой парсер напиши аля SAX» — Пожалуйста с исходным кодом — https://github.com/sikuda/bigxmlread

   sikuda

71 — 22.11.21 — 09:52

(70) Библиотека Qt. Читает первый уровень узлов, остальные по мере раскрытия, поиск работает.

А так конечно таким программ как грязи, выбирай на свой вкус.

   ptiz

72 — 22.11.21 — 09:55

+ к  firstobject xml editor

   Pprog151713

73 — 22.11.21 — 09:59

Нотепад ++. Хорош.

   Sneer

74 — 22.11.21 — 11:56

(67) (73) (72) они же не открывают большие файлы

   ДенисЧ

75 — 22.11.21 — 11:59

(74) firstobject открыл.

   Sneer

76 — 22.11.21 — 12:09

(75) Больше гига уже не открывает — пишет out of memory

   ptiz

77 — 22.11.21 — 12:34

(76) Ну тогда bigxmlreader.

  

mikecool

78 — 22.11.21 — 12:38

(76) у меня > гига никогда не было, значит есть проблема

но firstobject достаточно шустро работает с хмл

I need to parse and process an XML feed, unfortunately the feed is about 110mb in size (and i cannot do anything about it) but to be able to parse it i need to see the structure (or if anyone has any other ideas i’d love to hear it).

But for some reason using editplus i’ve been unable to open the file. I’m on a 64bit Vista Machine with 4gb of ram (And alot of it free for use) but the file crashes every program i try to open it with.

Anyone have any ideas of how i can parse blindly (server runs linux…) ?! (PHP please..!) or a program that might be able to resolve my problem?

Cheers

UPDATE

I have managed to find the problem but it was resolved by the answer i’ve accepted. It seemed to be that the XML file wasn’t just large but all on one line which seemed to break the line limit in most programs. The chosen answer textpad++ detected this and broke the file across different lines so that it can be viewed…(might help someone in the future!)

asked Dec 1, 2009 at 20:24

Shadi Almosri's user avatar

Shadi AlmosriShadi Almosri

11.6k16 gold badges57 silver badges79 bronze badges

1

THIS is the program you want. It’s the best I’ve seen anywhere and I regularly use it for large XML documents. It’s completely free, tiny and doesn’t require an install.

Damned genius, and nobody has ever heard of it!

XML Viewer 3.1

If that link doesn’t work, scroll down on this page until you find it:

http://www.mitec.cz/Data/XML/data_downloads.xml

answered May 27, 2010 at 18:44

Chuck Le Butt's user avatar

Chuck Le ButtChuck Le Butt

47.1k61 gold badges199 silver badges286 bronze badges

I’ve never had trouble opening very large files in TextPad: http://www.textpad.com/

answered Dec 1, 2009 at 20:26

Zoran Simic's user avatar

Zoran SimicZoran Simic

10.3k6 gold badges33 silver badges35 bronze badges

1

XmlReader is a pull-parser. It maintains a cursor in the file and only reads in one element at a time. It’s a slightly different way to work with XML over DOM, but it performs well for large files.

Of course, if you just want to peek manually into the file, use less or vim for it.

answered Dec 1, 2009 at 21:01

troelskn's user avatar

troelskntroelskn

114k26 gold badges132 silver badges155 bronze badges

gVim can open extremely large files without trouble.

answered Dec 1, 2009 at 20:39

Amber's user avatar

AmberAmber

500k82 gold badges622 silver badges548 bronze badges

XMLMax will open your 100MB file in a treeview in under 5 seconds and will handle any size or structure xml file. It also has a number of options to split it up for you. YOu mentioned wanting to see the structure: if you create an index, the index file, which is a plain text utf-8 file, has a list at the end of all the unique paths in the xml file.

answered Dec 2, 2009 at 1:28

bill seacham's user avatar

bill seachambill seacham

3951 gold badge2 silver badges4 bronze badges

Since XML is just text, you could potentially split it into multiple smaller files, and examine each section individually to determine the structure of the XML inside. I’ve used these many times to split large files into manageable chinks for emailing and such (with 20MB limits, etc). I don’t know of any viewers that I can guarantee will open a 100MB+ XML file without crashing.

answered Dec 1, 2009 at 20:28

Nathan Wheeler's user avatar

Nathan WheelerNathan Wheeler

5,8762 gold badges29 silver badges47 bronze badges

2

You have several options for this:

  • Notepad++ is my personal favourite for opening large files.

  • The V file viewer is pretty handy

  • Microsoft Log Parser is pretty good too, it is desgned to allow you to use SQL-like access to large text files including XML.

    E.g. Select top 10 * from test.xml

  • You could install Cygwin then use the GNU utility ‘head’

  • You could use OPENROWSET to import the XML file into a SQL Server table

answered Dec 1, 2009 at 20:49

Jon Winstanley's user avatar

Jon WinstanleyJon Winstanley

22.8k21 gold badges73 silver badges115 bronze badges

IntelliJIDEA can open big files (read-only mode)

answered Feb 6 at 9:19

Zaur_M's user avatar

Zaur_MZaur_M

7373 gold badges11 silver badges18 bronze badges


12.11.2009
сайт автора: http://webi.ru
публикация данной статьи разрешена только со ссылкой на сайт автора статьи

В этой статье я покажу пример, как разобрать большой XML файл. Если на вашем сервере (хостинге) не запрещено увеличение времени работы скрипта, то можно разбирать XML файл весом хоть гигабайты, сам лично разбирал только файлы от озона весом 450 мегабайт.

При разборе больших XML файлов возникает две проблемы:
 1. Не хватает памяти.
 2. Не хватает выделенного времени для работы скрипта.

Вторую проблему с временем решить можно, если сервером это не запрещено.
А вот проблему с памятью решить сложно, даже если речь идет о своем сервере, то ворочать файлы по 500 мегабайт не очень просто а уж на хостинге и на VDS увеличить память просто не получится.

В PHP существует несколько встроенных вариантов обработки XML — SimpleXML, DOM, SAX.
Все эти варианты подробно описаны во многих статьях с примерами, но все примеры демонстрируют работу с полным XML документом.

Вот один из примеров, получаем объект из XML файла

<? $xml = simplexml_load_file("1.xml"); ?>

Теперь можно обрабатывать этот объект, НО…
Как видно, весь XML файл считывается в память, затем все разбирается в объект.
То есть все данные попадают в память и если выделенной памяти мало, то скрипт останавливается.

Для обработки больших файлов такой вариант не подходит, нужно читать файл построчно и обрабатывать эти данные по очереди.
При этом проверка на валидность осуществляется так же по мере обработки данных, поэтому нужно иметь возможность для отката, например удалить все внесенные в базу данные в случае не валидного XML файла, либо осуществить два прохода по файлу, сначала читать на валидность, затем читать для обработки данных.

Вот теоретический пример разбора большого XML файла.
Этот скрипт читает по одному символу из файла, собирает эти данные в блоки и отправляет в разборщик XML.
Такой подход полностью решает проблему с памятью и не вызывает нагрузки, но усугубляет проблему со временем. Как попытаться решить проблему со временем, читайте ниже.

<?function webi_xml($file)
{
####################################################
    ### функция работы с данными
    
function data ($parser, $data)
    {
        print
$data;
    }
    
############################################

    ####################################################
    ### функция открывающих тегов

function startElement($parser, $name, $attrs)
    {
        print
$name;
        
print_r($attrs);
    }
    
###############################################

    #################################################
    ## функция закрывающих тегов

function endElement($parser, $name)
    {
        print
$name;
    }
    
############################################$xml_parser = xml_parser_create();
    
xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, true);// указываем какие функции будут работать при открытии и закрытии тегов
    
xml_set_element_handler($xml_parser, "startElement", "endElement");// указываем функцию для работы с данными
    
xml_set_character_data_handler($xml_parser,"data");// открываем файл
    
$fp = fopen($file, "r");$perviy_vxod=1; // флаг для проверки первого входа в файл
    
$data="";  // сюда собираем частями данные из файла и отправляем в разборщик xml

    // цикл пока не найден конец файла

while (!feof ($fp) and $fp)
    {
$simvol = fgetc($fp); // читаем один символ из файла
        
$data.=$simvol; // добавляем этот символ к данным для отправки

        // если символ не завершающий тег, то вернемся к началу цикла и добавим еще один символ к данным, и так до тех пор, пока не будет найден закрывающий тег

if($simvol!='>') { continue;}
        
// если закрывающий тег был найден, теперь отправим эти собранные данные в обработку

        // проверяем, если это первый вход в файл, то удалим все, что находится до тега <?
        // так как иногда может встретиться мусор до начала XML (корявые редакторы, либо файл получен скриптом с другого сервера)

if($perviy_vxod) {$data=strstr($data, '<?'); $perviy_vxod=0;}// теперь кидаем данные в разборщик xml
        
if (!xml_parse($xml_parser, $data, feof($fp))) {// здесь можно обработать и получить ошибки на валидность...
            // как только встретится ошибка, разбор прекращается
            
echo "<br>XML Error: ".xml_error_string(xml_get_error_code($xml_parser));
            echo
" at line ".xml_get_current_line_number($xml_parser);
            break;
        }
// после разбора скидываем собранные данные для следующего шага цикла.
        
$data="";
    }
    
fclose($fp);
    
xml_parser_free($xml_parser);

}

webi_xml('1.xml');?>

В этом примере я все сложил в одну функцию webi_xml() и в самом низу видно ее вызов.
Сам скрипт состоит из трех основных функций:
1. Функция которая ловит открытие тега startElement()
2. Функция которая ловит закрытие тега endElement()
3. И функция получения данных data().

Предположим что содержимое файла 1.xml некий рецепт

<?xml version="1.0" encoding="windows-1251"?>
<recipe name
="хлеб" preptime="5" cooktime="180">
  <
title>Простой хлеб</title>
  <
ingredient amount="3" unit="стакан">Мука</ingredient>
  <
ingredient amount="0.25" unit="грамм">Дрожжи</ingredient>
  <
ingredient amount="1.5" unit="стакан">Тёплая вода</ingredient>
  <
ingredient amount="1" unit="чайная ложка">Соль</ingredient>
  <
instructions>
   <
step>Смешать все ингредиенты и тщательно замесить.</step>
   <
step>Закрыть тканью и оставить на один час в тёплом помещении.</step>
   <
step>Замесить ещё раз, положить на противень и поставить в духовку.</step>
   <
step>Посетить сайт webi.ru</step>
  </
instructions>
</
recipe>

Начинаем все с вызова общей функции webi_xml(‘1.xml’);
Дальше в этой функции стартует разборщик и все имена тегов переводим в верхний регистр, чтобы все теги имели одинаковый регистр.

$xml_parser = xml_parser_create();
xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, true);

Теперь указываем какие функции будут работать для отлова открытия тега, закрытия и обработки данных 

xml_set_element_handler($xml_parser, "startElement", "endElement");
xml_set_character_data_handler($xml_parser,"data");

Дальше идет открытие указанного файла, перебор файла по одному символу и каждый символ добавляется в строковую переменную пока не будет найден символ >.
Если это самое первое обращение к файлу, то попутно будет удалено все что будет лишним в начале файла, все что стоит до <?, именно с такого тега должен начинаться XML.
В первый раз строковая переменная соберет в себе строку
<?xml version=»1.0″ encoding=»windows-1251″?>
И отправит ее в разборщик
xml_parse($xml_parser, $data, feof($fp));
После обработки данных строковая переменная сбрасыватеся и снова начинается сбор данных в строку и во второй раз сформируется строка
<recipe name=»хлеб» preptime=»5″ cooktime=»180″>
В третий
<title>
в четвертый
Простой хлеб</title>

Обратите внимание, что строковая переменная всегда формируется по законченному тегу > и не обязательно посылать разбощику открытый и закрытый тег с данными например
<title>Простой хлеб</title>
Данному обработчику важно получить целый не разбитый тег, хоть один открытый, а в следущий шаг закрытый тег, или сразу получить 1000 строк файла, не важно, главное чтобы тег не разрывался, например
<tit
le>Простой хлеб
Так отправить данные обработчику нельзя, так как тег разорвался.
Вы можете придумать свою методику посылания данных в обработчик, например собирать по 1 мегабайту данных и отправлять в обработчик для повышения скорости, только следите чтобы теги всегда завершались, а данные можно разрывать
<title>Простой
хлеб</title>

Таким образом частями, как вы пожелаете можно отправить большой файл в обработчик.

Теперь рассмотрим каким образом эти данные обрабатываются и как их получить.

Начинаем с функции открывающих тегов startElement($parser, $name, $attrs)
Предположим, что обработка дошла до строки
<ingredient amount=«3» unit=«стакан»>Мука</ingredient>
Тогда внутри функции переменная $name будет равна ingredient то есть название открытого тега (до закрытия тега дело еще не дошло).
Так же в данном случае будет доступен массив атрибутов этого тега $attrs, в котором будут данные amount=«3» и unit=«стакан».

После этого пошла обработка данных открытого тега функцией data ($parser, $data)
В переменной $data будет все, что находится между открывающим и закрывающим тегом, в нашем случае это текст Мука

И завершается обработка нашей строки функцией endElement($parser, $name)
Это название закрытого тега, в нашем случае $name будет равна ingredient

А после этого опять пошло все по кругу.

Приведенный пример лишь демонстрирует принцип обработки XML, но для реального применения его нужно доработать.
Обычно, разбирать большой XML приходится для занесения данных в базу и для правильной обработки данных нужно знать к какому открытому тегу относятся данные, какой уровень вложения тега и какие теги открыты по иерархии выше. Обладая такой информацией можно без проблем правильно обработать файл.
Для этого нужно ввести несколько глобальных переменных, которые будут собирать информацию об открытых тегах, вложенности и данные.
Привожу пример, который можно использовать

<?function webi_xml($file)
{
    global
$webi_depth;       // счетчик, для отслеживания глубины вложенности
    
$webi_depth = 0;
    global
$webi_tag_open;    // будет содержать массив открытых в данный момент тегов
    
$webi_tag_open= array();
    global
$webi_data_temp;   // этот массив будет содержать данные одного тега

    ####################################################
    ### функция работы с данными

function data ($parser, $data)
    {
        global
$webi_depth;
        global
$webi_tag_open;
        global
$webi_data_temp;
        
// добавляем данные в массив с указанием вложенности и открытого в данный момент тега
        
$webi_data_temp[$webi_depth][$webi_tag_open[$webi_depth]]['data'].=$data;
    }
    
############################################

    ####################################################
    ### функция открывающих тегов

function startElement($parser, $name, $attrs)
    {
        global
$webi_depth;
        global
$webi_tag_open;
        global
$webi_data_temp;// если уровень вложенности уже не нулевой, значит один тег уже открыт
        // и данные из него уже в массиве, можно их обработать
        
if ($webi_depth)
        {
            
// здесь начинается обработка данных, например добаление в базу, сохранение в файл и т.д.
            // $webi_tag_open содержит цепочку открытых тегов по уровню вложенности
            // например $webi_tag_open[$webi_depth] содержит название открытого тега чья информация сейчас обрабатывается
            // $webi_depth уровень вложенности тега
            // $webi_data_temp[$webi_depth][$webi_tag_open[$webi_depth]]['attrs'] массив атрибутов тега
            // $webi_data_temp[$webi_depth][$webi_tag_open[$webi_depth]]['data'] данные тега
print 'данные '.$webi_tag_open[$webi_depth].'--'.($webi_data_temp[$webi_depth][$webi_tag_open[$webi_depth]]['data']).'<br>';
            
print_r($webi_data_temp[$webi_depth][$webi_tag_open[$webi_depth]]['attrs']);
            print
'<br>';
            
print_r($webi_tag_open); // массив открытых тегов
            
print '<hr>';// после обработки данных удаляем их для освобождения памяти
            
unset($GLOBALS['webi_data_temp'][$webi_depth]);
        }
// теперь пошло открытие следующего тега и дальше обработка его произойдет на следующем шаге
        
$webi_depth++; // увеличиваем вложенность$webi_tag_open[$webi_depth]=$name; // добавляем открытый тег в массив информаци
        
$webi_data_temp[$webi_depth][$name]['attrs']=$attrs; // теперь добавляем атрибуты тега}
    
###############################################

    #################################################
    ## функция закрывающих тегов

function endElement($parser, $name) {
        global
$webi_depth;
        global
$webi_tag_open;
        global
$webi_data_temp;// здесь начинается обработка данных, например добаление в базу, сохранение в файл и т.д.
        // $webi_tag_open содержит цепочку открытых тегов по уровню вложенности
        // например $webi_tag_open[$webi_depth] содержит название открытого тега чья информация сейчас обрабатывается
        // $webi_depth уровень вложенности тега
        // $webi_data_temp[$webi_depth][$webi_tag_open[$webi_depth]]['attrs'] массив атрибутов тега
        // $webi_data_temp[$webi_depth][$webi_tag_open[$webi_depth]]['data'] данные тега
print 'данные '.$webi_tag_open[$webi_depth].'--'.($webi_data_temp[$webi_depth][$webi_tag_open[$webi_depth]]['data']).'<br>';
        
print_r($webi_data_temp[$webi_depth][$webi_tag_open[$webi_depth]]['attrs']);
        print
'<br>';
        
print_r($webi_tag_open);
        print
'<hr>';

        unset(

$GLOBALS['webi_data_temp']); // после обработки данных удаляем массив с данными целиком, так как произошло закрытие тега
        
unset($GLOBALS['webi_tag_open'][$webi_depth]); // удаляем информацию об этом открытом теге... так как он закрылся$webi_depth--; // уменьшаем вложенность
    
}
    
############################################$xml_parser = xml_parser_create();
    
xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, true);// указываем какие функции будут работать при открытии и закрытии тегов
    
xml_set_element_handler($xml_parser, "startElement", "endElement");// указываем функцию для работы с данными
    
xml_set_character_data_handler($xml_parser,"data");// открываем файл
    
$fp = fopen($file, "r");$perviy_vxod=1; // флаг для проверки первого входа в файл
    
$data="";  // сюда собираем частями данные из файла и отправляем в разборщик xml

    // цикл пока не найден конец файла

while (!feof ($fp) and $fp)
    {
        
$simvol = fgetc($fp); // читаем один символ из файла
        
$data.=$simvol; // добавляем этот символ к данным для отправки

        // если символ не завершающий тег, то вернемся к началу цикла и добавим еще один символ к данным, и так до тех пор, пока не будет найден закрывающий тег

if($simvol!='>') { continue;}
        
// если закрывающий тег был найден, теперь отправим эти собранные данные в обработку

        // проверяем, если это первый вход в файл, то удалим все, что находится до тега <?
        // так как иногда может встретиться мусор до начала XML (корявые редакторы, либо файл получен скриптом с другого сервера)

if($perviy_vxod) {$data=strstr($data, '<?'); $perviy_vxod=0;}// теперь кидаем данные в разборщик xml
        
if (!xml_parse($xml_parser, $data, feof($fp))) {// здесь можно обработать и получить ошибки на валидность...
            // как только встретится ошибка, разбор прекращается
            
echo "<br>XML Error: ".xml_error_string(xml_get_error_code($xml_parser));
            echo
" at line ".xml_get_current_line_number($xml_parser);
            break;
        }
// после разбора скидываем собранные данные для следующего шага цикла.
        
$data="";
    }
    
fclose($fp);
    
xml_parser_free($xml_parser);
    
// удаление глобальных переменных
    
unset($GLOBALS['webi_depth']);  
    unset(
$GLOBALS['webi_tag_open']);
    unset(
$GLOBALS['webi_data_temp']);

}

webi_xml('1.xml');?>

Весь пример сопроводил комментариями, теперь тестируйте и экспериментируйте.
Обратите внимание, в функции работы с данными в массив данные не просто вставляются, а именно добавляются с помощью «.=» так как данные могут поступать не в целом виде и если сделать просто присвоение, то время от времени вы будете получать данные кусками.

Ну вот и все, теперь при обработке файла любого размера памяти хватит, а вот время работы скрипта увеличить можно несколькими способами.
В начало скрипта вставьте функцию
set_time_limit(6000);
или
ini_set
(«max_execution_time», «6000»);

Либо добавьте в файл .htaccess текст 
php_value max_execution_time 6000

Данные примеры увеличат время работы скрипта до 6000 секунд.
Увеличить время подобным образом можно только в выключенном безопасном режиме.

Если у вас есть доступ к редактированию php.ini можете увеличить время с помощью
max_execution_time = 6000

Например на хостинге мастерхост на момент написания статьи увеличение времени скрипта запрещено, не смотря на выключенный безопасный режим, но если вы профи, вы можете сделать свою сборку php на мастерхосте, но об этом не в этой статье.


Комментарии


RSS комментарии

13.11.2009 developer

проверил.
взял файл озона 450 мб с книгами.
и обработал его этим примером.
вставлял в базу данные минут 30, долго, но в итоге вставил…


28.02.2010 leksus.info

у меня хмл-файл на 25 гигов, вот думаю, сколько это времени займёт :)
Я так понимаю, если указать считываемые кусочки по 1мб, дело пойдёт быстрее?


01.03.2010 Админ

25 гигов это конечно круто.
если честно, не знаю что тут может ускорить процесс, надо экспериментировать.
разобрать конечно можно такой файл, но сколько часов это будет обрабатываться…


01.05.2010 Alex-Dnepr

PHP для разбора больших баз не очень-то подходит, причем — всего лишь в силу того, что сам процесс занимает очень много времени (450MB = 30мин. = 1800сек.), а, очень часто, настройки веб-сервера просто не позволяют его осуществить полностью из-за установленного лимита времени на работу php-скрипта (например стандартные для Апача 30сек.).
Такие вещи реально осуществлять на своем выделенном сервере (виртуальном либо реальном), так как появляется возможность изменения данного лимита времени исходя из своих потребностей.
Поэтому, в этом плане, парсер на базе perl — один из лучших вариантов.

P.S. Пробовал изменять размер куска на считывание — почти нулевое изменение производительности.


01.05.2010 админ

Alex-Dnepr, конечно, для подобных вещей perl более предпочтителен.
Скорость выполнение подобного разбора в perl должна быть быстрее, но не сильно уж быстрее.
Да и потом у perl по времени тоже существуют ограничения, такие же как в php.


02.05.2010 Alex-Dnepr

Как ни крути, но sax-парсер — медленный.
Для ускорения разбора больших баз можно попробовать использовать парсер, построенный, например на базе strpos() и substr().


02.05.2010 админ

Alex-Dnepr, на базе strpos() и substr() тоже можно, но будет ли быстрее… мне кажется, что не будет быстрее.
проверки и поиск символов по тексту создаст тоже не малую нагрузку.
Но это все уже тестировать надо.
Если прирост в скорости будет значительный, тогда наверное стоит обратить внимание на обработку строковыми функциями.


10.05.2010 Alex-Dnepr

Здравствйте, Admin.

Какая средняя скорость разбора больших XML файлов sax-парсером была зафиксирована Вами (размер файла/время обработки)?


11.05.2010 админ

Да вот тут я не отвечу. Я не проводил тестов на время.
Была задача сделать разбор файлов до 50 МБ на хостинге.
Естественно обычным способом разобрать не получалось, не хватало памяти.
А вот вариант описанный в этой статье подошел и укладывался в ресурсы хостинга, хватало и памяти и времени.
А вот сколько по времени длились разборы, я вот тут не засекал.


14.05.2010 Alex-Dnepr

Попробовал парсить XML-базу с помощью строковых функций — результат достаточно преемлемый.
По факту, разбор XML базы размером 500 Мб (только чтение — без записи в БД) с помощью SAX занял 460 сек, а строковыми функциями — 260 сек.


14.05.2010 админ

результат конечно впечатляет, почти в два раза быстрее.
но все ли вы учли? например разбор атрибутов предусмотрели?
может покажите этот пример(на почту), я его обработаю, потестирую и присоединю к этой статье.


15.05.2010 Alex-Dnepr

Как уже указывал — такие результаты были достигнуты в режиме <u>чтения</u> XML-базы — без учета затрат времени на работу с СУБД (например MySQL).

По факту, необходимость записи в базу данных — практически нивелирует относительную разницу в результатах работы парсеров.

П.С. Выделение атрибутов осуществляется и не ничем не отличается от процесса выделения значений, заключенных в теги.
По факту — это тот же sax, разница в том, что выделение тегов и атрибутов осуществляется с помощью строковых функций.


15.05.2010 админ

Про атрибуты я упомянул потому, что выделение атрибутов такое же затратное действие как и выделение значений тегов. И если опустить атрибуты, то скорость увеличится, поэтому я и спросил про атрибуты.
А зачем учитывать время работы с базой?
Тут было интересно узнать скорость самого разбора. Раз он быстрее в два раза, значит более предпочтителен.
Ведь может понадобиться не вставлять данные в базу, а лишь найти несколько значений в большом XML


26.07.2010 Виталий

А можно после считывания из xml сразу удалять строку?
т.е. считал, закинул эти данные в базу(например), удалил эту строку и так далее… а то у меня строк 400 считывает и потом 504ю ошибку выдает, приходится удалять уже добавленные строки…


27.07.2010 Никита

Виталий, просто так удалить строку из файла нельзя.
Для этого надо прочитать все оставшиеся строки и записать их в новый файл.


28.07.2010 Виталий

А если крупный файл, то оперативку сильно забивать будет…?


28.07.2010 Никита

Виталий, ну у тебя же проблема с какой-то ошибкой после прочтения 400 строк.
Скорее всего просто не хватает времени.
А для того, чтобы выкинуть обработанные строки из файла, нужно оставшиеся строки читать и складывать в новый файл и тут опять же возникнет твоя ошибка.
Если прочитать всю оставшуюся часть файла и сохранить в новый файл, то тут конечно не хватит оперативки, если файл большой.


30.07.2010 Виталий

В общем ничего сочинять не нужно… надо просто запускать такие вещи через крон, и все потихоньку за раз хоть за час хоть за два добавится…


01.04.2011 m11

А можно пример как в базу вставить полученные данные?


12.04.2011 Алексей

Может быть что-то изменилось в PHP’шном парсере… Но факт тот, что сейчас, судя по всему, пляски с поиском начала и конца тегов не нужны…

Читай хоть по одному символу — оно запоминает и парсит как надо:

while ($data = fread($fp, 1)) {
if (!xml_parse($xml_parser, $data, feof($fp))) {
* * * *
}
}


Добавить свой комментарий

image

На работе попросили провести исследование какими средствами лучше разбирать объёмный XML файл (более 100Mb). Предлагаю сообществу ознакомиться с результатами.

Рассмотрим основные методы работы с XML:

1. Simple XML (documentation)
2. DOM (documentation)
3. xml_parser (SAX) (documentation)
4. XMLReader (documentation)

Simple XML

Минусы: работает очень медленно, собирает весь файл в память, дерево составляется в отдельных массив.
Плюсы: простота работы, работа «из коробки» (требует библиотеки libxml которая включена практически на всех серверах)

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

$xml = simplexml_load_file("price.xml");
echo "<table border='1'>n";

foreach ($xml->xpath('/DocumentElement/price') as $producs) { ?> 
    <tr>
        <td><?php echo $producs->name; ?></td>
        <td><?php echo $producs->company; ?></td>
        <td><?php echo $producs->city; ?></td>
        <td><?php echo $producs->amount ?></td>
    </tr> 
<?
}
echo "</table>n";

DOM

Минусы: работает очень медленно, как и все предыдущие примеры собирает весь файл в память.
Плюсы: На выходе привычный DOM с которым очень легко работать.

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

$doc = new DOMDocument();
$doc->load( 'books.xml' );
 
$books = $doc->getElementsByTagName( "book" );
foreach( $books as $book )
{
$authors = $book->getElementsByTagName( "author" );
$author = $authors->item(0)->nodeValue;
 
$publishers = $book->getElementsByTagName( "publisher" );
$publisher = $publishers->item(0)->nodeValue;
 
$titles = $book->getElementsByTagName( "title" );
$title = $titles->item(0)->nodeValue;
 
echo "$title - $author - $publishern";

xml_parser и XMLReader.

Предыдущие 2 нам не подходят из-за работы с целым файлом, т.к. файлы у нас бывают по 20-30 Mb, и во время работы с ними некоторые блоки образуют цепочку (массив) в 100> Mb

Оба способа работают чтением файла построчно что подходит идеально для поставленной задачи.

Разница между xml_parser и XMLReader в том что, в первом случае вам нужно будет писать собственные функции которые будут реагировать на начало и конец тэга.

Проще говоря, xml_parser работает через 2 триггера – тэг открыт, тэг закрыт. Его не волнует что там идёт дальше, какие данные используются и т.д. Для работы вы задаёте 2 триггера указывающие на функции обработки.

Пример работы xml_parser

class Simple_Parser 
{
    var $parser;
    var $error_code;
    var $error_string;
    var $current_line;
    var $current_column;
    var $data = array();
    var $datas = array();
    
    function parse($data)
    {
        $this->parser = xml_parser_create('UTF-8');
        xml_set_object($this->parser, $this);
        xml_parser_set_option($this->parser, XML_OPTION_SKIP_WHITE, 1);
        xml_set_element_handler($this->parser, 'tag_open', 'tag_close');
        xml_set_character_data_handler($this->parser, 'cdata');
        if (!xml_parse($this->parser, $data))
        {
            $this->data = array();
            $this->error_code = xml_get_error_code($this->parser);
            $this->error_string = xml_error_string($this->error_code);
            $this->current_line = xml_get_current_line_number($this->parser);
            $this->current_column = xml_get_current_column_number($this->parser);
        }
        else
        {
            $this->data = $this->data['child'];
        }
        xml_parser_free($this->parser);
    }

    function tag_open($parser, $tag, $attribs)
    {
        $this->data['child'][$tag][] = array('data' => '', 'attribs' => $attribs, 'child' => array());
        $this->datas[] =& $this->data;
        $this->data =& $this->data['child'][$tag][count($this->data['child'][$tag])-1];
    }

    function cdata($parser, $cdata)
    {
        $this->data['data'] .= $cdata;
    }

    function tag_close($parser, $tag)
    {
        $this->data =& $this->datas[count($this->datas)-1];
        array_pop($this->datas);
    }
}

$xml_parser = new Simple_Parser;
$xml_parser->parse('<foo><bar>test</bar></foo>');

В XMLReader всё проще. Во первых, это класс. Все триггеры уже заданы константами (их всего 17), чтение осуществляется функцией read() которая читает первое вхождение подходящее под заданные триггеры. Далее мы получаем объект в который заносится тип данных (аля триггер), название тэга, его значение. Также XMLReader отлично работает с аттрибутами тэгов.

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


<?php
<?php
Class StoreXMLReader
{
	
	private $reader;
	private $tag;
	
	// if $ignoreDepth == 1 then will parse just first level, else parse 2th level too
	
	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;
		}
	}

	public function parse($filename) {
		
		if (!$filename) return array();
		
		$this->reader = new XMLReader();
		$this->reader->open($filename);
		
		// begin read XML
		while ($this->reader->read()) {
			
			if ($this->reader->name == 'store_categories') {
			// while not found end tag read blocks
			while (!($this->reader->name == 'store_categories' && $this->reader->nodeType == XMLReader::END_ELEMENT)) {
				$store_category = $this->parseBlock('store_category');
				
				/*
					Do some code
				*/
				
				$this->reader->read();
			}
			
			$this->reader->read();
		}
			
		} // while
	} // func
}

$xmlr = new StoreXMLReader();
$r = $xmlr->parse('example.xml');

Тест производительности

Код генератора example.xml

<?php
$xmlWriter = new XMLWriter();
$xmlWriter->openMemory();
$xmlWriter->startDocument('1.0', 'UTF-8');
$xmlWriter->startElement('shop');
for ($i=0; $i<=1000000; ++$i) {
    $productId = uniqid();

    $xmlWriter->startElement('product');
    $xmlWriter->writeElement('id', $productId);
    $xmlWriter->writeElement('name', 'Some product name. ID:' . $productId);
    $xmlWriter->endElement();
    // Flush XML in memory to file every 1000 iterations
    if (0 == $i%1000) {
        file_put_contents('example.xml', $xmlWriter->flush(true), FILE_APPEND);
    }
}
$xmlWriter->endElement();
// Final flush to make sure we haven't missed anything
file_put_contents('example.xml', $xmlWriter->flush(true), FILE_APPEND);

Результаты тестирования (чтение без разбора данных)

Характеристики тестовой среды

Ubuntu 16.04.1 LTS
PHP 7.0.15
Intel® Core(TM) i5-3550 CPU @ 3.30GHz, 16 Gb RAM, 256 SSD

Метод Время выполнения (19 Mb) Время выполнения (190 Mb)
Simple XML 0.46 сек 4.56 сек
DOM 0.52 сек 4.09 сек
xml_parse 0.22 сек 2.25 сек
XML Reader 0.26 сек 2.18 сек

P.S. Советы и комментарии с удовольствием выслушаю. Прошу сильно не пинать

Существуют ли программы способные отображать большие (гигабайт и больше) XML-файлы? Желательно конечно с возможностью применения XSLT

задан 1 фев 2011 в 15:17

groaner's user avatar

3

Боюсь только утилиты вроде less могут открывать такие файлы для просмотра..
Что же до преобразования, то попробуйте saxon с какими-нибудь жуткими параметрами запуска, чтобы памяти хватило.

ответ дан 1 фев 2011 в 15:33

cy6erGn0m's user avatar

cy6erGn0mcy6erGn0m

19.7k1 золотой знак31 серебряный знак38 бронзовых знаков

Парсить:

http://sax.sourceforge.net/

Посмотреть можно, например через cat:

cat filename |more

Под виндами я полагаю, что через far

ответ дан 1 фев 2011 в 15:31

Alex Silaev's user avatar

Alex SilaevAlex Silaev

4,0421 золотой знак16 серебряных знаков26 бронзовых знаков

Notepad2

ответ дан 1 фев 2011 в 18:00

Сергей's user avatar

СергейСергей

3,3223 золотых знака28 серебряных знаков56 бронзовых знаков

За последние 24 часа нас посетили 13748 программистов и 949 роботов. Сейчас ищут 476 программистов …

Страница 1 из 2

  1. Мне нужно спарсить большие XML файлы размером от ~ 500 до ~ 1700 мб.

    Собственно я и юзаю XMLReader

    1.     include_once ‘inc/Misc.php’;
    2.     include_once ‘inc/Database.php’;
    3.     $files = array(‘xml/large_file.xml’);
    4.     foreach($files as $file) {
    5.         echo ‘Filesize: ‘.convert(filesize($file)).«n«;
    6.         echo ‘Start parsing…’.«n«;
    7.         $reader = new XMLReader();
    8.         while ($reader->read()) {
    9.             switch ($reader->nodeType) {
    10.                 case (XMLREADER::ELEMENT):
    11.                 if ($reader->localName == «element-name») {                    
    12.                         $dom = new DomDocument();
    13.                         $n = $dom->importNode($reader->expand(),true);
    14.                         $tess->file_big->insert($sxe);          
    15.                         echo «Insert done! «; benchmark();                     

    все нормально сначала… парсится потихоньку файл и вставляются нужные мне данные, но постепенно растет потребление памяти и пока не заканчиваются ресурсы только парсится. То есть я взял файл на 400 метров и пока его парсило то израсходовало 2000 метров оперативки и все ресурсы кончились и скрипт остановился.

    Как быть с большими файлами ? ~ 500 до ~ 1700 мб. поможет ли тут XML Parser ? да и как им нормально парсить то что мне надо как в моем скрипте выше, примеров толковых нет… Еще варианты может есть ?


  2. Gromo

    Gromo
    Активный пользователь

    С нами с:
    24 май 2010
    Сообщения:
    2.786
    Симпатии:
    2
    Адрес:
    Ташкент

    Awilum
    решение «в лоб» — загонять файл в базу для более удобной работы, построчно считывая файл в память и обрабатывая каждую строку отдельно — грубо говоря, писать собственный парсер для большого файла.

    Держать в памяти 2 Гб файл XML в виде объектной структуры — это нереально.


  3. Psih

    Psih
    Активный пользователь

    Команда форума
    Модератор

    Поскольку сам по себе парсинг работает, то явно всё в память не грузится. Насколько я знаю XMLReader потоковый.

    Проблема в том, что в таких случаях надо забочитстя об очисте памяти, т.е. делать unset переменным, которые больше не нужны.
    Ну и желательно использовать PHP 5.3+ в включённым сборщиком мусора.

  4. дело в том что у меня могут повторяться названия элементов и количество как оказалось тоже вот например

    1. <party-name>Guess? IP Holder L.P.</party-name>
    2. <postcode>90021</postcode>

    и сработает только один флажок state


  5. Psih

    Psih
    Активный пользователь

    Команда форума
    Модератор

    С SAX парсером не подскажу — я с ним работал, но давненько. А вот XMLReader скрипт вполне есть куда улучшить.

    1. // Try to enable garbage collection on 5.3+
    2. include_once ‘inc/Misc.php’;
    3. include_once ‘inc/Database.php’;
    4. $files = array(‘xml/large_file.xml’);
    5. foreach($files as $file) {
    6.     echo ‘Filesize: ‘.convert(filesize($file)).«n«;
    7.     echo ‘Start parsing…’.«n«;
    8.     $reader = new XMLReader();
    9.     while ($reader->read()) {
    10.         switch ($reader->nodeType) {
    11.             case (XMLREADER::ELEMENT):
    12.                 if ($reader->localName == «element-name») {
    13.                     $dom = new DomDocument();
    14.                     $n = $dom->importNode($reader->expand(),true);
    15.                     * Database insert? What is $tess and what’s inside it?
    16.                     $tess->file_big->insert($sxe);
    17.                     echo «Insert done! «; benchmark();
    18.     // Delete the object to free memory

  6. да это mongoDB почему то течет. уже сделал и так, но все равно течет с каждой вставкой

    1.              case (XMLREADER::ELEMENT):
    2.                  if ($reader->localName == «case-file») {
    3.                      $dom = new DomDocument();
    4.                      $n = $dom->importNode($reader->expand(),true);
    5.                      $sxe = simplexml_import_dom($n);
    6.                      * Database insert? What is $tess and what’s inside it?
    7.                      //$tess->file_big->insert($sxe);
    8.                      $tess = $mongo->selectDB(‘tess’);         
    9.                      $tess->file_big->insert($sxe);
    10.                      echo «Insert done! «; benchmark();
    11.                      unset($n, $dom, $sxe, $mongo, $tess);                    
  7. хотя в варианте с САКСОМ такой утечки не наблюдаю. а тут прям с каждой вставкой оперативка отжирается.

    без этого

    $mongo = new Mongo();
    $tess = $mongo->selectDB(‘tess’);
    $tess->file_big->insert($sxe);

    норм а с этой вставкой сильно жрутся ресурсы и не очищаются.


  8. Psih

    Psih
    Активный пользователь

    Команда форума
    Модератор

    Awilum
    А вы забываете закрыть соединение с MongoDB — ресурс то кушается с каждым новым объектом. Бережнее надо, бережнее.

    1.              case (XMLREADER::ELEMENT):
    2.                  if ($reader->localName == «case-file») {
    3.                      $dom = new DomDocument();
    4.                      $n = $dom->importNode($reader->expand(),true);
    5.                      $sxe = simplexml_import_dom($n);
    6.                      * Database insert? What is $tess and what’s inside it?
    7.                      //$tess->file_big->insert($sxe);
    8.                      $tess = $mongo->selectDB(‘tess’);         
    9.                      $tess->file_big->insert($sxe);
    10.                      echo «Insert done! «; benchmark();
    11.                      unset($n, $dom, $sxe, $mongo, $tess);                    

    потребление памяти все растет с каждой вставкой. тут уже и unset и close есть.


  9. Psih

    Psih
    Активный пользователь

    Команда форума
    Модератор

    Awilum
    Могу покопаться на досуге, но нужен приличный XML файл и скрипт, которым вы его парсите. Только тестировать буду на PHP 5.3 а не 5.2 (под 7-ку нету нормальных толковых версий 5.2). У 5.2 есть проблемы с памятью и их решить нереально иногда.


  10. Psih

    Psih
    Активный пользователь

    Команда форума
    Модератор

    Вы мне, пожайлуста, перешлите всё же все файлы скрипта — Misc.php, Database.php — может проблема утечек вообще где-то в них. Тут нужно всё пересматривать, а не отдельные части :)

    Эх, надо будет ещё MongoDB поставить себе…

  11. утром тестил в холостую без вставки ничего не жрало, а со вставкой жрало. Щас чего то пофиг как: и так и так жрет зараза (что в холостую что со вставкой) жесть какая та.

  12. Database.php и Misc.php можно не брать во внимание оно и без них жрет

    Database.php

    1.     $tess = $mongo->selectDB(‘tess’);          

    Misc.php

    1.       * Convert bytes in ‘kb’,’mb’,’gb’,’tb’,’pb’
    2.       * @param integer $size Data to convert
    3.     function convert($size) {
    4.         $unit=array(‘b’,’kb’,’mb’,’gb’,’tb’,’pb’);
    5.         return @round($size/pow(1024,($i=floor(log($size,1024)))),2).’ ‘.$unit[$i];
    6.      * @param boolean $render Displays the result of the function in the browser or not
    7.     function getMemoryUsage($render=true) {
    8.         if (function_exists(‘memory_get_usage’)) {
    9.             $memory_usage = memory_get_usage();
    10.         } else if (substr(PHP_OS,0,3) == ‘WIN’) {
    11.             // Windows 2000 workaround
    12.             exec(‘pslist ‘ . getmypid() , $output);
    13.             $memory_usage = trim(substr($output[8],38,10));
    14.             printf(‘Memory usage: ‘.convert($memory_usage));
    15.      * @global integer $start_time Start time value
    16.      * @param boolean $render Displays the result of the function in the browser or not
    17.     function getElapsedTime($render=true) {
    18.         $result_time = microtime(true) — $start_time;
    19.         if($render) printf(«Elapsed time %.3f seconds»,$result_time); else return sprintf(«%.3f», $result_time);
    20.         getMemoryUsage(); echo » — «; getElapsedTime(); echo «n»;

    1.  // Try to enable garbage collection on 5.3+
    2.  if (function_exists(‘gc_enable’) && !gc_enabled()) {
    3.  $files = array(‘xml/apc101231-42.xml’);
    4.  foreach($files as $file) {
    5.      echo ‘Filename: ‘.basename($file).»n»;
    6.      //echo ‘Filesize: ‘.convert(filesize($file)).»n»;
    7.      echo ‘Start parsing…’.»n»;
    8.      $reader = new XMLReader();
    9.      while ($reader->read()) {
    10.          switch ($reader->nodeType) {          
    11.              case (XMLREADER::ELEMENT):
    12.                  if ($reader->localName == «case-file») {
    13.                      $dom = new DomDocument();
    14.                      $n = $dom->importNode($reader->expand(),true);
    15.                      $sxe = simplexml_import_dom($n);
    16.                      $tess = $mongo->selectDB(‘tess’);          
    17.                      $tess->file_big->insert($sxe);
    18.                      //unset($n, $dom, $sxe, $mongo, $tess);                    
    19.      // Delete the object to free memory
  13. $reader->open($file,’LIBXML_PARSEHUGE’);

    потребление памяти растет.


  14. Psih

    Psih
    Активный пользователь

    Команда форума
    Модератор

    Awilum
    Эээ, ну вы хоть читайте мануал и параметры для функций то…

    $reader->open($file, null, LIBXML_PARSEHUGE);

    либо как советуют в комментах к XMLReader::read():

    $xmlreader->open($uri, null, 1<<19);

  15. $reader->open($file, null, ‘LIBXML_PARSEHUGE’);
    Warning: XMLReader::eek:pen() expects parameter 3 to be long

    $reader->open($file, null, LIBXML_PARSEHUGE);
    Notice: Use of undefined constant LIBXML_PARSEHUGE — assumed ‘LIBXML_PARSEHUGE’

    1. while ($reader->read()) {

    а это место течь не может ? потому что больше некуда смотреть, так как что в холостую что нет одинаково.

    прологировал

    1.          while ($reader->read()) {   
    2.              switch ($reader->nodeType) {          
    3.                  case (XMLREADER::ELEMENT):                  
    4.                      if ($reader->localName == «case-file») {
    5.                          logAdd(‘case-file found’);
    6.                          $dom = new DomDocument();
    7.                          $n = $dom->importNode($reader->expand(),true);
    8.                          $sxe = simplexml_import_dom($n);
    9.                          logAdd(‘case-file in $sxe’);
    10.                          $mongo->$db->$collection->insert($sxe);                           
    11.                          logAdd(‘Clear the memory’);

    не вижу ни каких проблем, но процесс расходует память.

  16. я уже и так сделал. но все равно у процесса растет память! жесть. то ли действительно этот ридер копит весь файл и по этому потребление растет.

    1.     $start_time = microtime(true);
    2.      // Try to enable garbage collection on 5.3+
    3.     if (function_exists(‘gc_enable’) && !gc_enabled()) {       
    4.     $files = array(/*’xml/apc101231-42.xml’,*/
    5.      foreach($files as $file) {
    6.          echo ‘Filename: ‘.basename($file).»n»;
    7.          echo ‘Start parsing…’.»n»;
    8.          $reader = new XMLReader();
    9.          if(!($fp = fopen($file, «r»)));
    10.          while ($data = fread($fp, 4096)) {
    11.          //while ($reader->read()) {     
    12.              switch ($reader->nodeType) {          
    13.                  case (XMLREADER::ELEMENT):                  
    14.                      if ($reader->localName == «case-file») {
    15.                          logAdd(‘case-file found’);
    16.                          $dom = new DomDocument();
    17.                          $n = $dom->importNode($reader->expand(),true);
    18.                          $sxe = simplexml_import_dom($n);
    19.                          logAdd(‘case-file in $sxe’);
    20.                          //$mongo->$db->$collection->insert($sxe);                         
    21.                          logAdd(‘Clear the memory’);
    22.          // Delete the object to free memory

  17. Psih

    Psih
    Активный пользователь

    Команда форума
    Модератор

    Awilum
    Сегодня будет вечером время, попробую разобраться. Но если память так и кушаться будет — надо будет смотреть базу багов PHP и репортить, если такового нету.

  18. Скачал исходник php что бы посмотреть как же все такие устроен этот ридер.

    $Id: php_xmlreader.c 306939 2011-01-01 02:19:59Z felipe $

    1. #include «ext/standard/info.h»
    2. #include «php_xmlreader.h»
    3. #include «ext/dom/xml_common.h»
    4. zend_class_entry *xmlreader_class_entry;
    5. static zend_object_handlers xmlreader_object_handlers;
    6. static HashTable xmlreader_prop_handlers;
    7. typedef int (*xmlreader_read_int_t)(xmlTextReaderPtr reader);
    8. typedef unsigned char *(*xmlreader_read_char_t)(xmlTextReaderPtr reader);
    9. typedef const unsigned char *(*xmlreader_read_const_char_t)(xmlTextReaderPtr reader);
    10. typedef int (*xmlreader_write_t)(xmlreader_object *obj, zval *newval TSRMLS_DC);
    11. typedef unsigned char *(*xmlreader_read_one_char_t)(xmlTextReaderPtr reader, const unsigned char *);
    12. typedef struct _xmlreader_prop_handler {
    13.     xmlreader_read_int_t read_int_func;
    14.     xmlreader_read_const_char_t read_char_func;
    15.     xmlreader_write_t write_func;
    16. } xmlreader_prop_handler;
    17. #define XMLREADER_LOAD_STRING 0
    18. #define XMLREADER_LOAD_FILE 1
    19. /* {{{ xmlreader_register_prop_handler */
    20. static void xmlreader_register_prop_handler(HashTable *prop_handler, char *name, xmlreader_read_int_t read_int_func, xmlreader_read_const_char_t read_char_func, int rettype TSRMLS_DC)
    21.     xmlreader_prop_handler hnd;
    22.     hnd.read_char_func = read_char_func;
    23.     hnd.read_int_func = read_int_func;
    24.     zend_hash_add(prop_handler, name, strlen(name)+1, &hnd, sizeof(xmlreader_prop_handler), NULL);
    25. /* {{{ xmlreader_property_reader */
    26. static int xmlreader_property_reader(xmlreader_object *obj, xmlreader_prop_handler *hnd, zval **retval TSRMLS_DC)
    27.     const xmlChar *retchar = NULL;
    28.         if (hnd->read_char_func) {
    29.             retchar = hnd->read_char_func(obj->ptr);
    30.             if (hnd->read_int_func) {
    31.                 retint = hnd->read_int_func(obj->ptr);
    32.                     php_error_docref(NULL TSRMLS_CC, E_WARNING, «Internal libxml error returned»);
    33.                 ZVAL_STRING(*retval, (char *) retchar, 1);
    34.                 ZVAL_EMPTY_STRING(*retval);
    35.             ZVAL_BOOL(*retval, retint);
    36.             ZVAL_LONG(*retval, retint);
    37. /* {{{ xmlreader_get_property_ptr_ptr */
    38. zval **xmlreader_get_property_ptr_ptr(zval *object, zval *member TSRMLS_DC)
    39.     xmlreader_prop_handler *hnd;
    40.     zend_object_handlers *std_hnd;
    41.     if (member->type != IS_STRING) {
    42.         zval_copy_ctor(&tmp_member);
    43.         convert_to_string(&tmp_member);
    44.     obj = (xmlreader_object *)zend_objects_get_address(object TSRMLS_CC);
    45.     if (obj->prop_handler != NULL) {
    46.         ret = zend_hash_find(obj->prop_handler, Z_STRVAL_P(member), Z_STRLEN_P(member)+1, (void **) &hnd);
    47.         std_hnd = zend_get_std_object_handlers();
    48.         retval = std_hnd->get_property_ptr_ptr(object, member TSRMLS_CC);
    49.     if (member == &tmp_member) {
    50. /* {{{ xmlreader_read_property */
    51. zval *xmlreader_read_property(zval *object, zval *member, int type TSRMLS_DC)
    52.     xmlreader_prop_handler *hnd;
    53.     zend_object_handlers *std_hnd;
    54.     if (member->type != IS_STRING) {
    55.         zval_copy_ctor(&tmp_member);
    56.         convert_to_string(&tmp_member);
    57.     obj = (xmlreader_object *)zend_objects_get_address(object TSRMLS_CC);
    58.     if (obj->prop_handler != NULL) {
    59.         ret = zend_hash_find(obj->prop_handler, Z_STRVAL_P(member), Z_STRLEN_P(member)+1, (void **) &hnd);
    60.         ret = xmlreader_property_reader(obj, hnd, &retval TSRMLS_CC);
    61.             /* ensure we’re creating a temporary variable */
    62.             Z_SET_REFCOUNT_P(retval, 0);
    63.             retval = EG(uninitialized_zval_ptr);
    64.         std_hnd = zend_get_std_object_handlers();
    65.         retval = std_hnd->read_property(object, member, type TSRMLS_CC);
    66.     if (member == &tmp_member) {
    67. /* {{{ xmlreader_write_property */
    68. void xmlreader_write_property(zval *object, zval *member, zval *value TSRMLS_DC)
    69.     xmlreader_prop_handler *hnd;
    70.     zend_object_handlers *std_hnd;
    71.     if (member->type != IS_STRING) {
    72.         zval_copy_ctor(&tmp_member);
    73.         convert_to_string(&tmp_member);
    74.     obj = (xmlreader_object *)zend_objects_get_address(object TSRMLS_CC);
    75.     if (obj->prop_handler != NULL) {
    76.         ret = zend_hash_find((HashTable *)obj->prop_handler, Z_STRVAL_P(member), Z_STRLEN_P(member)+1, (void **) &hnd);
    77.         php_error_docref(NULL TSRMLS_CC, E_WARNING, «Cannot write to read-only property»);
    78.         std_hnd = zend_get_std_object_handlers();
    79.         std_hnd->write_property(object, member, value TSRMLS_CC);
    80.     if (member == &tmp_member) {
    81. /* {{{ _xmlreader_get_valid_file_path */
    82. /* _xmlreader_get_valid_file_path and _xmlreader_get_relaxNG should be made a
    83.     common function in libxml extension as code is common to a few xml extensions */
    84. char *_xmlreader_get_valid_file_path(char *source, char *resolved_path, int resolved_path_len  TSRMLS_DC) {
    85.     escsource = xmlURIEscapeStr((xmlChar *)source, (xmlChar *)»:»);
    86.     xmlParseURIReference(uri, (const char *)escsource);
    87.     if (uri->scheme != NULL) {
    88.         /* absolute file uris — libxml only supports localhost or empty host */
    89.         if (strncasecmp(source, «file:///»,8) == 0) {
    90.         } else if (strncasecmp(source, «file://localhost/»,17) == 0) {
    91.     if ((uri->scheme == NULL || isFileUri)) {
    92.         if (!VCWD_REALPATH(source, resolved_path) && !expand_filepath(source, resolved_path TSRMLS_CC)) {
    93.         file_dest = resolved_path;
    94. #ifdef LIBXML_SCHEMAS_ENABLED
    95. /* {{{ _xmlreader_get_relaxNG */
    96. static xmlRelaxNGPtr _xmlreader_get_relaxNG(char *source, int source_len, int type,
    97.                                             xmlRelaxNGValidityErrorFunc error_func,
    98.                                             xmlRelaxNGValidityWarningFunc warn_func TSRMLS_DC)
    99.     xmlRelaxNGParserCtxtPtr parser = NULL;
    100.     char resolved_path[MAXPATHLEN + 1];
    101.     case XMLREADER_LOAD_FILE:
    102.         valid_file = _xmlreader_get_valid_file_path(source, resolved_path, MAXPATHLEN  TSRMLS_CC);
    103.         parser = xmlRelaxNGNewParserCtxt(valid_file);
    104.     case XMLREADER_LOAD_STRING:
    105.         parser = xmlRelaxNGNewMemParserCtxt(source, source_len);
    106.         /* If loading from memory, we need to set the base directory for the document
    107.            but it is not apparent how to do that for schema’s */
    108.     if (error_func || warn_func) {
    109.         xmlRelaxNGSetParserErrors(parser,
    110.             (xmlRelaxNGValidityErrorFunc) error_func,
    111.             (xmlRelaxNGValidityWarningFunc) warn_func,
    112.     sptr = xmlRelaxNGParse(parser);
    113.     xmlRelaxNGFreeParserCtxt(parser);
    114. static const zend_module_dep xmlreader_deps[] = {
    115.     ZEND_MOD_REQUIRED(«libxml»)
    116. /* {{{ xmlreader_module_entry
    117. zend_module_entry xmlreader_module_entry = {
    118.     STANDARD_MODULE_HEADER_EX, NULL,
    119.     PHP_MSHUTDOWN(xmlreader),
    120.     «0.1», /* Replace with version number for your extension */
    121.     STANDARD_MODULE_PROPERTIES
    122. #ifdef COMPILE_DL_XMLREADER
    123. ZEND_GET_MODULE(xmlreader)
    124. /* {{{ xmlreader_objects_clone */
    125. void xmlreader_objects_clone(void *object, void **object_clone TSRMLS_DC)
    126. /* {{{ xmlreader_free_resources */
    127. static void xmlreader_free_resources(xmlreader_object *intern) {
    128.             xmlFreeParserInputBuffer(intern->input);
    129.             xmlFreeTextReader(intern->ptr);
    130. #ifdef LIBXML_SCHEMAS_ENABLED
    131.             xmlRelaxNGFree((xmlRelaxNGPtr) intern->schema);
    132. /* {{{ xmlreader_objects_free_storage */
    133. void xmlreader_objects_free_storage(void *object TSRMLS_DC)
    134.     xmlreader_object *intern = (xmlreader_object *)object;
    135.     zend_object_std_dtor(&intern->std TSRMLS_CC);
    136.     xmlreader_free_resources(intern);
    137. /* {{{ xmlreader_objects_new */
    138. zend_object_value xmlreader_objects_new(zend_class_entry *class_type TSRMLS_DC)
    139.     zend_object_value retval;
    140.     xmlreader_object *intern;
    141.     intern = emalloc(sizeof(xmlreader_object));
    142.     memset(&intern->std, 0, sizeof(zend_object));
    143.     intern->prop_handler = &xmlreader_prop_handlers;
    144.     zend_object_std_init(&intern->std, class_type TSRMLS_CC);
    145.     zend_hash_copy(intern->std.properties, &class_type->default_properties, (copy_ctor_func_t) zval_add_ref, (void *) &tmp, sizeof(zval *));
    146.     retval.handle = zend_objects_store_put(intern, (zend_objects_store_dtor_t)zend_objects_destroy_object, (zend_objects_free_object_storage_t) xmlreader_objects_free_storage, xmlreader_objects_clone TSRMLS_CC);
    147.     intern->handle = retval.handle;
    148.     retval.handlers = &xmlreader_object_handlers;
    149. /* {{{ php_xmlreader_string_arg */
    150. static void php_xmlreader_string_arg(INTERNAL_FUNCTION_PARAMETERS, xmlreader_read_one_char_t internal_function) {
    151.     xmlreader_object *intern;
    152.     if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, «s», &name, &name_len) == FAILURE) {
    153.         php_error_docref(NULL TSRMLS_CC, E_WARNING, «Argument cannot be an empty string»);
    154.     intern = (xmlreader_object *)zend_object_store_get_object(id TSRMLS_CC);
    155.     if (intern && intern->ptr) {
    156.         retchar = (char *)internal_function(intern->ptr, (const unsigned char *)name);
    157.         RETVAL_STRING(retchar, 1);
    158. /* {{{ php_xmlreader_no_arg */
    159. static void php_xmlreader_no_arg(INTERNAL_FUNCTION_PARAMETERS, xmlreader_read_int_t internal_function) {
    160.     xmlreader_object *intern;
    161.     intern = (xmlreader_object *)zend_object_store_get_object(id TSRMLS_CC);
    162.     if (intern && intern->ptr) {
    163.         retval = internal_function(intern->ptr);
    164. #if LIBXML_VERSION >= 20620
    165. /* {{{ php_xmlreader_no_arg_string */
    166. static void php_xmlreader_no_arg_string(INTERNAL_FUNCTION_PARAMETERS, xmlreader_read_char_t internal_function) {
    167.     xmlreader_object *intern;
    168.     intern = (xmlreader_object *)zend_object_store_get_object(id TSRMLS_CC);
    169.     if (intern && intern->ptr) {
    170.         retchar = (char *)internal_function(intern->ptr);
    171.         RETVAL_STRING(retchar, 1);
    172. /* {{{ php_xmlreader_set_relaxng_schema */
    173. static void php_xmlreader_set_relaxng_schema(INTERNAL_FUNCTION_PARAMETERS, int type) {
    174. #ifdef LIBXML_SCHEMAS_ENABLED
    175.     int source_len = 0, retval = -1;
    176.     xmlreader_object *intern;
    177.     xmlRelaxNGPtr schema = NULL;
    178.     if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, «s!», &source, &source_len) == FAILURE) {
    179.     if (source != NULL && !source_len) {
    180.         php_error_docref(NULL TSRMLS_CC, E_WARNING, «Schema data source is required»);
    181.     intern = (xmlreader_object *)zend_object_store_get_object(id TSRMLS_CC);
    182.     if (intern && intern->ptr) {
    183.             schema =  _xmlreader_get_relaxNG(source, source_len, type, NULL, NULL TSRMLS_CC);
    184.                 retval = xmlTextReaderRelaxNGSetSchema(intern->ptr, schema);
    185.             /* unset the associated relaxNG context and schema if one exists */
    186.             retval = xmlTextReaderRelaxNGSetSchema(intern->ptr, NULL);
    187.                 xmlRelaxNGFree((xmlRelaxNGPtr) intern->schema);
    188.     php_error_docref(NULL TSRMLS_CC, E_WARNING, «Unable to set schema. This must be set prior to reading or schema contains errors.»);
    189.     php_error_docref(NULL TSRMLS_CC, E_WARNING, «No Schema support built into libxml.»);
    190. /* {{{ proto boolean XMLReader::close()
    191. Closes xmlreader — current frees resources until xmlTextReaderClose is fixed in libxml */
    192. PHP_METHOD(xmlreader, close)
    193.     xmlreader_object *intern;
    194.     intern = (xmlreader_object *)zend_object_store_get_object(id TSRMLS_CC);
    195.     /* libxml is segfaulting in versions up to 2.6.8 using xmlTextReaderClose so for
    196.     now we will free the whole reader when close is called as it would get rebuilt on
    197.     xmlreader_free_resources(intern);
    198. /* {{{ proto string XMLReader::getAttribute(string name)
    199. Get value of an attribute from current element */
    200. PHP_METHOD(xmlreader, getAttribute)
    201.     php_xmlreader_string_arg(INTERNAL_FUNCTION_PARAM_PASSTHRU, xmlTextReaderGetAttribute);
    202. /* {{{ proto string XMLReader::getAttributeNo(int index)
    203. Get value of an attribute at index from current element */
    204. PHP_METHOD(xmlreader, getAttributeNo)
    205.     xmlreader_object *intern;
    206.     if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, «l», &attr_pos) == FAILURE) {
    207.     intern = (xmlreader_object *)zend_object_store_get_object(id TSRMLS_CC);
    208.     if (intern && intern->ptr) {
    209.         retchar = (char *)xmlTextReaderGetAttributeNo(intern->ptr, attr_pos);
    210.         RETVAL_STRING(retchar, 1);
    211. /* {{{ proto string XMLReader::getAttributeNs(string name, string namespaceURI)
    212. Get value of a attribute via name and namespace from current element */
    213. PHP_METHOD(xmlreader, getAttributeNs)
    214.     int name_len = 0, ns_uri_len = 0;
    215.     xmlreader_object *intern;
    216.     char *name, *ns_uri, *retchar = NULL;
    217.     if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, «ss», &name, &name_len, &ns_uri, &ns_uri_len) == FAILURE) {
    218.     if (name_len == 0 || ns_uri_len == 0) {
    219.         php_error_docref(NULL TSRMLS_CC, E_WARNING, «Attribute Name and Namespace URI cannot be empty»);
    220.     intern = (xmlreader_object *)zend_object_store_get_object(id TSRMLS_CC);
    221.     if (intern && intern->ptr) {
    222.         retchar = (char *)xmlTextReaderGetAttributeNs(intern->ptr, (xmlChar *)name, (xmlChar *)ns_uri);
    223.         RETVAL_STRING(retchar, 1);
    224. /* {{{ proto boolean XMLReader::getParserProperty(int property)
    225. Indicates whether given property (one of the parser option constants) is set or not on parser */
    226. PHP_METHOD(xmlreader, getParserProperty)
    227.     xmlreader_object *intern;
    228.     if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, «l», &property) == FAILURE) {
    229.     intern = (xmlreader_object *)zend_object_store_get_object(id TSRMLS_CC);
    230.     if (intern && intern->ptr) {
    231.         retval = xmlTextReaderGetParserProp(intern->ptr,property);
    232.         php_error_docref(NULL TSRMLS_CC, E_WARNING, «Invalid parser property»);
    233. /* {{{ proto boolean XMLReader::isValid()
    234. Returns boolean indicating if parsed document is valid or not.
    235. Must set XMLREADER_LOADDTD or XMLREADER_VALIDATE parser option prior to the first call to read
    236. or this method will always return FALSE */
    237. PHP_METHOD(xmlreader, isValid)
    238.     php_xmlreader_no_arg(INTERNAL_FUNCTION_PARAM_PASSTHRU, xmlTextReaderIsValid);
    239. /* {{{ proto string XMLReader::lookupNamespace(string prefix)
    240. Return namespaceURI for associated prefix on current node */
    241. PHP_METHOD(xmlreader, lookupNamespace)
    242.     php_xmlreader_string_arg(INTERNAL_FUNCTION_PARAM_PASSTHRU, xmlTextReaderLookupNamespace);
    243. /* {{{ proto boolean XMLReader::moveToAttribute(string name)
    244. Positions reader at specified attribute — Returns TRUE on success and FALSE on failure */
    245. PHP_METHOD(xmlreader, moveToAttribute)
    246.     int name_len = 0, retval;
    247.     xmlreader_object *intern;
    248.     if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, «s», &name, &name_len) == FAILURE) {
    249.         php_error_docref(NULL TSRMLS_CC, E_WARNING, «Attribute Name is required»);
    250.     intern = (xmlreader_object *)zend_object_store_get_object(id TSRMLS_CC);
    251.     if (intern && intern->ptr) {
    252.         retval = xmlTextReaderMoveToAttribute(intern->ptr, (xmlChar *)name);
    253. /* {{{ proto boolean XMLReader::moveToAttributeNo(int index)
    254. Positions reader at attribute at spcecified index.
    255. Returns TRUE on success and FALSE on failure */
    256. PHP_METHOD(xmlreader, moveToAttributeNo)
    257.     xmlreader_object *intern;
    258.     if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, «l», &attr_pos) == FAILURE) {
    259.     intern = (xmlreader_object *)zend_object_store_get_object(id TSRMLS_CC);
    260.     if (intern && intern->ptr) {
    261.         retval = xmlTextReaderMoveToAttributeNo(intern->ptr, attr_pos);
    262. /* {{{ proto boolean XMLReader::moveToAttributeNs(string name, string namespaceURI)
    263. Positions reader at attribute spcified by name and namespaceURI.
    264. Returns TRUE on success and FALSE on failure */
    265. PHP_METHOD(xmlreader, moveToAttributeNs)
    266.     int name_len=0, ns_uri_len=0, retval;
    267.     xmlreader_object *intern;
    268.     if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, «ss», &name, &name_len, &ns_uri, &ns_uri_len) == FAILURE) {
    269.     if (name_len == 0 || ns_uri_len == 0) {
    270.         php_error_docref(NULL TSRMLS_CC, E_WARNING, «Attribute Name and Namespace URI cannot be empty»);
    271.     intern = (xmlreader_object *)zend_object_store_get_object(id TSRMLS_CC);
    272.     if (intern && intern->ptr) {
    273.         retval = xmlTextReaderMoveToAttributeNs(intern->ptr, (xmlChar *)name, (xmlChar *)ns_uri);
    274. /* {{{ proto boolean XMLReader::moveToElement()
    275. Moves the position of the current instance to the node that contains the current Attribute node. */
    276. PHP_METHOD(xmlreader, moveToElement)
    277.     php_xmlreader_no_arg(INTERNAL_FUNCTION_PARAM_PASSTHRU, xmlTextReaderMoveToElement);
    278. /* {{{ proto boolean XMLReader::moveToFirstAttribute()
    279. Moves the position of the current instance to the first attribute associated with the current node. */
    280. PHP_METHOD(xmlreader, moveToFirstAttribute)
    281.     php_xmlreader_no_arg(INTERNAL_FUNCTION_PARAM_PASSTHRU, xmlTextReaderMoveToFirstAttribute);
    282. /* {{{ proto boolean XMLReader::moveToNextAttribute()
    283. Moves the position of the current instance to the next attribute associated with the current node. */
    284. PHP_METHOD(xmlreader, moveToNextAttribute)
    285.     php_xmlreader_no_arg(INTERNAL_FUNCTION_PARAM_PASSTHRU, xmlTextReaderMoveToNextAttribute);
    286. /* {{{ proto boolean XMLReader::read()
    287. Moves the position of the current instance to the next node in the stream. */
    288. PHP_METHOD(xmlreader, read)
    289.     xmlreader_object *intern;
    290.     intern = (xmlreader_object *)zend_object_store_get_object(id TSRMLS_CC);
    291.     if (intern != NULL && intern->ptr != NULL) {
    292.         retval = xmlTextReaderRead(intern->ptr);
    293.             php_error_docref(NULL TSRMLS_CC, E_WARNING, «An Error Occured while reading»);
    294.     php_error_docref(NULL TSRMLS_CC, E_WARNING, «Load Data before trying to read»);
    295. /* {{{ proto boolean XMLReader::next([string localname])
    296. Moves the position of the current instance to the next node in the stream. */
    297. PHP_METHOD(xmlreader, next)
    298.     xmlreader_object *intern;
    299.     if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, «|s», &name, &name_len) == FAILURE) {
    300.     intern = (xmlreader_object *)zend_object_store_get_object(id TSRMLS_CC);
    301.     if (intern != NULL && intern->ptr != NULL) {
    302. #if LIBXML_VERSION <= 20617
    303.         /* Bug in libxml prevents a next in certain cases when positioned on end of element */
    304.         if (xmlTextReaderNodeType(intern->ptr) == XML_READER_TYPE_END_ELEMENT) {
    305.             retval = xmlTextReaderRead(intern->ptr);
    306.         retval = xmlTextReaderNext(intern->ptr);
    307.         while (name != NULL && retval == 1) {
    308.             if (xmlStrEqual(xmlTextReaderConstLocalName(intern->ptr), (xmlChar *)name)) {
    309.             retval = xmlTextReaderNext(intern->ptr);
    310.             php_error_docref(NULL TSRMLS_CC, E_WARNING, «An Error Occured while reading»);
    311.     php_error_docref(NULL TSRMLS_CC, E_WARNING, «Load Data before trying to read»);
    312. /* {{{ proto boolean XMLReader::open(string URI [, string encoding [, int options]])
    313. Sets the URI that the the XMLReader will parse. */
    314. PHP_METHOD(xmlreader, open)
    315.     int source_len = 0, encoding_len = 0;
    316.     xmlreader_object *intern = NULL;
    317.     char *source, *valid_file = NULL;
    318.     char resolved_path[MAXPATHLEN + 1];
    319.     xmlTextReaderPtr reader = NULL;
    320.     if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, «s|s!l», &source, &source_len, &encoding, &encoding_len, &options) == FAILURE) {
    321.         if (! instanceof_function(Z_OBJCE_P(id), xmlreader_class_entry TSRMLS_CC)) {
    322.             intern = (xmlreader_object *)zend_object_store_get_object(id TSRMLS_CC);
    323.             xmlreader_free_resources(intern);
    324.         php_error_docref(NULL TSRMLS_CC, E_WARNING, «Empty string supplied as input»);
    325.     valid_file = _xmlreader_get_valid_file_path(source, resolved_path, MAXPATHLEN  TSRMLS_CC);
    326.         reader = xmlReaderForFile(valid_file, encoding, options);
    327.         php_error_docref(NULL TSRMLS_CC, E_WARNING, «Unable to open source data»);
    328.         object_init_ex(return_value, xmlreader_class_entry);
    329.         intern = (xmlreader_object *)zend_objects_get_address(return_value TSRMLS_CC);
    330. /* Not Yet Implemented in libxml — functions exist just not coded
    331. PHP_METHOD(xmlreader, resetState)
    332. #if LIBXML_VERSION >= 20620
    333. /* {{{ proto string XMLReader::readInnerXml()
    334. Reads the contents of the current node, including child nodes and markup. */
    335. PHP_METHOD(xmlreader, readInnerXml)
    336.     php_xmlreader_no_arg_string(INTERNAL_FUNCTION_PARAM_PASSTHRU, xmlTextReaderReadInnerXml);
    337. /* {{{ proto boolean XMLReader::readOuterXml()
    338. Reads the contents of the current node, including child nodes and markup. */
    339. PHP_METHOD(xmlreader, readOuterXml)
    340.     php_xmlreader_no_arg_string(INTERNAL_FUNCTION_PARAM_PASSTHRU, xmlTextReaderReadOuterXml);
    341. /* {{{ proto boolean XMLReader::readString()
    342. Reads the contents of an element or a text node as a string. */
    343. PHP_METHOD(xmlreader, readString)
    344.     php_xmlreader_no_arg_string(INTERNAL_FUNCTION_PARAM_PASSTHRU, xmlTextReaderReadString);
    345. /* {{{ proto boolean XMLReader::setSchema(string filename)
    346. Use W3C XSD schema to validate the document as it is processed. Activation is only possible before the first Read(). */
    347. PHP_METHOD(xmlreader, setSchema)
    348. #ifdef LIBXML_SCHEMAS_ENABLED
    349.     int source_len = 0, retval = -1;
    350.     xmlreader_object *intern;
    351.     if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, «s!», &source, &source_len) == FAILURE) {
    352.     if (source != NULL && !source_len) {
    353.         php_error_docref(NULL TSRMLS_CC, E_WARNING, «Schema data source is required»);
    354.     intern = (xmlreader_object *)zend_object_store_get_object(id TSRMLS_CC);
    355.     if (intern && intern->ptr) {
    356.         retval = xmlTextReaderSchemaValidate(intern->ptr, source);
    357.     php_error_docref(NULL TSRMLS_CC, E_WARNING, «Unable to set schema. This must be set prior to reading or schema contains errors.»);
    358.     php_error_docref(NULL TSRMLS_CC, E_WARNING, «No Schema support built into libxml.»);
    359. /* {{{ proto boolean XMLReader::setParserProperty(int property, boolean value)
    360. Sets parser property (one of the parser option constants).
    361. Properties must be set after open() or XML() and before the first read() is called */
    362. PHP_METHOD(xmlreader, setParserProperty)
    363.     xmlreader_object *intern;
    364.     if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, «lb», &property, &value) == FAILURE) {
    365.     intern = (xmlreader_object *)zend_object_store_get_object(id TSRMLS_CC);
    366.     if (intern && intern->ptr) {
    367.         retval = xmlTextReaderSetParserProp(intern->ptr,property, value);
    368.         php_error_docref(NULL TSRMLS_CC, E_WARNING, «Invalid parser property»);
    369. /* {{{ proto boolean XMLReader::setRelaxNGSchema(string filename)
    370. Sets the string that the the XMLReader will parse. */
    371. PHP_METHOD(xmlreader, setRelaxNGSchema)
    372.     php_xmlreader_set_relaxng_schema(INTERNAL_FUNCTION_PARAM_PASSTHRU, XMLREADER_LOAD_FILE);
    373. /* {{{ proto boolean XMLReader::setRelaxNGSchemaSource(string source)
    374. Sets the string that the the XMLReader will parse. */
    375. PHP_METHOD(xmlreader, setRelaxNGSchemaSource)
    376.     php_xmlreader_set_relaxng_schema(INTERNAL_FUNCTION_PARAM_PASSTHRU, XMLREADER_LOAD_STRING);
    377.             xmlTextReaderSetSchema  (xmlTextReaderPtr reader,
    378. /* {{{ proto boolean XMLReader::XML(string source [, string encoding [, int options]])
    379. Sets the string that the the XMLReader will parse. */
    380. PHP_METHOD(xmlreader, XML)
    381.     int source_len = 0, encoding_len = 0;
    382.     xmlreader_object *intern = NULL;
    383.     char *source, *uri = NULL, *encoding = NULL;
    384.     int resolved_path_len, ret = 0;
    385.     char *directory=NULL, resolved_path[MAXPATHLEN];
    386.     xmlParserInputBufferPtr inputbfr;
    387.     if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, «s|s!l», &source, &source_len, &encoding, &encoding_len, &options) == FAILURE) {
    388.     if (id != NULL && ! instanceof_function(Z_OBJCE_P(id), xmlreader_class_entry TSRMLS_CC)) {
    389.         intern = (xmlreader_object *)zend_object_store_get_object(id TSRMLS_CC);
    390.         xmlreader_free_resources(intern);
    391.         php_error_docref(NULL TSRMLS_CC, E_WARNING, «Empty string supplied as input»);
    392.     inputbfr = xmlParserInputBufferCreateMem(source, source_len, XML_CHAR_ENCODING_NONE);
    393. /* Get the URI of the current script so that we can set the base directory in libxml */
    394.         directory = VCWD_GETCWD(resolved_path, MAXPATHLEN);
    395.         directory = VCWD_GETWD(resolved_path);
    396.             resolved_path_len = strlen(resolved_path);
    397.             if (resolved_path[resolved_path_len — 1] != DEFAULT_SLASH) {
    398.                 resolved_path[resolved_path_len] = DEFAULT_SLASH;
    399.                 resolved_path[++resolved_path_len] = »;
    400.             uri = (char *) xmlCanonicPath((const xmlChar *) resolved_path);
    401.         reader = xmlNewTextReader(inputbfr, uri);
    402. #if LIBXML_VERSION >= 20628
    403.             ret = xmlTextReaderSetup(reader, NULL, uri, encoding, options);
    404.                     object_init_ex(return_value, xmlreader_class_entry);
    405.                     intern = (xmlreader_object *)zend_objects_get_address(return_value TSRMLS_CC);
    406.                 intern->input = inputbfr;
    407.         xmlFreeParserInputBuffer(inputbfr);
    408.     php_error_docref(NULL TSRMLS_CC, E_WARNING, «Unable to load source data»);
    409. /* {{{ proto boolean XMLReader::expand()
    410. Moves the position of the current instance to the next node in the stream. */
    411. PHP_METHOD(xmlreader, expand)
    412.     zval *id, *rv = NULL, *basenode = NULL;
    413.     xmlreader_object *intern;
    414.     php_libxml_node_object *domobj = NULL;
    415.     if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), «O|O!», &id, xmlreader_class_entry, &basenode, dom_node_class_entry) == FAILURE) {
    416.         NODE_GET_OBJ(node, basenode, xmlNodePtr, domobj);
    417.     intern = (xmlreader_object *)zend_object_store_get_object(id TSRMLS_CC);
    418.     if (intern && intern->ptr) {
    419.         node = xmlTextReaderExpand(intern->ptr);
    420.             php_error_docref(NULL TSRMLS_CC, E_WARNING, «An Error Occured while expanding «);
    421.             nodec = xmlDocCopyNode(node, docp, 1);
    422.                 php_error_docref(NULL TSRMLS_CC, E_NOTICE, «Cannot expand this node type»);
    423.                 DOM_RET_OBJ(rv, nodec, &ret, (dom_object *)domobj);
    424.         php_error_docref(NULL TSRMLS_CC, E_WARNING, «Load Data before trying to expand»);
    425.     php_error(E_WARNING, «DOM support is not enabled»);
    426. ZEND_BEGIN_ARG_INFO(arginfo_xmlreader_close, 0)
    427. ZEND_BEGIN_ARG_INFO(arginfo_xmlreader_getAttribute, 0)
    428. ZEND_BEGIN_ARG_INFO(arginfo_xmlreader_getAttributeNo, 0)
    429. ZEND_BEGIN_ARG_INFO(arginfo_xmlreader_getAttributeNs, 0)
    430.     ZEND_ARG_INFO(0, namespaceURI)
    431. ZEND_BEGIN_ARG_INFO(arginfo_xmlreader_getParserProperty, 0)
    432.     ZEND_ARG_INFO(0, property)
    433. ZEND_BEGIN_ARG_INFO(arginfo_xmlreader_isValid, 0)
    434. ZEND_BEGIN_ARG_INFO(arginfo_xmlreader_lookupNamespace, 0)
    435. ZEND_BEGIN_ARG_INFO(arginfo_xmlreader_moveToAttribute, 0)
    436. ZEND_BEGIN_ARG_INFO(arginfo_xmlreader_moveToAttributeNo, 0)
    437. ZEND_BEGIN_ARG_INFO(arginfo_xmlreader_moveToAttributeNs, 0)
    438.     ZEND_ARG_INFO(0, namespaceURI)
    439. ZEND_BEGIN_ARG_INFO(arginfo_xmlreader_moveToElement, 0)
    440. ZEND_BEGIN_ARG_INFO(arginfo_xmlreader_moveToFirstAttribute, 0)
    441. ZEND_BEGIN_ARG_INFO(arginfo_xmlreader_moveToNextAttribute, 0)
    442. ZEND_BEGIN_ARG_INFO(arginfo_xmlreader_read, 0)
    443. ZEND_BEGIN_ARG_INFO_EX(arginfo_xmlreader_next, 0, 0, 0)
    444.     ZEND_ARG_INFO(0, localname)
    445. ZEND_BEGIN_ARG_INFO_EX(arginfo_xmlreader_open, 0, 0, 1)
    446.     ZEND_ARG_INFO(0, encoding)
    447.     ZEND_ARG_INFO(0, options)
    448. ZEND_BEGIN_ARG_INFO(arginfo_xmlreader_readInnerXml, 0)
    449. ZEND_BEGIN_ARG_INFO(arginfo_xmlreader_readOuterXml, 0)
    450. ZEND_BEGIN_ARG_INFO(arginfo_xmlreader_readString, 0)
    451. ZEND_BEGIN_ARG_INFO(arginfo_xmlreader_setSchema, 0)
    452.     ZEND_ARG_INFO(0, filename)
    453. ZEND_BEGIN_ARG_INFO(arginfo_xmlreader_setParserProperty, 0)
    454.     ZEND_ARG_INFO(0, property)
    455. ZEND_BEGIN_ARG_INFO(arginfo_xmlreader_setRelaxNGSchema, 0)
    456.     ZEND_ARG_INFO(0, filename)
    457. ZEND_BEGIN_ARG_INFO(arginfo_xmlreader_setRelaxNGSchemaSource, 0)
    458. ZEND_BEGIN_ARG_INFO_EX(arginfo_xmlreader_XML, 0, 0, 1)
    459.     ZEND_ARG_INFO(0, encoding)
    460.     ZEND_ARG_INFO(0, options)
    461. ZEND_BEGIN_ARG_INFO(arginfo_xmlreader_expand, 0)
    462. static const zend_function_entry xmlreader_functions[] = {
    463.     PHP_ME(xmlreader, close, arginfo_xmlreader_close, ZEND_ACC_PUBLIC)
    464.     PHP_ME(xmlreader, getAttribute, arginfo_xmlreader_getAttribute, ZEND_ACC_PUBLIC)
    465.     PHP_ME(xmlreader, getAttributeNo, arginfo_xmlreader_getAttributeNo, ZEND_ACC_PUBLIC)
    466.     PHP_ME(xmlreader, getAttributeNs, arginfo_xmlreader_getAttributeNs, ZEND_ACC_PUBLIC)
    467.     PHP_ME(xmlreader, getParserProperty, arginfo_xmlreader_getParserProperty, ZEND_ACC_PUBLIC)
    468.     PHP_ME(xmlreader, isValid, arginfo_xmlreader_isValid, ZEND_ACC_PUBLIC)
    469.     PHP_ME(xmlreader, lookupNamespace, arginfo_xmlreader_lookupNamespace, ZEND_ACC_PUBLIC)
    470.     PHP_ME(xmlreader, moveToAttributeNo, arginfo_xmlreader_moveToAttributeNo, ZEND_ACC_PUBLIC)
    471.     PHP_ME(xmlreader, moveToAttribute, arginfo_xmlreader_moveToAttribute, ZEND_ACC_PUBLIC)
    472.     PHP_ME(xmlreader, moveToAttributeNs, arginfo_xmlreader_moveToAttributeNs, ZEND_ACC_PUBLIC)
    473.     PHP_ME(xmlreader, moveToElement, arginfo_xmlreader_moveToElement, ZEND_ACC_PUBLIC)
    474.     PHP_ME(xmlreader, moveToFirstAttribute, arginfo_xmlreader_moveToFirstAttribute, ZEND_ACC_PUBLIC)
    475.     PHP_ME(xmlreader, moveToNextAttribute, arginfo_xmlreader_moveToNextAttribute, ZEND_ACC_PUBLIC)
    476.     PHP_ME(xmlreader, open, arginfo_xmlreader_open, ZEND_ACC_PUBLIC|ZEND_ACC_ALLOW_STATIC)
    477.     PHP_ME(xmlreader, read, arginfo_xmlreader_read, ZEND_ACC_PUBLIC)
    478.     PHP_ME(xmlreader, next, arginfo_xmlreader_next, ZEND_ACC_PUBLIC)
    479. #if LIBXML_VERSION >= 20620
    480.     PHP_ME(xmlreader, readInnerXml, arginfo_xmlreader_readInnerXml, ZEND_ACC_PUBLIC)
    481.     PHP_ME(xmlreader, readOuterXml, arginfo_xmlreader_readOuterXml, ZEND_ACC_PUBLIC)
    482.     PHP_ME(xmlreader, readString, arginfo_xmlreader_readString, ZEND_ACC_PUBLIC)
    483.     PHP_ME(xmlreader, setSchema, arginfo_xmlreader_setSchema, ZEND_ACC_PUBLIC)
    484. /* Not Yet Implemented though defined in libxml as of 2.6.9dev
    485.     PHP_ME(xmlreader, resetState, NULL, ZEND_ACC_PUBLIC)
    486.     PHP_ME(xmlreader, setParserProperty, arginfo_xmlreader_setParserProperty, ZEND_ACC_PUBLIC)
    487.     PHP_ME(xmlreader, setRelaxNGSchema, arginfo_xmlreader_setRelaxNGSchema, ZEND_ACC_PUBLIC)
    488.     PHP_ME(xmlreader, setRelaxNGSchemaSource, arginfo_xmlreader_setRelaxNGSchemaSource, ZEND_ACC_PUBLIC)
    489.     PHP_ME(xmlreader, XML, arginfo_xmlreader_XML, ZEND_ACC_PUBLIC|ZEND_ACC_ALLOW_STATIC)
    490.     PHP_ME(xmlreader, expand, arginfo_xmlreader_expand, ZEND_ACC_PUBLIC)
    491. /* {{{ PHP_MINIT_FUNCTION
    492. PHP_MINIT_FUNCTION(xmlreader)
    493.     memcpy(&xmlreader_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
    494.     xmlreader_object_handlers.read_property = xmlreader_read_property;
    495.     xmlreader_object_handlers.write_property = xmlreader_write_property;
    496.     xmlreader_object_handlers.get_property_ptr_ptr = xmlreader_get_property_ptr_ptr;
    497.     INIT_CLASS_ENTRY(ce, «XMLReader», xmlreader_functions);
    498.     ce.create_object = xmlreader_objects_new;
    499.     xmlreader_class_entry = zend_register_internal_class(&ce TSRMLS_CC);
    500.     zend_hash_init(&xmlreader_prop_handlers, 0, NULL, NULL, 1);
    501.     xmlreader_register_prop_handler(&xmlreader_prop_handlers, «attributeCount», xmlTextReaderAttributeCount, NULL, IS_LONG TSRMLS_CC);
    502.     xmlreader_register_prop_handler(&xmlreader_prop_handlers, «baseURI», NULL, xmlTextReaderConstBaseUri, IS_STRING TSRMLS_CC);
    503.     xmlreader_register_prop_handler(&xmlreader_prop_handlers, «depth», xmlTextReaderDepth, NULL, IS_LONG TSRMLS_CC);
    504.     xmlreader_register_prop_handler(&xmlreader_prop_handlers, «hasAttributes», xmlTextReaderHasAttributes, NULL, IS_BOOL TSRMLS_CC);
    505.     xmlreader_register_prop_handler(&xmlreader_prop_handlers, «hasValue», xmlTextReaderHasValue, NULL, IS_BOOL TSRMLS_CC);
    506.     xmlreader_register_prop_handler(&xmlreader_prop_handlers, «isDefault», xmlTextReaderIsDefault, NULL, IS_BOOL TSRMLS_CC);
    507.     xmlreader_register_prop_handler(&xmlreader_prop_handlers, «isEmptyElement», xmlTextReaderIsEmptyElement, NULL, IS_BOOL TSRMLS_CC);
    508.     xmlreader_register_prop_handler(&xmlreader_prop_handlers, «localName», NULL, xmlTextReaderConstLocalName, IS_STRING TSRMLS_CC);
    509.     xmlreader_register_prop_handler(&xmlreader_prop_handlers, «name», NULL, xmlTextReaderConstName, IS_STRING TSRMLS_CC);
    510.     xmlreader_register_prop_handler(&xmlreader_prop_handlers, «namespaceURI», NULL, xmlTextReaderConstNamespaceUri, IS_STRING TSRMLS_CC);
    511.     xmlreader_register_prop_handler(&xmlreader_prop_handlers, «nodeType», xmlTextReaderNodeType, NULL, IS_LONG TSRMLS_CC);
    512.     xmlreader_register_prop_handler(&xmlreader_prop_handlers, «prefix», NULL, xmlTextReaderConstPrefix, IS_STRING TSRMLS_CC);
    513.     xmlreader_register_prop_handler(&xmlreader_prop_handlers, «value», NULL, xmlTextReaderConstValue, IS_STRING TSRMLS_CC);
    514.     xmlreader_register_prop_handler(&xmlreader_prop_handlers, «xmlLang», NULL, xmlTextReaderConstXmlLang, IS_STRING TSRMLS_CC);
    515.     /* Constants for NodeType — cannot define common types to share with dom as there are differences in these types */
    516.     REGISTER_XMLREADER_CLASS_CONST_LONG(«NONE», XML_READER_TYPE_NONE);
    517.     REGISTER_XMLREADER_CLASS_CONST_LONG(«ELEMENT»,  XML_READER_TYPE_ELEMENT);
    518.     REGISTER_XMLREADER_CLASS_CONST_LONG(«ATTRIBUTE»,    XML_READER_TYPE_ATTRIBUTE);
    519.     REGISTER_XMLREADER_CLASS_CONST_LONG(«TEXT», XML_READER_TYPE_TEXT);
    520.     REGISTER_XMLREADER_CLASS_CONST_LONG(«CDATA»,    XML_READER_TYPE_CDATA);
    521.     REGISTER_XMLREADER_CLASS_CONST_LONG(«ENTITY_REF»,   XML_READER_TYPE_ENTITY_REFERENCE);
    522.     REGISTER_XMLREADER_CLASS_CONST_LONG(«ENTITY»,   XML_READER_TYPE_ENTITY);
    523.     REGISTER_XMLREADER_CLASS_CONST_LONG(«PI»,   XML_READER_TYPE_PROCESSING_INSTRUCTION);
    524.     REGISTER_XMLREADER_CLASS_CONST_LONG(«COMMENT»,  XML_READER_TYPE_COMMENT);
    525.     REGISTER_XMLREADER_CLASS_CONST_LONG(«DOC»,  XML_READER_TYPE_DOCUMENT);
    526.     REGISTER_XMLREADER_CLASS_CONST_LONG(«DOC_TYPE», XML_READER_TYPE_DOCUMENT_TYPE);
    527.     REGISTER_XMLREADER_CLASS_CONST_LONG(«DOC_FRAGMENT», XML_READER_TYPE_DOCUMENT_FRAGMENT);
    528.     REGISTER_XMLREADER_CLASS_CONST_LONG(«NOTATION», XML_READER_TYPE_NOTATION);
    529.     REGISTER_XMLREADER_CLASS_CONST_LONG(«WHITESPACE»,   XML_READER_TYPE_WHITESPACE);
    530.     REGISTER_XMLREADER_CLASS_CONST_LONG(«SIGNIFICANT_WHITESPACE»,   XML_READER_TYPE_SIGNIFICANT_WHITESPACE);
    531.     REGISTER_XMLREADER_CLASS_CONST_LONG(«END_ELEMENT»,  XML_READER_TYPE_END_ELEMENT);
    532.     REGISTER_XMLREADER_CLASS_CONST_LONG(«END_ENTITY»,   XML_READER_TYPE_END_ENTITY);
    533.     REGISTER_XMLREADER_CLASS_CONST_LONG(«XML_DECLARATION»,  XML_READER_TYPE_XML_DECLARATION);
    534.     /* Constants for Parser options */
    535.     REGISTER_XMLREADER_CLASS_CONST_LONG(«LOADDTD»,  XML_PARSER_LOADDTD);
    536.     REGISTER_XMLREADER_CLASS_CONST_LONG(«DEFAULTATTRS», XML_PARSER_DEFAULTATTRS);
    537.     REGISTER_XMLREADER_CLASS_CONST_LONG(«VALIDATE», XML_PARSER_VALIDATE);
    538.     REGISTER_XMLREADER_CLASS_CONST_LONG(«SUBST_ENTITIES»,   XML_PARSER_SUBST_ENTITIES);
    539.     /* Constants for Errors when loading — not yet used until we implement custom error handling
    540.     REGISTER_XMLREADER_CLASS_CONST_LONG(«VALIDITY_WARNING»,     XML_PARSER_SEVERITY_VALIDITY_WARNING,   CONST_CS | CONST_PERSISTENT);
    541.     REGISTER_XMLREADER_CLASS_CONST_LONG(«VALIDITY_ERROR»,       XML_PARSER_SEVERITY_VALIDITY_ERROR,     CONST_CS | CONST_PERSISTENT);
    542.     REGISTER_XMLREADER_CLASS_CONST_LONG(«WARNING»,              XML_PARSER_SEVERITY_WARNING,            CONST_CS | CONST_PERSISTENT);
    543.     REGISTER_XMLREADER_CLASS_CONST_LONG(«ERROR»,                XML_PARSER_SEVERITY_ERROR,              CONST_CS | CONST_PERSISTENT);
    544. /* {{{ PHP_MSHUTDOWN_FUNCTION
    545. PHP_MSHUTDOWN_FUNCTION(xmlreader)
    546.     zend_hash_destroy(&xmlreader_prop_handlers);
    547. /* {{{ PHP_MINFO_FUNCTION
    548. PHP_MINFO_FUNCTION(xmlreader)
    549.     php_info_print_table_start();
    550.         php_info_print_table_row(2, «XMLReader», «enabled»);
    551.     php_info_print_table_end();
    552.  * vim600: noet sw=4 ts=4 fdm=marker
    553.  * vim<600: noet sw=4 ts=4

Страница 1 из 2

Понравилась статья? Поделить с друзьями:
  • Работа в строительных компаниях в москве свежие вакансии
  • Работа ведется согласно графика по прошествию двух часов
  • Работа во время больничного по инициативе работника 2022
  • Работа во время декретного отпуска неполный рабочий день
  • Работа во время школьных каникул засчитывается ли в стаж