Как и обещал, познакомлю своих читателей с технологией создания и обработки XML файлов. На сегодняшний день в XML формате хранят всё, что попадается на глаза. Так же XML хорошо себя зарекомендовал как формат передачи данных между клиентом и сервером в Ajax приложениях. Кроме того на языке XML обмениваются данными различные он-лайн сервисы, в которых так же применяется расширенный формат – XML-RPC. Но обо всём по порядку.
XML так же широко распространён за пределами онлайна. В этом формате очень удобно хранить конфигурационные файлы. Более того, даже Microsoft Office 2007 хранит все создаваемые документы в XML структурах! Это уже говорит о многом. Даже существует формат MusicXML, который представляет midi данные в более понятном для человека формате. Я более чем уверен, XML найдёт применение в любой области, куда могут дотянуться руки программистов.
Неискушённый читатель обязательно спросит: зачем же весь этот гемморой по портированию данных из одного представления в другое? Действительно, с первого взгляда невозможно понять всей прелести хранения данных в XML. Это переход на открытые форматы, не нужно сидеть сутками, изучая спецификацию нужного формата или заниматься реверсивным инженерингом, достаточно открыть XML файл и за пару минут понять структуру данных. Благодаря этому, в разы уменьшается время разработки приложений и вместо обработки хитроумного формата можно уделить больше времени для пользовательской части приложения. Да, XML занимает больше дискового пространства, парсеры уступают по скорости прямому доступу к файлам. Но никто не запрещает применять алгоритмы сжатия и шифрования, а так же кеширование XML структур. Но о кешировании я пока что не ничего не слышал. Подведу итог: XML открытый и кроссплатформенный формат.
В этой статье будет рассмотрено:
1) Общая структура XML файла
2) Способы генерации XML данных
3) Обработка (парсинг) XML данных
Для знакомых с HTML (я надеюсь, что большинство моих читателей им владеет) проблем разобраться в XML не будет. Абсолютно тоже самое: открывающие и закрывающие теги, вложенные друг в друга теги (родители и потомки), атрибуты тегов. Если Вам знакомы эти определения и не вызывают трудностей – то можно считать XML Вы уже освоили. А для тех, кто знаком с понятием семантической вёрстки – можно считать начальным знанием XML. Суть XML – построить древовидную (иерархическую) структуру данных, для последующего хранения, передачи и обработки. Рассмотрим простейший пример XML файла.
<?xml version="1.0" encoding="utf-8" ?>
<rss version="2.0">
<channel>
<title>Блог Холи Дайвера</title>
<description>Блог о web-программировани, создании и продвижении сайтов.</description>
<link>http://holydiver.ru/</link>
<lastBuildDate>Wed, 18 Nov 2009 17:27:59 -0000</lastBuildDate>
<item>
<title>Нововведения в статистике Яндекс.Директ</title>
<link>http://holydiver.ru/posts/wordstat-news.html</link>
<description><![CDATA[<p>26 марта 2009 года в статистике Яндекс.Директ (<a href="http://wordstat.yandex.ru/">wordstat.yandex.ru</a>) произошёл ряд изменений.</p>]]></description>
<pubDate>Sat, 28 Mar 2009 20:43:05 +0000</pubDate>
</item>
<item>
<title>Восстановление MySQL таблиц</title>
<link>http://holydiver.ru/posts/mysql-repair-tables.html</link>
<description><![CDATA[<p>Вчера позвонил знакомый и описал невероятную историю.</p>]]></description>
<pubDate>Sat, 28 Mar 2009 15:12:08 +0000</pubDate>
</item>
</channel>
</rss>
Первая строка говорит о том, что данные находятся в XML формате и в кодировке UTF-8. Очень рекомендую использовать именно UTF-8 при обработке XML данных, иначе могу возникать ситуации с неправильным определением кодировки. Далее следует обязательный корневой элемент с любым именем, в данном случае <rss> с аттрибутом version=»2.0″. Абсолютно неважно, как Вы будете называть теги, всё зависит от поставленных задач и фантазии, главное чтобы имена соответствовали спецификации. Далее всё должно быть предельно понятно, если возникают трудности с восприятием структуры XML формата, лучше пойти и заняться более простыми вещами.
Вы должны были обратить внимание, что часть данных заключена в какое-то непонятное «<![CDATA[" и "]]>». Это очень полезная вещь! Если тег содержит HTML или XML данные, которые не нужно парсить (обрабатывать), заключение из в конструкцию <![CDATA[]]> предотвратит это. Возможно выглядит несовсем понятно, но когда перейдём к примерам – всё прояснится.
Первым делом нужно сгенерировать XML данные. Конечно можно делать по принципу echo ‘<title>Восстановление MySQL таблиц</title>’; и в том же духе, но такой убогий вариант нас не устраивает! Для более правильной генерации XML потребуется PHP5 с установленным модулем SimpleXML. Если Вы ещё не перешли на PHP5 – мне очень жаль. Можно конечно воспользоваться различными костылями, в виде функций xml2array или XML Parser функций. В этих случаях Вы не сможете быть уверенными, что при изменении структуры данных, приложение не потеряет работоспособность и его не придётся модифицировать.
<?php
// ЗАПРОС НА RSS ФИД
if(isset($_GET['feed'])) {
// ЗАДАНИЕ ТИПА ДОКУМЕНТА И КОДИРОВКИ
header('Content-Type: text/xml;charset=utf-8');
// СОЗДАНИЕ ЭКЗЕМПЛЯРА КЛАССА И ПЕРЕДАЧА ЕМУ ЗАГОЛОВКА
$xml = new SimpleXMLElement('<?xml version="1.0" encoding="utf-8" ?><rss version="2.0" />');
// ЗАПОЛНЕНИЕ ИНФО О КАНАЛЕ
$channel = $xml->addChild('channel');
$xml->channel->addChild('title', 'Блог Холи Дайвера');
$channel->addChild('description', 'Блог о web-программировани, создании и продвижении сайтов.');
$channel->addChild('link', 'http://holydiver.ru/');
$channel->addChild('lastBuildDate', date('r')); // Дата в формате RFC 2822
// ДОБАВЛЕНИЕ ПЕРВОЙ ЗАПИСИ
$item = $channel->addChild('item');
$item->addChild('title', 'Нововведения в статистике Яндекс.Директ');
$item->addChild('link', 'http://holydiver.ru/posts/wordstat-news.html');
$item->addChild('description', '<![CDATA[<p>26 марта 2009 года в статистике Яндекс.Директ (<a href="http://wordstat.yandex.ru/">wordstat.yandex.ru</a>) произошёл ряд изменений.</p>]]>');
$item->addChild('pubDate', date('r'));
// ДОБАВЛЕНИЕ ВТОРОЙ ЗАПИСИ
$item = $channel->addChild('item');
$item->addChild('title', 'Восстановление MySQL таблиц');
$item->addChild('link', 'http://holydiver.ru/posts/mysql-repair-tables.html');
$item->addChild('description', '<![CDATA[<p>Вчера позвонил знакомый и описал невероятную историю.</p>]]>');
$item->addChild('pubDate', date('r'));
// ВЫВОД XML СТРУКТУРЫ В БРАУЗЕР С ПРЕОБРАЗОВАНИЕМ HTML СУЩНОСТЕЙ В СООТВЕТСТВУЮЩИЕ СИМВОЛЫ
echo html_entity_decode($xml->asXML());
}
?>
Таким нехитрым PHP кодом выводится полноценная RSS лента. Да, RSS лента это XML структура. Если сохранить этот фрагмент кода в файл и обратиться к нему с добавлением имя файла + «?feed», то можно посмотреть сгенерированное содержимое. В данном примере использованы всего 3 функции для работы с XML. SimpleXMLElement() – создаёт новый объект для работы. $object->addChild(name, value) добавляет новый элемент внутрь XML объекта. $object->asXML() – возвращает XML код объекта. Чтобы добавить новый элемент «item» в элемент «channel» нужно выполнить код $xml->channel->addChild(‘item’), а чтобы в этот «item» добавить «title» – $xml->channel->item->addChild(‘title’, ‘текст’). Чем больше уровень вложенности, тем длиннее будет выглядеть команда. Но можно сделать всё проще, каждый вызов функции addChild() возвращает ссылку на созданный элемент. То есть если написать $item = $xml->channel->addChild(‘item’), то переменная $item будет ссылаться на $xml->channel->item. Вот такое простое сокращение кода и увеличение читаемости. Надеюсь получилось достаточно понятно. При выводе я использовал функцию html_entity_decode(), так как по-умолчанию SimpleXML превращает все HTML символы в их заменители, например, < d <. На этом генерация XML кода закончена, переходим к парсингу.
Я не просто такрешил сделать вывод RSS фида через параметр «?feed». Было бы глупо для такого простого примера создавать отдельные файлы для PHP скриптов генерации и парсинга XML данных. Поэтому добавляем в уже созданный файл следующий код.
<?php
// ПРОСЛОЙКА ДЛЯ mb_convert_encoding()
function dataconv($string) {
return mb_convert_encoding($string, 'WINDOWS-1251', 'UTF-8');
}
if(!isset($_GET['feed'])) {
// ЗАДАНИЕ ТИПА ДОКУМЕНТА И КОДИРОВКИ
header('Content-Type: text/html;charset=windows-1251');
// ПОЛУЕНИЕ ДАННЫХ ИЗ ФИДА
$data = file_get_contents('http://'.$_SERVER['SERVER_NAME'].$_SERVER['REQUEST_URI'].'?feed');
// ПАРСИНГ
$data = new SimpleXMLElement($data);
$channel = $data->channel;
echo '<html>';
echo '<head>';
echo '<meta http-equiv=content-type content="text/html;charset=windows-1251">';
echo '<title>'.dataconv($channel->title.': Лента новостей').'</title>';
echo '</head>';
echo '<body>';
echo '<h1><a href="'.$channel->link.'">'.dataconv($channel->title).'</a></h1>';
echo '<pre>'.dataconv($channel->description).'</pre>';
// ПРОХОД ПО ВСЕМ ЭЛЕМЕНТАМ item
foreach($channel->item AS $k => $v) {
echo '<h3><a href="'.$v->link.'">'.dataconv($v->title).'</a></h3>';
echo '<p>'.dataconv($v->description).'</p>';
}
echo '</body>';
echo '</html>';
}
?>
Обработка XML данных выглядит ещё проще, чем их генерация! В данном применре я немного схитрил: фид генерируется в кодировке UTF-8, а обработка и вывод HTML страницы в WINDOWS-1251. Это чтобы показать, что нет никакой сложности работы в разных кодировках. Принцип действия прост до безумия: сначала в переменную $data записывается RSS фид, потом в эту же переменную записывается SimpleXML, содержащий эти же данные. Самым интересным моментом сдесл является цикл foreach, перебирающий все элементы item в RSS фиде. Именно так нужно поступать, когда внутри одного родителя содержится несколько элементов с одинаковыми именами. Так же к ним можно полукчить доступ через указание индекса в массиве, то есть $channel->item[0]->title, $channel->item[1]->title и так далее.
На этоми закончу первую статью о XML. Буду рад ответить на все вопросы, плохо раскрытые в этой статье.