Парсване на XML през PHP
Публикувана от smilev на January 12 2011 10:38:37

Разширена новина
Урока е доста труден и определено е за хора напреднали в РНР и запознати с основните методи и функции за писане на код.
XML файловете са много "модерни" и удобни в днешния Интернет свят, могат да служат за много и различни цели, от просто маркиране на дадена файлова система, до сложна комуникация между два различни сървъра. Всеки решил да се занимава с разработването на уеб рано или късно ще му се наложи да работи именно с такъв тип файлове.

За урока предполагаме, че имаме дърво от директории с неограничена дълбочина, като във всяка директория (без основната) има по един xml файл, който маркира съдържанието на съответната директория (в случая на примерите - книги), всички xml файлове са със сходна структура и имат някой задължителни елементи.
Отбележете, че може би няма да е лоша идея да си вземете под ръка PHP Manual-а за да погледнете някой от функциите ползвани в по-долните примери. Има и доста коментари по кодовете така, че им обърнете внимание.
На края на урока има и линк към цялата работеща система, с подробно описание какви настройки да нпарваите за да тръгне, предварително се изивнявам, че всички файлове са с коментари на английски ( колкото и лош да е той), но ситуацията го изискваше :)

Опис на файловете:

Основни:

* bpclass.php -- файла, който съдържа класа на самият парсър
* inclu_fns.php -- файла, който съдържа библиотеката от функции, който ще ползваме
* merged.php -- файла, с чиято помощ ще можем да направим един голям общ XML файл


Конфигурационни:

* config.php -- файла, който съдържа основните настройки, без който "системата" няма да работи
* db_info.php -- файла, който съдържа данни за връзката с базата данни ТРЯБВА ДА СЕ НАМИРА ИЗВЪН ПАПКАТА public_html

Допълнителни:

* bottom.php -- съдържа, фуутъра на страницата
* top.php -- съдържа, хедъра на страницата


index.php -- единствения файл, с който борави потребителя

И така...време е да се мятаме :)

Започваме с основния BookParser клас, който се намира в bpclass.php.

<?php
class BookParser
{
var $_mode;
var $_url;
var $_content;
var $_stack = array();//краен масив на всички намерени обекти във файла
var $_names = array();//Тук съхраняваме всички времени имена на получените обекти

function set_BP($mode, $url, $file_content)
{
$this->_content = $file_content;
$this->_mode = $mode;
$this->_url = $url;
}
function parser()
{
$this->parseXML();
return $this->_stack;
}

// Основния метод, който парсва всички обекти/елементи на XMl файла
function parseXML()
{
$xml_parser = xml_parser_create("UTF-8"); //Казваме на парсъра, че нашите файлове са с UTF кодировка, доста е важно ако искаме да се държи нормално и с файлове, които съдържат не само латински букви
xml_parser_set_option($xml_parser, XML_OPTION_SKIP_WHITE, 1); //Задаваме опции на парсъра
xml_set_object($xml_parser, $this);
xml_set_element_handler($xml_parser, "startElementHandler", "endElementHandler");//Показваме на парсъра кои методи да вика в определени ситуации
xml_set_character_data_handler($xml_parser, 'cdataHandler');//същото като горното

if ($this->_mode != 'all')
{
if (@!($fp = fopen($this->_url, "rb")))
{
die("Could not open XML input");
}
while ($data = fread($fp, 4096))
{
if (@!xml_parse($xml_parser, $data, feof($fp)))
{
die(sprintf("XML error: %s at line %d", xml_error_string(xml_get_error_code($xml_parser)),
xml_get_current_line_number($xml_parser)));
}
}
}
elseif($this->_mode == 'all')
{
if (@!xml_parse($xml_parser, $this->_content, feof($fp)))
{
die(sprintf("XML error: %s at line %d", xml_error_string(xml_get_error_code($xml_parser)),
xml_get_current_line_number($xml_parser)));
}
}
xml_parser_free($xml_parser);
}

// Метод, към който се обръщаме когато парсъра достигне началото на обект
function startElementHandler($parser, $name, $attrs)
{
array_push($this->_names, $name);

if ($name == 'BOOKCONTAINER')
{
$this->_currentBook = array();
$this->_currentBook['num'] = -1;
}
elseif ($name == 'BOOK')
{
$this->_currentBook['num']++;
}
}

// Метод, към който се обръщаме когато парсъра достигне стойноста на обекта
function cdataHandler($parser, $cdata)
{
$cdata = trim($cdata);
$this->_currentName = array_slice($this->_names, -1, 1);
$this->_currentName = $this->_currentName[0];

if (empty($this->_stack['BOOK' . $this->_currentBook['num']][$this->
_currentName]))
{
if (!empty($cdata))
$this->_stack['BOOK' . $this->_currentBook['num']][$this->_currentName] = $cdata;
}
else
{
if (!empty($cdata))
$this->_stack['BOOK' . $this->_currentBook['num']][$this->_currentName] .= $cdata;
}
}

// Метод към който парсъра се обръща, когато стигне края на обект
function endElementHandler($parser, $name)
{
//в нашия простоват пример нямаме нужда да правим нищо
}
}
?>

Този клас е ядрото на цялата система, тук се извършват няколко доста прости, но и важни неща. Първо ще обърна внимание на двата метода, които ще се "викат" от този клас, това са set_BP($mode, $url, $file_content) и parser().
Първият метод, очевидно подава на класа някой витални за него данни: $mode показва на класа дали ще парсваме само един XML файл или ще ги вземем всичките веднъж, $url съвсем ясно задава пътя към дадения файл, а $file_content подава съдържанието на всички файлове при положение че mode е указало това.
Методът parser() извиква големият метод parseXML() и връща неговия резултат.

Ще разгледаме следващите методи малко по-подробно.