PHP-4 Эффективная работа


Что такое XML и как с ним работать?
XML и Expat
Функции XML-анализатора
Практический подход к анализу XML-документов

18.4.Практический подход к анализу XML- документов

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

Все, что нам потребуется, приведено в табл. 18.4. Этот набор тегов представляет собой развитие стиля документов новостной ленты NewsBoy, которая используется на сервере http://jusgrant.cjb.net/.

Таблица 18.4. Список тегов новостной ленты Newswire
Наименование тега Его назначение
1date Дата публикации на сервере
2nате Ключевые термины
3 newline Разделение абзацев
4 pic Иллюстрация
5 slug Заголовок (тема) сообщения
6source Указание на источник данных
7 story Границы заметки
8 text Собственно текст сообщения

Теперь для представления новостей мы можем воспользоваться форматом вида:

<?xml version="l.0" standalone="no"?>
<!DOCTYPE NewsWire SYSTEM "NewsWire.dtd">
     <NewsWi re>
       <story>
          <date>14/05/2001</date>
          <slug>Фотонная система связи</slug>
          <text>
               <name>DERA</name>, военное научное агентство МО
               Великобритании, разработало и проверило на практике
               оригинальную систему безопасной передами сообщений
               между спутниками и наземными станциями. Она
               использует в качестве переносчиков информации фотоны,
               кодирующие отдельные биты посылки. Так как каждый
               фотон, очевидно, может быть принят только одним
               физическим устройством (после чего перестает
               существовать), любая попытка незаметного перехвата
               сообщений будет зафиксирована. Данная система станет
               применяться в целях контроля за безопасностью канала
               связи и для передачи секретных цифровых ключей на
               основе "квантовой криптографии".
          </text>
          <source>PC-Week/RE 14. 2001</source>
       </story>
       ...
     </NewsWire>

Итак, что мы видим? Нам удалось полностью отказаться от тегов HTML, заменив их всего восемью инструкциями XML Оказалось, что их по силам запомнить даже машинистке, которой можно поручить готовить новости путем заполнения готовой формы. Теперь нам остается только научить РНР обращаться с этой информацией. Учтем, что по умолчанию РНР 4 конфигурируется со встроенной поддержкой анализатора XML, поэтому никаких дополнительных мер по конфигурированию РНР- машины обычно принимать не надо.

Но еще на минуту вернемся к формату XML-файла. Первая строка указывает, что этот файл оформлен в формате XML. Воспринимайте ее просто как некоторое «заклинание», которое можно использовать не задумываясь. Обратите только внимание на параметр standalone. Он означает, что определение формата расположено во внешнем файле DTD, имя которого указывается в следующей строке. Объявление DOCTYPE содержит ссылку на корневой элемент XML-документа, которым в нашем случае является NewsWi ге, а также адрес, по которому можно найти определение формата. Хитрость заключается в том, что никакого файла DTD мы создавать не будем! Причина очень проста: библиотека Expat, входящая в состав РНР, просто не поддерживает операцию проверки документа на соответствие DTD.

примечание
       Зачем же тогда я включил ссылку на DTD-документ? Все очень просто. Это требуется стандартом XML. А поскольку анализатор не будет автоматически контролировать целостность документа, мы сделаем это сами.

Теперь нам остается только настроить анализатор таким образом, чтобы распознавать и обрабатывать теги, приведенные в табл. 18.3. Для этого мы вначале определим класс, который будет фактически представлять собой надстройку над анализатором из Expat. Его определение (xml.php) приведено в листинге 18.5.

Листинг 18.5. Определение класса newswire (xml.php)
<?php
/*
     NewsWire: определение новостного канала, построенного на основе
                  системы NewsBoy, разработанной Justin Grant
               (Web: jusgrant.cjb.net or justin.host.za.net
          Mail: justin@glendale.net)
*/
class newswire
{
       var $xml_parser;
       var $xml_file;
       var $html;
       var $open_tag;
       var $close_tag;

     // Конструктор класса
     function newswire ()
     {
     $this->xml_parser = "";
     $this->xml_file = "";
     $this->html = "";
     $this->open_tag = array(
     // Настраиваем интерпретатор отдельных
     // тегов нашего заказного формата
     "NEWSWIRE" => "<TABLE COLS=1 CELLPADDING=5>",
     "STORY" => "<TR><TD BGCOLOR=#f2f2f2>",
     "DATE" => "<FONT COLOR=#8B3B00>",
     "SLUG" => "<FONT COLOR=#000A0C><B> ",
     "TEXT" => "<FONT COLOR=#3F3F00>" ,
     "SOURCE" => "<FONT SIZE = -1><FONT COLOR=#D22323>",
     "PIC" => "<IMG SRC=\"",
     "NAME" => "<B>",
     "NEWLINE" => "");
     // а здесь - закрывающие теги
     $this->close_tag = array(
     "NEWSWIRE" => "</TABLE>\n\n" ,
     "STORY" => "</TD></TR>",
     "DATE" => "",
     "SLUG" => </B><BR>",
     "TEXT" => "\n",
     "SOURCE" => "</FONT></FONT>",
     "PIC" => "\" align=right hborder=10 vborder=10>",
     "NAME" => "</B>",
     "NEWLINE" => "<BR>");
     }
// Деструктор класса. В отличие от Си++ и Perl его
// необходимо вызывать вручную
function destroy()

xml_parser_free( $this->xml_parser);

//Основные функции введенного нами класса
function concat($str)

// дописывает в генерируемый HTML-поток очередную строку
$this-> html .= $str;

// Вызывается при начале нового элемента в документе
function startElement( $parser, $name, $attrs)

if ($format= $this->open_tag[$name])

$this-> html .= $format;
          }
     }
// Вызывается при окончании обработки элемента
function endElement ($parser , $name )
{
     global $close_tag;
     if ($format= $this->close_tag[$name] )
       {
          $this-> html .= $format;
          }
     }
// Обработчик символьных данных
function characterData( $parser, $data )
     {
     $this-> html .= $data;
     }
     //
// Разбор файла
//
function parse()
{
     $this-> xml_parser = xml_parser_create() ;
     xml_set_object ($this->xml_parser , &$this) ;
// обратите внимание на нивелирование регистров тегов
// что позволяет при любом раскладе установить соответствие
// их записям $map_array
xml_parser_set_option($this->xml_parser,
                                        XML_OPTION_CASE_FOLDING, true );
xml_set_element_handler ($thi s->xml_parser,
                                        "startElement" , "endElement" );
xml_set_character_data_handler ($thi s->xml_parser,
                                              "characterData") ;
if (!($fp = fopen($this->xml_file, "r")))

     die("He могу открыть входной файл XML");
while ($data = fread( $fp, 4096»
     {
     if ( !xml_parse($this->xml_parser , $data, feof($fp)))
       { die( sprintf ("Ошибка в XML файле : %s на строке %d",
               xml_error_string( xml_get_error_code(
                                                  $this->xml_parser
               xml_get_current_line_number($this->xml_parser)) )
                              }
                         }
                    }
               }
               ?>

Давайте посмотрим, как устроен этот класс. Прежде всего обратите внимание на два массива open_tag и close_tag в конструкторе. Ключи соответствуют именам тегов, а значения — фрагментам кода HTML, который должен подставляться вместо них при преобразовании XML-файла в HTML-формат.

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

Поскольку SAX-анализаторы ориентированы на события, нам остается только вписать обработчики тех событий, которые нас интересуют. В принципе, Expat позволяет распознать и обработать около десятка различных событий, но нам потребуется только три из них: обнаружение нового элемента и его завершение, а также обработчик фрагмента, представляющего собой текстовые данные (CDATA в терминах XML). Эти события мы будем обрабатывать методами startElement, closeElement и characterData соответственно.

Первые два метода устроены достаточно просто. Если обнаруженному элементу соответствует запись в массиве open_tag или close_tag, то генерируемую страницу подставляется соответствующее значение. Третий метод еще проще: все символьные данные, поступившие из анализатора, передаются в выходную страницу без изменений.

Теперь перейдем к основному методу — собственно обертке анализатора parse (). Вначале с помощью вызова xml_parser_create() осуществляется создание экземпляра анализатора Expat, который мы помещаем в поле xml_parser нашего класса. Затем нам необходимо зарегистрировать нашу надстройку над анализатором, что выполняется с помощью вызова: xml_set_object($this->xml_parser, &$this)

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

Затем производится настройка параметров анализатора. В данном случае используется только один ключ — XML_OPTION_CASE_FOLDING, который заставляет анализатор игнорировать регистр, в котором введены теги. После этого нам остается только Подключитьобработликисобытийспомощью функциии xml_sеt_element_handler(). Как видно из примера, мы ограничиваемся всего двумя функциями — startElement и endElement. Осталось добавить всего один необходимый обработчик — тот, который предназначен для обработки полей CDATA XML-документа. Для этого мы используем специальную функцию xml_set_character_data_handler () и подключаем наш обработчик — characterData(), который попросту транслирует данные в HTML-файл.

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

Теперь давайте попробуем использовать созданный нами анализатор. Во-первых нам необходимо поместить созданную нами библиотеку xml.php в место, доступ- ное для PHP-машины. В примере я использовал текущий каталог, но это вовсе не обязательно. Дальше нам остается сформировать небольшую программу:

<?php
     # Файл $doc_root/php/part3/xml-php.php

     require("xml .php") ;

     # создаем новый объект
     $news = new newswire();
     # ассоциируем с ним файл новостей
     $news->xml_file = "mynews . xml" ;

     $news->parse() ;           // разбираем новости

     # Выводим заголовок
     echo "<h2 align=center> Текущие новости </h2>";

     # а затем синтезируем HTML-страницу
     print ($news->html) ;
     # освобождаем ресурсы
     $news->destroy () ;
     ?>

И все! В результате обращения к этому файлу через Apache вы получите страницу, представленную на рис. 18.3.


Рис. 18.3. Наш новостной канал

Итак, чего же мы добились? Прежде всего нам удалось значительно упростить процесс подготовки типовых данных и отказаться от назойливого повторения одних и тех же HTML-тегов. Кроме того, теперь вы можете самостоятельно развить предложенное решение, например:

  • создать единый заголовок, автоматически формируемый при открытии документа;
  • дополнить тег <pic> автоматическим счетчиком, чередующим выравнивание картинок по левому и правому краям окна;
  • при каждом открытии элемента story связать с ним уникальную для документа метку («якорь»), что облегчит использование поисковых машин при индексации документов.

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

назад
далее

Сайт управляется системой uCoz