PHP и XML
Итак, поговорим об XML. Что же это такое и почему многие профессиональные программисты предпочитают его всем другим форматам? И почему так много хвалебных отзывов со стороны тех, кто с ним столкнулся? И почему, наконец, так мало негативных отзывов от тех, кто с ним не сталкивался :-)? XML как язык (а это именно язык, только не программирования, а разметки) сформировался сравнительно недавно — официально первая редакция его спецификации была опубликована в 1998 году. Формат этот оказался настолько удачным, что сразу пришелся ко двору, и его реализации разошлись практически по всем языкам программирования (правда, пока еще в виде внешних модулей или обработчиков) — от Delphi до PHP. Однако русскоязычной документации по нему мало, а сам язык настолько масштабируем и гибок, что описать все области его применения просто невозможно. Поэтому отечественные программисты еще только начинают постигать тайны XML и пока лишь пытаются применять его на практике.
Аббревиатура XML расшифровывается и переводится как «расширяемый язык разметки». И в этом вся его суть. В принципе, программист сам определяет формат файла и сам пишет его обработчик, используя для этого предоставляемые языком средства или разрабатывая собственные. Теперь никому не нужны километры исходного кода для обработки сотен текстовых файлов — все это легко заменяется одним XML-файлом и одним парсером (обработчиком).
XML-файл является обыкновенным текстовым файлом, данные которого организованы таким образом, чтобы создать иерархическую структуру (дерево) тэгов. Имена и атрибуты тэгов программист придумывает самостоятельно, а правила их написания аналогичны таковым в HTML. Например, для книжного магазина:
<?xml version="1.0" encoding="UTF-8"?>
<shop>
<book author=”Donald Knuth”>Art of programming</book>
<book author=”Vasily Golovachev”>Magacitly</book>
</shop>
Как видите, даже отвлеченный от программирования человек может понять, о чем идет речь в этом файле. Таким нехитрым способом, применяя древовидную структуру организации данных, разработчики достигают полного отделения содержимого (XML) от дизайна (HTML и прочее), в чем и состоит цель рассматриваемого языка разметки.
Теперь об основных понятиях XML-разметки. Количество тэгов в файле не ограничено, равно как и количество их атрибутов. XML-документ должен быть составлен корректно, в соответствии со следующими правилами, многие из которых перекликаются со спецификацией HTML:
1. <?xml version="1.0"?> — в первой строке всегда содержится версия спецификации, но могут быть и дополнительные атрибуты — например, кодировка символов документа.
2. В документе присутствует один и только один корневой элемент (парный тэг <shop> в нашем случае), подобно <html></html> в языке HTML. Все дочерние элементы могут содержать любое количество вложенных тэгов, которые, в свою очередь, тоже могут содержать любое количество потомков, за счет чего и обеспечивается древовидность.
3. Все без исключения тэги должны иметь соответствующие закрывающие элементы. Если в HTML можно было, например, опустить некоторые закрывающие тэги, и это считалось правильным даже в соответствии со спецификацией, то в XML это недопустимо, так как моментально вызовет ошибку обработки. Правда, если в тэге не планируется создавать никаких вложенных элементов (будь то содержимое или другой тэг), то закрыть его можно несколько проще (например, <song name="Only you" /> вместо <song name="Only you"></song>)
4. Все атрибуты тэгов нужно заключать в кавычки — двойные или одинарные.
5. Все остальные правила, дабы не загромождать статью, читайте в спецификации, которая находится по адресу http://www.w3.org/TR/REC-xml.
PHP и XML
Итак, с XML более-менее разобрались. Во всяком случае, несложный структурированный файл в соответствии с правилами XML-разметки вы создать уже сможете, особенно зная основы HTML. Теперь давайте приступим к реализации функций обработки XML-содержимого на языке PHP.
Думаю, не стоит особо распространяться о пользе PHP как серверного языка программирования. Все возможности Perl плюс «еще кое-что» — и мы имеем полноценный интерпретируемый язык программирования, выполняемый на стороне сервера. В дальнейшем предполагается, что сервер и PHP у вас уже установлены и должным образом настроены на совместную работу, и что вы имеете общие понятия об их функционировании и программировании (начинающим программистам рекомендуем ознакомиться с циклом Артема Шманцырева «Сервер племени апачей», МК №№38-40, 42, 44, 46, 50, 4, 9 (209-211, 213, 215, 217, 221, 227, 232) — примеч. ред.) Многие хостинг-провайдеры — это в основном относится к платным хостингам — предоставляют PHP, в конфигурации которого уже доступны модули обработки XML. Для домашней же платформы могу порекомендовать следующее.
В последние версии PHP (начиная с версии 4.3.0 для платформы Windows, как самой распространенной) включена библиотека php_domxml.dll, которую нужно подключить к интерпретатору для получения доступа к функциям обработки XML (подобная библиотека есть и в Linux, но там она подключается несколько иначе). Эта библиотека находится в каталоге extension_dir, прописанном в конфигурационном файле php.ini, который в свою очередь лежит (или должен лежать) в вашей папке Windows. В этом же файле раскомментируйте строчку extension=php_domxml.dll, и вы получите возможность оперировать документами XML, применяя объектную модель Document Object Model, на которой мы сегодня подробно остановимся. Кстати, рассматривать возможности PHP по работе с XML-файлами мы будем на простейшем примере — мы напишем собственную гостевую книгу. Пример, конечно, идеализирован, но на серверах со скриптами я пока еще не встречал гостевых книг, написанных с применением XML. Поэтому, надеюсь, сегодняшние примеры будут для вас не только интересными, но и полезными.
Для гостевой книги мы на сервере создадим файл с именем guest.xml, который будет содержать все оставленные записи в следующем формате:
<?xml version="1.0" encoding="UTF-8"?>
<guestbook>
<message date="01.01.04" time="11:22:25" author="Cosmic" email=“cosmic@mail.zp.ua” subject="New GB presented">
Message body. No HTML tags here.
</message>
<message date="01.01.04" time="12:22:25" author="Cosmic" email=“cosmic@mail.zp.ua” subject="This is a subject">
Another message body
</message>
<message date="01.01.04" time="10:22:25" author="Cosmic" email=“cosmic@mail.zp.ua” subject="i?ia?aiia">
Message body. Attention! Subject in UTF-8 encoding!
</message>
</guestbook>
Как следует из первой строчки, наш XML-файл будет хранить данные в кодировке UTF-8. Для чего это нужно, вы узнаете чуть позже. Корневой элемент <guestbook> содержит множество дочерних элементов <message>, которые характеризуются атрибутами date (дата), time (время), author (автор), email (почтовый адрес) и subject (тема). В теле элемента <message> находится собственно тело сообщения. Как видите, все интуитивно понятно и немножко похоже на базу данных, в которой поля мы называем так, как сами того хотим.
Перед тем как приступить к написанию самого скрипта, хочу сказать, что в PHP функции работы с XML пока реализованы экспериментально (правда, в версии 5.0 на XML сделана особая ставка, вот только версия эта пока еще находится в состоянии беты, следовательно, переходить на нее пока еще не стоит). Это значит, что приведенные скрипты могут не работать при наличии других версий PHP, не соответствующих указанной. К тому же мои скрипты далеки от совершенства в плане производительности и красоты написания. Поэтому не нужно заваливать мой бедный почтовый ящик гневными письмами. Мое дело, как автора, — натолкнуть вас на мысль, а реализация этой мысли остается исключительно за вами.
PHP поддерживает два модуля, осуществляющие XML-парсинг. Первый называется SAX (Simple API for XML). В силу сложности и ограниченной функциональности (с помощью SAX невозможно записать данные в XML-файл — он поддерживает только чтение), этот интерфейс нами сегодня рассматриваться не будет. Второй модуль представляет гораздо больший интерес с точки зрения разработчика, так как позволяет более наглядно работать с XML-файлом, причем поддерживаются операции и чтения, и записи. Называется он DOM (Document Object Module) и реализуется в PHP следующим образом.
С помощью функции domxml_open_file, вызываемой с именем XML-файла в качестве параметра (в нашем случае — guest.xml), все дерево XML-элементов из файла загружается в память компьютера-сервера (в переменную). Это значит, что никакие изменения, произведенные с открытым файлом, не вступят в силу до его принудительной записи на диск. Эта технология очень удобна, так как позволяет десять раз перепроверить корректность произведенных над файлом действий перед тем как окончательно его сохранить. Вообще, корректности в формате XML уделено огромное внимание, что не может не сказаться на его реализациях в языках программирования.
В XML-файле, как уже было сказано, может существовать только один корневой элемент. Корневой элемент можно получить, используя функцию document_element(), вызываемую без параметров и возвращающую объект. В полученном объекте хранятся все дочерние элементы (тэги <message>), которые мы можем получить, вызвав функцию child_nodes(), возвращающую массив дочерних тэгов. Теперь, пройдясь по полученному массиву циклом foreach, можно получить атрибуты каждого из дочерних элементов (функция get_attribute(), вызываемая с именем атрибута в качестве параметра) и содержимое этих элементов (функция get_content() без параметров). Каждый элемент, дочерний для корневого, может быть корневым для вложенных элементов, если таковые имеются. В таком случае их можно также получить во вложенном цикле foreach, пройдясь им по элементу верхнего уровня.
Для нашей гостевой книги скрипт чтения сообщений будет выглядеть следующим образом:
// открытие XML-файла
$xml = domxml_open_file('guest.xml');
// получение корневого элемента
$root = $xml->document_element();
// получение массива вложенных тэгов (потомков)
$nodes = $root->child_nodes();
foreach($nodes as $node) {
// если имя потомка соответствует необходимому...
if ($node->node_name() == 'message') {
// ...заполняем именованный массив текущего сообщения данными из файла...
$currentMessage['date'] = $node->get_attribute('date');
$currentMessage['time'] = $node->get_attribute('time');
$currentMessage['author'] = $node->get_attribute('author');
$currentMessage['email'] = $node->get_attribute('email');
$currentMessage['subject'] = $node->get_attribute('subject');
$currentMessage['content'] = $node->get_content();
// ... и добавляем заполненный элемент в массив
$messages[] = $currentMessage;
}
}
После выполнения скрипта мы получаем заполненный массив $messages, пройдясь по которому в цикле foreach, мы запросто получаем сообщения и выводим их в тело страницы:
foreach($messages as $item) {
echo $item['date'];
echo $item['time'];
echo utf8_decode($item['author']);
echo $item['email'];
echo utf8_decode($item['subject']);
echo utf8_decode($item['content']);
}
В результате выполнения скрипта на страничку в одну строчку выведутся все сообщения из гостевой книги. Я намеренно не делаю никакого оформления (например, можно было бы вывести все сообщения в таблице, разделив все поля и сделав сообщение удобочитаемым), так как статья в этом случае растянется на десяток номеров. Еще раз повторю, что моя задача — натолкнуть вас на идею. Все остальное — дело вашего личного вкуса и предпочтений. Рабочий вариант гостевой вы можете посмотреть по адресу http://www.cosmic.net.ua/gb.
Как я уже говорил, модель DOM позволяет не только читать данные из XML-файла, но и записывать их, добавляя новые элементы или атрибуты. Для того чтобы записать данные, нужно снова открыть XML-файл с помощью функции domxml_open_file и получить корневой элемент дерева (функция document_element()). Далее, с помощью функции new_child() в конце множества имеющихся дочерних тэгов создаем новый дочерний элемент. Функция принимает два параметра — название элемента и собственно его содержимое. Атрибуты дочернего тэга <message> устанавливаются при помощи функции set_attribute(), в качестве параметров принимающей название атрибута и его значение.
Так как мы записываем сообщения гостевой книги, мы их должны сначала получить из массива $_GET или $_POST, в который они должны быть переданы из формы (если вы не знаете, как это делается, можете прочесть об этом в моих предыдущих статьях). Из полученных данных мы готовим именованный массив атрибутов, которые в будущем будут записаны в тэг <message>:
$msgToAdd = array(
'date' => date("d.m.y"),
'time' => date("H:i:s"),
'author' => utf8_encode($author),
'email' => $email,
'subject' => utf8_encode($subject)
)
Как видите, перед записью переменных $author и $subject (а в будущем и тела сообщения), мы перекодируем их в кодировку UTF-8. Это делается по той причине, что на данном этапе PHP, к сожалению, не поддерживает кодировку Windows-1251 в XML-файлах. Это значит, что при попытке записать сообщение на русском языке скрипт будет выдавать ошибку. Поэтому приходится кодировать символы в промежуточную кодировку (функция utf8_encode), а потом при чтении их декодировать (функция utf8_decode). Весь скрипт записи нового сообщения примет примерно следующий вид:
$xml = domxml_open_file('guest.xml');
$root = $xml->document_element();
// $message — тело сообщения из массива $_GET или $_POST
$msgNode = $root->new_child('message',utf8_encode($message));
$msgNode->set_attribute('date',$msgToAdd['date']);
$msgNode->set_attribute('time',$msgToAdd['time']);
$msgNode->set_attribute('author',$msgToAdd['author']);
$msgNode->set_attribute('email',$msgToAdd['email']);
$msgNode->set_attribute('subject',$msgToAdd['subject']);
$text = $xml->dump_mem();
$fp = fopen('guest.xml','w');
fwrite($fp,$text);
fclose($fp);
Здесь появляется новая функция dump_mem(), скидывающая все содержимое памяти в переменную $text, которая в дальнейшем записывается на диск стандартными операторами записи. Как видите, все элементарно просто, безопасно и корректно — пока вы не убедитесь, что все операторы выполнены корректно, ваш файл не будет записан и, следовательно, увеличивается надежность хранения данных и уменьшается вероятность их потери.
В заключение поговорим о возможных областях применения рассмотренных технологий. XML, несмотря на свою универсальность, все же не является панацеей, поэтому не стоит сразу все бросать и переходить на использование нового формата, не убедившись в его стопроцентной необходимости. XML может выручить, например, если заранее неизвестно, каким клиентом будут обрабатываться данные (будь то интернет-браузер или совершенно самостоятельный клиент, написанный сторонними разработчиками). Очень удобно делать развязку между данными и оформлением, строя шаблоны своих страниц на выборках из XML-файлов. Еще удобнее делать сайты на нескольких языках, используя встроенные средства XML для организации мультиязычного интерфейса.
Так что как всегда выбор остается за вами, уважаемые читатели.
Рекомендуем почитать