Робота з XML за допомогою скриптової мови PHP
Одним з додатків XML є RSS (Rich Site Summary), в якому окремі елементи (наприклад, заголовки новин) представлені як повністю виділені елементи, що дозволяють іншим сайтам отримувати останні новини в такому форматі, в якому вони бажають їх отримати і відобразити у себе на сторінках в повній злагоді з правилами XML-обміну даними.
RSS версії 0.91 розробила компанія Netscape для їх мережі "My Netscape Network", так RSS дозволяє створити XML- файл, який містить інформацію про сайт, а також окремі елементи, які мають "заголовок", "посилання" і "опис". Це чудово, але як нам, отримавши RSS- файл, витягти з нього інформацію і опублікувати її за допомогою HTML? Кожна мова дозволяє робити це по-своєму, але тут ми будемо використовувати PHP з її вбудованим XML- парсером. PHP використовує бібліотеку expat Джеймса Кларка, яка у вас вже є, якщо у вас встановлений Apache версії 1.3.9 або старше. Щоб розбирати документи XML за допомогою PHP, потрібно включити аргумент - with-xml і зібрати PHP з початкових кодів.
Ми напишемо простий скрипт, який розбирає RSS- файл, витягує з нього інформацію, форматує її і відображає її як звичайний HTML. Він не тільки служить прикладом того, як розбирати XML в PHP, але може бути також включений в будь-який інший скрипт, щоб відображати цю інформацію.
Перше, що нам слід зробити - це створити клас, який буде зберігати наші заголовки:
class xItem {
var $xTitle;
var $xLink;
var $xDescription;
}
Потім нам потрібно визначити кілька глобальних змінних для основної інформації про сайт і масив для зберігання об'єктів заголовка:
$ sTitle = "";
$ sLink = "";
$ sDescription = "";
$ arItems = array();
$ itemCount = 0;
Далі наші перші дві функції:
function startElement($ parser, $ name, $attrs) {
global $ curTag;
$ curTag .= "^$ name";
}
function endElement($ parser, $ name) {
global $ curTag;
$caret_pos = strrpos($ curTag,^);
$ curTag = substr($ curTag,0,$caret_pos);
}
Щоб розібрати XML в PHP, вам слід визначити функції, які викликаються, коли:
- парсер зустрічає початковий елемент тегу;
- парсер зустрічає кінцевий елемент тегу;
- парсер зустрічає дані між початковим і кінцевим тегом.
Ми оформимо їх наступним чином: встановивши глобальну змінну ($ curTag) як рядок, що містить всі батьківські теги, розділені знаком ^. Наприклад, для такої XML-структури:
змінна $ curTag виглядатиме наступним чином:
^ RSS^CHANNEL^ITEM
Все, що нам потрібно зробити - це виявити, коли парсер зустріне правильну $ curTag, і у відповідності з ним витягти дані. Все це проводиться в функції characterData. Ось вона:
function characterData($ parser, $data) {
global $ curTag; // спочатку інформація про канал
global $ sTitle, $ sLink, $ sDescription;
$titleKey = "^ RSS^CHANNEL^TITLE";
$linkKey = "^ RSS^CHANNEL^LINK";
$descKey = "^ RSS^CHANNEL^DESCRIPTION";
if ($ curTag == $titleKey) {
$ sTitle = $data;
} elseif ($ curTag == $linkKey) {
$ sLink = $data;
} elseif ($ curTag == $descKey) {
$ sDescription = $data;
}
// тепер отримуємо елементи
global $ arItems, $ itemCount;
$itemTitleKey = "^ RSS^CHANNEL^ITEM^TITLE";
$itemLinkKey = "^ RSS^CHANNEL^ITEM^LINK";
$itemDescKey = "^ RSS^CHANNEL^ITEM^DESCRIPTION";
if ($ curTag == $itemTitleKey) {
// створюємо новий xItem
$ arItems[$ itemCount] = new xItem();
// встановлюємо властивості нового елементу
$ arItems[$ itemCount]->xTitle = $data;
} elseif ($ curTag == $itemLinkKey) {
$ arItems[$ itemCount]->xLink = $data;
} elseif ($ curTag == $itemDescKey) {
$ arItems[$ itemCount]->xDescription = $data;
// збільшуємо лічильник
$ itemCount++;
}
}
Ця функція перевіряє, чи містить $ curTag потрібний нам рядок, і якщо це так, витягує з нього дані і присвоює змінним. Спочатку вона витягує основну інформацію про сайт, потім перевіряє, чи є елементи. Якщо є, вона створює xItem, вставляє його в масив $ arItems і встановлює властивості для відповідних даних в RSS- файлі.
Тепер, коли функції визначені, ми скористаємося стандартним в PHP способом для зв'язку з XML- парсером:
// основний цикл
$xml_ parser = xml_ parser_create();
xml_set_element_handler($xml_ parser, "startElement", "endElement");
xml_set_character_data_handler($xml_ parser, " characterData");
if (!($fp = fopen($uFile,"r"))) {
die ("Не можу отримати RSS");
}
while ($data = fread($fp, 4096)) {
if (!xml_parse($xml_ parser, $data, feof($fp))) {
die(sprintf("XML помилка: %s в рядку %d", xml_error_string(xml_get_error_code($xml_ parser)), xml_get_current_line_number($xml_ parser)));
}
}
xml_ parser_free($xml_ parser);
Все, що у вищенаведеному коді починається з "xml_" - це стандартні XML- функції PHP. Ми повідомляємо парсеру, що наші функції мають виконуватися, коли він зустрічає початковий тег, кінцевий або- дані, а потім ми завантажуємо RSS файл (змінна $uFile повинна бути встановлена на потрібний RSS- Файл) і запускаємо парсер (xml_parse).
Тепер, коли наші дані розібрані по окремих змінним, не так важко перевести їх в HTML:
Ми додали кілька змінних для опису, шрифту, його розміру.
Коли справа стосується обміну даними, XML важко щось протиставити. Визначення XML-формату, який може бути використаний багатьма людьми (як RSS) - це тільки одна з переваг цієї складної, але елегантної технології.
Повний вихідний код дивіться тут:
class xItem {
var $xTitle;
var $xLink;
var $xDescription;
}
// general vars
$ sTitle = "";
$ sLink = "";
$ sDescription = "";
$ arItems = array();
$ itemCount = 0;
// ********* Start User-Defined Vars ************
// rss url goes here
$uFile = "http://www.wirelessdevnet.com/wirelessnews/ rss/dailynews. rss";
// descriptions (true or false) goes here
$bDesc = true;
// font goes here
$uFont = "Verdana, Arial, Helvetica, sans-serif";
$uFontSize = "2";
// ********* End User-Defined Vars **************
function startElement($ parser, $ name, $attrs) {
global $ curTag;
$ curTag .= "^$ name";
}
function endElement($ parser, $ name) {
global $ curTag;
$caret_pos = strrpos($ curTag,^);
$ curTag = substr($ curTag,0,$caret_pos);
}
function characterData($ parser, $data) { global $ curTag; // get the Channel information first
global $ sTitle, $ sLink, $ sDescription;
$titleKey = "^ RSS^CHANNEL^TITLE";
$linkKey = "^ RSS^CHANNEL^LINK";
$descKey = "^ RSS^CHANNEL^DESCRIPTION";
if ($ curTag == $titleKey) {
$ sTitle = $data;
}
elseif ($ curTag == $linkKey) {
$ sLink = $data;
}
elseif ($ curTag == $descKey) {
$ sDescription = $data;
}
// now get the items
global $ arItems, $ itemCount;
$itemTitleKey = "^ RSS^CHANNEL^ITEM^TITLE";
$itemLinkKey = "^ RSS^CHANNEL^ITEM^LINK";
$itemDescKey = "^ RSS^CHANNEL^ITEM^DESCRIPTION";
if ($ curTag == $itemTitleKey) {
// make new xItem
$ arItems[$ itemCount] = new xItem();
// set new item objects properties
$ arItems[$ itemCount]->xTitle = $data;
}
elseif ($ curTag == $itemLinkKey) {
$ arItems[$ itemCount]->xLink = $data;
}
elseif ($ curTag == $itemDescKey) {
$ arItems[$ itemCount]->xDescription = $data;
// increment item counter
$ itemCount++;
}
}
// main loop
$xml_ parser = xml_ parser_create();
xml_set_element_handler($xml_ parser, "startElement", "endElement");
xml_set_character_data_handler($xml_ parser, " characterData");
if (!($fp = fopen($uFile,"r"))) {
die ("could not open RSS for 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)));
}
}
xml_ parser_free($xml_ parser);
// write out the items
?>
for ($i=0;$i $t xItem = $ arItems[$i];
?>
xTitle); ?>
if ($bDesc) {
?>
xDescription); ?>
}
echo ("
");
}