Главная » Полезные статьи » Язык PHP » Обработка запросов к БД при помощи PEAR::XML
Распечатать статью

Обработка запросов к БД при помощи PEAR::XML

Уважаемые читатели, обратите внимание: это не продолжение серии «Всеобъемлющая история шаблонов». Это отдельная статья. Следующая статья про шаблоны появится чуть позже.

Проект, работающий на технологиях XML, требует иного подхода к формированию документов. Инструменты, сделанные для формирования HTML-документов, часто оказываются непригодными, и требуются новые, имеющие несколько иную концепцию.

Скажем, как после добавления в проект поддержки XSLT формировать в XML данные, получаемые из базы? Оказывается, что делать это при помощи класса шаблона ничуть не проще, чем составлять документ прямо в скрипте:

<?php $result = mysql_query("SELECT DATE_FORMAT(news_date, '%e.%c.%Y') AS date_typed, DATE_FORMAT(news_date, '%d.%m.%Y') AS date_url, title, announce FROM news ORDER BY news_date DESC LIMIT 10"); if(!mysql_error()) { $xml .= "rn"; while($row = mysql_fetch_assoc($result)) $xml .= " {$row[date_url]} {$row[date_typed]} {$row[announce]} rn"; $xml .= "rn"; } ?>

Согласитесь, с классом шаблона мы получили бы примерно тот же объём кода (а ещё файлы .tpl на диске).

Очевидно, что всё, что можно было вывести из скриптов php, уже выведено. Дата в приведённом примере форматируется базой данных и возвращается сразу в нужном нам формате, форматирование с подсветкой четных строк, нумерацией и ещё чем-нибудь делаются в XSLT. Код, которым мы выбираем данные из базы, максимально упрощён и повторяется из раза в раз. Возникает естественное желание сжать его в минимальную конструкцию.

Размышление: Если вы хотите решить какую-то проблему на php, поищите в архивах скриптов — скорее всего для вашей задачи найдётся уже готовое решение. Чужой скрипт, конечно, не будет идеальным решением (потому что наилучшим приближением к идеалу будет решение, написанное специально для задачи), но, скорее всего, будет пригодно к использованию. Вы можете быть противником применения чужого кода в своих проектах, либо не быть уверены в том, что найденный код будет работать как надо (разработчики PEAR, как известно, предупреждают, что весь их проект — вечная бета). Однако посмотреть, как чужой скрипт работает, будет полезно, чтобы написать свой собственный. Заглянув в чужой код, и профессиональный разработчик, и разработчик среднего уровня смогут лучше понять подходы к решению задачи и написать своё собственное, если это понадобится.

В конце сентября, прочитав присланный мне материал про PEAR::DB, я решил поискать на PEAR и класс для автоматического преобразования результатов запроса к базе данных в XML. В разделе XML я нашёл класс, созданный как раз для моей проблемы — XML_sql2xml.

Класс использует для доступа к базам данных другой класс библиотеки PEAR — PEAR::DB. Для операций с XML-документом использует функции DOM XML (это требует установки соответствующего модуля php). Преобразует результат запроса в XML-дерево и возвращает его либо как текст XML-документа, либо как DOM-объект.

Установка PEAR

Тема не была серьёзно описана в материале о PEAR::DB, поэтому будем разбираться.

Библиотеки PEAR раньше распространялись с дистрибутивом php, но больше этого не будет — видимо, чтобы устаревшие версии классов не распространялись с дистрибутивами php, их оттуда убрали. Теперь архивы с классами нужно брать с сервера pear.php.net.

Проблемы с установкой PEAR вызваны тем, что во всех его файлах указываются пути относительно корня директории pear, то есть для вызова PEAR::DB делается команда

include_once("DB/${type}.php");

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

Как вы могли увидеть из строки кода выше, файлы подключаются относительно include_path (который по умолчанию является директорией, из которой работает скрипт, либо директорией, в которую установлен PEAR). Если в проекте include_path устанавливается, вам повезло. Можно положить вызываемые скрипты в эту директорию и наслаждаться жизнью.

Если include_path не устанавливается, его можно установить в .htaccess такой строкой:

php_value include_path my_dir/pear

Вместо my_dir поставьте адрес нужной директории. Можно поменять include_path «на лету» в скрипте:

<?php ini_set("include_path", "my_dir/pear"); include("DB.php"); $dsn = "mysql://user:pass@host/db_name"; $db = DB::connect($dsn, true); if (DB::isError($db)) { die ($db->getMessage()); } $db->setFetchMode(DB_FETCHMODE_ASSOC); ini_restore("include_path"); ?>

После подключения файлов PEAR лучше вернуть include_path на место командой ini_restore. В комментариях к предыдущей статье меня спросили, зачем нужен ini_restore. Во-первых, у меня все скрипты работают из корня сайта, а подключаемые файлы находятся в разных директориях (например, содержимое страницы сайта может быть статичным XML-файлом, а может быть скриптом, который будет вызываться основными скриптами). Пути к подключаемым файлам я указываю тоже от корня. PEAR на сайте появилась не сразу, поэтому ini_restore нужен, чтобы подключить библиотеку, и это не мешало остальным скриптам работать по-старому. Во-вторых, в php.ini или в .htaccess может указываться путь к директории с подключаемыми файлами, а PEAR, по-моему, лучше положить в отдельную директорию, чтобы библиотека не захламляла рабочую. Впрочем, каждый делает как ему удобнее.

Однако хостинг-провайдер может держать php в безопасном режиме, который запрещает менять include_path. В таком случае, конечно, можно попробовать положить файлы PEAR в корень сайта или вручную править все include в файлах — больше ничем разработчики помочь не могут. В новостях PEAR пишут, что Стиг Баккен сообщил о плане добавить новую директиву ‘{get,set,restore}_include_path()’, чтобы можно было менять iclude_path даже в безопасном режиме.

DOM XML в PHP

DOM (Document Object Model) — модель работы с документом, в которой документ содержит объекты, которыми можно манипулировать. Модель DOM является стандартом W3C. Функции DOM XML в php — это одна из реализаций данной модели.

При работе с DOM вы оперируете с переменными, являющимися ссылками на объекты нескольких классов. Список классов и их функций можно узнать в соответствующем разделе руководства по php.

Стандартный модуль php_domxml не поддерживает кириллицу. При работе с ним вам придётся конвертировать данные на входе в UTF-8, а на выходе — обрабатывать сущности вроде &x440;. Чтобы установить DOM XML с поддержкой кириллицы на рабочей машине под Win32, рекомендую скачать мой архив, в котором находится модуль php_domxml скомпилированный с поддержкой кириллицы и необходимые для его работы библиотеки iconv, libxml, libxslt и libexslt. Положите файл php_domxml.dll в extenstion_dir, а остальные библиотеки — в директорию c:windowssystem.

Несколько примеров по работе с DOM XML:

<?php // Создание XML-документа $xmldoc = domxml_new_doc("1.0"); // В условиях документа создаётся элемент под названием my_root. $my_element = $xmldoc->create_element("my_root"); // Затем этот элемент присоединяется к документу как узел-потомок. До этой операции в //документе нет корневого узла! $my_root = $xmldoc->append_child($my_element); // Создаётся ещё один элемент — текст и добавляется как потомок к корневому узлу. $my_element = $xmldoc->create_text_node(iconv("windows-1251", "UTF-8", "Это содержимое //корневого узла XML-документа.")); $my_root->append_child($my_element); // XML-документ преобразуется в текстовый вид и выводится print($xmldoc->dump_mem()); ?>

К сожалению, в приведённом примере возникнут проблемы с русскими символами — на выходе они опять превращаются в &xXXX;. Чтобы модуль domxml понял, что идёт работа с русской кодировкой, нужно на входе дать XML-документ с параметром encoding=»windows-1251″ вот так:

<?php // Создание объекта документа из текстовой строки $xmldoc = domxml_open_mem('<?xml version="1.0" encoding="windows-1251"?>'); // Cсылку на корневой узел документа записываем в переменную $my_root (название //переменной значения не имеет). $my_root = $xmldoc->document_element(); // Создаём текстовый узел. $my_element = $xmldoc->create_text_node(iconv("windows-1251", "UTF-8", "Это содержимое корневого узла XML-документа.")); // Присоединяем текстовый узел к корневому. $my_root->append_child($my_element); print($xmldoc->dump_mem()); ?> Следующий пример показывает, как можно удалять элементы: <?php $xmldoc = domxml_open_mem('<?xml version="1.0" encoding="windows-1251"?> Нечто '); $my_root = $xmldoc->document_element(); // В массив $children записываются все потомки узла my_root. $children = $my_root->child_nodes(); // Уничтожается первый потомок (узел something) $children[0]->unlink_node(); // Создаётся новый узел под названием new и записывается в переменную $new $new = $my_root->append_child($xmldoc->create_element("new")); // К этому узлу добавляется потомок — текстовый узел $new->append_child($xmldoc->create_text_node(iconv("windows-1251", "UTF-8", "Это содержимое нового узла XML-документа."))); print($xmldoc->dump_mem()); ?>

в результате получится такой XML-документ:

<?xml version="1.0" encoding="windows-1251"?> <my_root><new>Это содержимое нового узла XML-документа.</new></my_root> Кстати, если изменить исходный XML-документ на такой: <?php $xmldoc = domxml_open_mem('<?xml version="1.0" encoding="windows-1251"?> Нечто '); ?>

Результат будет иным:

<?xml version="1.0" encoding="windows-1251"?> <my_root><something>Нечто</something> <new>Это содержимое нового узла XML-документа.</new></my_root>

Предоставляю вам возможность догадаться, почему это произошло.

Конечно же, приведённые примеры — самое простое из того, что можно делать в DOM XML. Кроме построения нового это и самые хитрые преобразования документа, и XSL-трансформация при помощи библиотеки libxslt, не уступающей в функциональности Sablotron, а в скорости превосходящей его в два раза. Перед нами открываются огромные возможности по работе с документом, проблема — как организовать и систематизировать эту работу.

На ум приходит следующая схема преемника классов шаблонов: вызывается скрипт, который открывает стандартный XML-файл и включает буферизацию данных. Все скрипты тупо выдают XML-данные в print. Вызывается второй скрипт, который останавливает буферизацию, берёт данные из буфера, дописывает к ним в начале «<?xml version=»1.0″ encoding=»windows-1251″?><root>» и «</root>» в конце, затем преобразует в объект DOM, открывает корневой элемент и берёт всех массив потомков. Полученные узлы вставляет в основной XML-документ (который тоже открыт как объект), результат преобразует через XSLT и выдаёт пользователю.

На этом заканчиваем краткое описание DOM XML и переходим к классу sql2xml.
Класс SQL2XML

Вся функциональность, которая нужна для преобразования результатов SQL-запросов в XML, есть в этом классе. Для соединения с базой данных класс использует либо существующее соединение класса PEAR::DB, либо своё собственное (точнее, он создаёт в себе объект класса DB). Из результата запроса строится XML-дерево. Пример из руководства по классу:

mysql> select * from bands;
+—-+—————+————+————-+————-+
| id | name | birth_year | birth_place | genre |
+—-+—————+————+————-+————-+
| 1 | The Blabbers | 1998 | London | Rock’n'Roll |
| 2 | Only Stupids | 1997 | New York | Hip Hop |
+—-+—————+————+————-+————-+

mysql> select * from albums;
+—-+———+——————+——+——————+
| id | bandsID | title | year | comment |
+—-+———+——————+——+——————+
| 1 | 1 | BlaBla | 1998 | Their first one |
| 2 | 1 | More Talks | 2000 | The second one |
| 3 | 2 | All your base… | 1999 | The Classic |
+—-+———+——————+——+——————+

Это набор данных. А теперь вызов класса и результаты работы. php-код:

<?php include_once("XML/sql2xml.php"); $sql2xmlclass = new xml_sql2xml("mysql://username:password@localhost/xmltest"); $xmlstring = $sql2xmlclass->getxml("select * from bands"); ?>

XML-документ:

<?xml version="1.0"?> <root> <result> <row> <id>1</id> <name>The Blabbers</name> <birth_year>1998</birth_year> <birth_place>London</birth_place> <genre>Rock'n'Roll</genre> </row> <row> <id>2</id> <name>Only Stupids</name> <birth_year>1997</birth_year> <birth_place>New York</birth_place> <genre>Hip Hop</genre> </row> </result> </root>

Результат выводится и в текстовом виде, и как DOM-объект (что весьма удобно при генерации документов через DOMXML). Так же можно из всего XML-дерева выдернуть одно значение при помощи выражения XPath. Ещё очень хорошая особенность: раз уж строятся деревья, и всё оперируется в XML, почему бы запросы с объединением «один-ко-многим» не делать в виде вложенных друг в друга узлов <row>. php-код:

<?php include_once("XML/sql2xml.php"); $sql2xml = new xml_sql2xml("mysql://username:password@localhost/xmltest"); $xmlstring = $sql2xml->getxml("select * from bands left join albums on bands.id = bandsID"); ?>

XML-документ:

<?xml version="1.0"?> <root> <result> <row> <id>1</id> <name>The Blabbers</name> <birth_year>1998</birth_year> <birth_place>London</birth_place> <genre>Rock'n'Roll</genre> <row> <id>1</id> <bandsID>1</bandsID> <title>BlaBla</title> <year>1998</year> <comment>Their first one</comment> </row> <row> <id>2</id> <bandsID>1</bandsID> <title>More Talks</title> <year>2000</year> <comment>The second one</comment> </row> </row> <row> <id>2</id> <name>Only Stupids</name> <birth_year>1997</birth_year> <birth_place>New York</birth_place> <genre>Hip Hop</genre> <row> <id>3</id> <bandsID>2</bandsID> <title>All your base...</title> <year>1999</year> <comment>The Classic</comment> </row> </row> </result> </root>

Впрочем, если вы хотите получить обычный результат запроса, это свойство можно отключить. Если имена узлов для результата и для ряда вас не устраивают, можно их поменять. Если вас не устраивает формат (всё в текстовых узлах, а не, например, в атрибутах), можно преобразовать полученный DOM-объект в нужный вам. На мой взгляд, этого не понадобится, поскольку если на сайте до этого уже использовался XSLT, исправить XSL-файл не представляет особой сложности.

Итак, класс вполне пригоден к использованию. Если он вас чем-то не устраивает, можно, глядя на существующий, написать свой собственный. Исправлять данный класс вполне можно, поскольку манипуляции с DOM-объектами не намного сложнее внутреннего устройства классов шаблонов. Я для себя исправил ошибки call-time pass-by-reference в классе версии 0.3 (версия 0.3.1 — это как раз мой багфикс) а так же заменил старые не поддерживаемые функции и конструкции DOM XML на новые. Сейчас работаю над тем, как справиться с проблемой кодировки документа (объект документа там создаётся функцией domxml_new_doc, а для создания из текстовой строки требуется основательно переделать существующую в классе sql2xml функцию).

Источник: internet-technologies.ru

Вы можете оставить комментарий, или обратную ссылку на Ваш сайт.

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

Похожие статьи