Статьи

PHP и XML: анализ статьи RSS 1.0

XML появляется во всем Интернете как средство для создания стандартных форматов данных для обмена информацией между системами, независимо от их платформы или технологии. Как вы, возможно, уже знаете, XML позволяет вам определять свои собственные языки разметки, похожие на HTML и подходящие для любых данных, которые вы хотите представить. Ряд стандартных языков разметки на основе XML был создан для облегчения обмена общими типами информации. В этой статье мы узнаем, как использовать PHP для чтения XML-документа и отображения данных, которые он содержит, в виде веб-страницы. Пример, который мы будем использовать, представляет собой документ с кратким описанием сайта (RSS) 1.0 структуры описания ресурсов (RDF), хотя представленные здесь методы применимы к любой ситуации, когда вы хотите проанализировать данные XML в сценарии PHP.

Краткий тур по RSS 1.0

RSS (ранее обозначавший Rich Site Summary, разработанный Netscape, но теперь ссылающийся на RDF Site Summary, обновленную и XML-совместимую версию технологии Netscape) — это формат XML-документа, предназначенный для описания, обобщения и распространения содержимого сети. сайт как «канал». Такие сайты, как MoreOver.com и Meerkat O’Reilly, обрабатывают RSS-каналы, предоставляемые новостными и другими контентными сайтами, и предоставляют комбинированные сервисы новостей. RSS в настоящее время разрабатывается рабочей группой RSS-DEV .

Как и в большинстве форматов XML-документов, значение документа можно легко определить, просто просматривая образец документа. SitePoint.com предоставляет резюме своих статей на первой странице в формате RSS по адресу http://www.sitepoint.com/rss.php . Если вы используете Internet Explorer 5 или более поздней версии, вы можете просмотреть текущую версию этого XML-документа прямо в браузере. Для всех остальных, вот текущий файл RSS SitePoint.com на момент написания этой статьи:

<?xml version="1.0" encoding="utf-8"?> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://purl.org/rss/1.0/">

<канал rdf: about = ”http://www.sitepoint.com/rss.php”>
<Название> SitePoint.com </ название>
<description> Овладей Интернетом! </ description>
<Ссылка> http://www.sitepoint.com/ </ ссылка>

<товар>
<RDF: Seq>
<rdf: li rdf: resource = ”http://www.PromotionBase.com/article/551 ″ />
<rdf: li rdf: resource = ”http://www.WebmasterBase.com/article/541 ″ />
<rdf: li rdf: resource = ”http://www.eCommerceBase.com/article/552 ″ />
<rdf: li rdf: resource = ”http://www.eCommerceBase.com/article/505 ″ />
<rdf: li rdf: resource = ”http://www.PromotionBase.com/article/556 ″ />
<rdf: li rdf: resource = ”http://www.eCommerceBase.com/article/508 ″ />
</ RDF: Seq>
</ Пункты>
</ Канал>

<item rdf: about = ”http://www.PromotionBase.com/article/551 ″>
<title> Кэширование в поисковой системе Escape </ title>
<описание> Знаете ли вы, что многие поисковые системы кэшируют ваши страницы?
Хотя эта практика может ускорить поиск, пользователи могут не видеть ваши
последние обновления сайта! Ральф показывает, как вы можете остановить поисковые системы
кэширование ваших страниц. </ description>
<Ссылка> http://www.PromotionBase.com/article/551 </ ссылка>
</ Элемент>

<item rdf: about = ”http://www.WebmasterBase.com/article/541 ″>
<title> Добавить JavaScript в Fireworks </ title>
<description> Нужен ли вашему дизайну больше визажа? Добавить интерактивность к
Ваш сайт без изучения JavaScript! Мэтт объясняет создание
JavaScript-эффекты в Fireworks и подробно исследует использование
инструменты этой программы. </ description>
<Ссылка> http://www.WebmasterBase.com/article/541 </ ссылка>
</ Элемент>

<item rdf: about = ”http://www.eCommerceBase.com/article/552 ″>
<title> Отправка электронных кампаний за 8 шагов — часть 2 </ title>
<описание> Хорошо, значит, вы ошарашены своими перспективами и они
ваш список рассылки. Что теперь? Как вы эффективно общаетесь, и
превратить их в клиентов? Джейсон раскрывает все … </ description>
<Ссылка> http://www.eCommerceBase.com/article/552 </ ссылка>
</ Элемент>

<item rdf: about = ”http://www.eCommerceBase.com/article/505 ″>
<title> Необходимость письменного контракта на сайте </ title>
<описание> Письменное соглашение необходимо, если вы платите другим
разрабатывать, создавать или поддерживать ваши веб-сайты. Иван объясняет необходимость
контрактов для тех, кто работает в Интернете. </ description>
<Ссылка> http://www.eCommerceBase.com/article/505 </ ссылка>
</ Элемент>

<item rdf: about = ”http://www.PromotionBase.com/article/556 ″>
<title> Стратегии поисковых систем 2001 — Отчет о конференции </ title>
<описание> Гэвин Аппель из Sinewave Interactive рассказывает Мэтту о
В этом году конференция по стратегиям поисковых систем. Он обрисовывает в общих чертах
обсуждения и прогнозы лидеров отрасли. </ description>
<Ссылка> http://www.PromotionBase.com/article/556 </ ссылка>
</ Элемент>

<item rdf: about = ”http://www.eCommerceBase.com/article/508 ″>
<title> Лучший вопросник для электронной коммерции </ title>
<description> Пересмотрите свою стратегию электронной коммерции сейчас! Лицом к
сложные вопросы с Ли, поскольку он проведет вас через простой процесс
оптимизировать вашу стратегию электронной коммерции. </ description>
<Ссылка> http://www.eCommerceBase.com/article/508 </ ссылка>
</ Элемент>

</ ТТО: RDF>

Как видите, файл начинается с <channel> который содержит заголовок, описание и URL-адрес сайта, который описывает файл RSS, а также список <items> который в данный момент содержит канал. Затем за этим тегом следует тег <item> для каждой из статей, которые появляются на первой странице SitePoint.com. Для каждого предоставляются заголовок, описание и URL. Следует отметить, что это простой RSS-файл — многие сайты используют стандартные расширения для формата RSS, чтобы включать такие вещи, как имена авторов, изображения и даты публикации для элементов в их канале, но для целей этого статья, которую сделает этот основной файл RSS.

Теперь, поскольку большинство веб-браузеров не могут читать страницы XML и браузеры, которые по умолчанию могут отображать только код страницы (Internet Explorer 5+) или текстовые части страницы (Netscape 6+), вам нужна промежуточная технология превратить этот документ RSS в нечто презентабельное, если вы хотите показать его пользователям. Другие возможности включают в себя чтение файла и сохранение заголовков в базе данных или отправку по электронной почте подписанным пользователям, если в описании новых статей появляются определенные ключевые слова. В любом случае вам понадобится что-то, что может читать XML. В этой статье будет рассмотрено использование PHP для анализа XML-документа.

PHP берет XML

Существует два широко используемых метода языков программирования для чтения XML-документов: API-интерфейсы на основе событий и API-интерфейсы объектной модели документов (DOM) . В последнем классе API XML-документы полностью считываются в память и затем могут управляться с помощью набора функций, которые обеспечивают доступ к объектно-ориентированной модели документа (DOM) в памяти. DOM API, как правило, считаются более мощными; однако у них есть один серьезный недостаток: они плохо подходят для обработки больших XML-документов, что потребует слишком много памяти для построения модели документа.

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

Вот основной код для настройки функций обработки событий и анализа (чтения) XML-документа в PHP:

// Create an XML parser $xml_parser = xml_parser_create();

// Устанавливаем функции для обработки открывающих и закрывающих тегов
xml_set_element_handler ($ xml_parser, «startElement», «endElement»);

// Установить функцию для обработки блоков символьных данных
xml_set_character_data_handler ($ xml_parser, «characterData»);

// Открыть файл XML для чтения
$ fp = fopen («http://www.sitepoint.com/rss.php», «r»)
или умри («Ошибка чтения данных RSS.»);

// Чтение XML-файла 4 КБ за раз
while ($ data = fread ($ fp, 4096))
// Анализируем каждый блок размером 4 КБ с помощью синтаксического анализатора XML, созданного выше
xml_parse ($ xml_parser, $ data, feof ($ fp))
// Обработка ошибок при разборе
или умереть (sprintf («Ошибка XML:% s в строке% d»,
xml_error_string (xml_get_error_code ($ XML_Parser)),
xml_get_current_line_number ($ XML_Parser)));

// Закрыть файл XML
fclose ($ FP);

// Освободить память, используемую анализатором XML
xml_parser_free ($ XML_Parser);

Каждая из строк приведенного выше кода прокомментирована, чтобы объяснить, что они делают, но давайте посмотрим на функции PHP, связанные с XML, которые используются в приведенном выше коде, немного подробнее:

  • xml_parser_create() Создает анализатор XML. Точно так же, как вы должны создать соединение с базой данных в PHP, если вы хотите взаимодействовать с базой данных, вы должны создать синтаксический анализатор XML для использования, когда вы хотите читать в файле XML. В приведенном выше примере ссылка на анализатор хранится в $xml_parser .
  • xml_set_element_handler( parser , startElementFunction , endElementFunction ) Эта функция указывает функции, которые синтаксический анализатор XML должен использовать для обработки событий, созданных открывающими и закрывающими тегами. В этом случае парсер хранится в нашей переменной $xml_parser , а функции называются startElement и endElement . Эти функции будут определены в других местах PHP-скрипта (я приведу пример ниже).
  • xml_set_character_data_handler( parser , characterDataFunction ) Эта функция указывает функцию, которую анализатор XML должен использовать для обработки символьных данных, появляющихся между тегами в документе XML. Еще раз мы используем нашу переменную $xml_parser в примере выше. Функция, которую мы выбираем для обработки символьных данных, называется characterData .
  • xml_parse( parser , data , endOfDocument ) Эта функция отправляет весь или часть XML-документа парсеру для обработки. Параметр endOfDocument должен иметь значение true если данные отмечают конец XML-документа, или значение false если за последующим вызовом xml_parse последует больше документа. Это позволяет анализатору правильно отлавливать незакрытые теги в конце документа и так далее. В нашем примере синтаксическим анализатором снова является $xml_parser . Переменная $data (размером до 4 КБ), извлеченная из файла с fread , передается в качестве данных для обработки, в то время как функция feof используется для определения того, достиг ли PHP конец файла XML или нет, таким образом обеспечивая обязательный параметр endOfDocument . Если при синтаксическом анализе документа возникает ошибка, мы распечатываем сообщение об ошибке и строку файла, в которой это происходит, с xml_error_string , xml_get_error_code и xml_get_current_line_number , которые подробно описаны в руководстве по PHP, если вам интересно ,
  • xml_parser_free( parser ) Хотя все ресурсы памяти освобождаются в конце скрипта PHP, вы можете захотеть освободить память, используемую синтаксическим анализатором XML, если ваш скрипт выполнит другие потенциально ресурсоемкие задачи после анализа данных XML. Эта функция уничтожает указанный синтаксический анализатор XML, освобождая ресурсы и память, которые он мог выделить для анализа.

Есть несколько дополнительных функций, которые позволяют вам обрабатывать некоторые из более эзотерических событий, возникающих во время синтаксического анализа XML, но они хорошо документированы в руководстве по PHP, поэтому я оставлю их для вас, чтобы они читали, если это требуется для вашего конкретного приложения. Для наших целей (чтение файла RSS) у нас теперь есть все, что нам нужно. startElement только написать три функции обработки событий: startElement , endElement и characterData .

Обработчики событий для разбора RSS

У нас есть три функции для записи. Каждая из этих функций должна принимать определенные параметры. Эти параметры продиктованы PHP, поскольку их будет вызывать синтаксический анализатор PHP. Вот атрибуты, которые вы должны определить для каждой из этих функций:

startElement($parser, $tagName, $attrs)

  • $parser будет передана ссылка на синтаксический анализатор XML, который используется для анализа документа.
  • $tagName — это ALL-UPPERCASE (в руководстве по PHP это называется «сложенным регистром») версии имени открывающего тега, который вызвал событие.
  • $attrs — это ассоциативный массив атрибутов, присутствующих в теге, вызвавшем событие. Например, если тег <body bgcolor="#FFFFFF"> вызвал событие, тогда значение $attrs['BGCOLOR'] будет равно "#FFFFFF" . Обратите внимание, что, как и имя тега, имена атрибутов складываются в регистр (все в верхнем регистре).

endElement($parser, $tagName)

  • $parser будет передана ссылка на синтаксический анализатор XML, который используется для анализа документа.
  • $tagName — это сложенное в регистр имя закрывающего тега, который вызвал событие.

characterData($parser, $data)

  • $parser будет передана ссылка на синтаксический анализатор XML, который используется для анализа документа.
  • $data — это строка текста между тегами XML в документе. Текст между двумя тегами не обязательно будет вызывать одно событие. Блоки текста, распределенные по нескольким строкам, будут вызывать одно событие в каждой строке, причем каждому событию передаются $data для этой строки.

Имея это в виду, процесс преобразования XML-данных для RSS-файла SitePoint в просматриваемый HTML-документ может показаться довольно простым на первый взгляд. Если вы остановитесь и попытаетесь понять, что должны делать три функции обработки событий, вы быстро поймете, что это не так просто, как кажется. Для тех из вас, кто может чувствовать себя потерянным на этом этапе, не волнуйтесь. Поиск определений для этих функций, которые будут обрабатывать RSS-файл SitePoint (или, действительно, RSS-файл любого сайта), должен помочь понять все это.

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

В случае нашего файла RSS вся необходимая нам информация о статьях на титульной странице SitePoint содержится в тегах <item> в документе. Поэтому первой глобальной переменной, которую мы определим, будет $insideitem , для которой мы установим значение true при вводе <item> и false при выходе из него. Мы также определим четыре другие переменные, цели которых станут ясны по мере продвижения вперед:

$insideitem = false; $tag = ""; $title = ""; $description = ""; $link = "";

Начнем с startElement . Эта функция будет вызываться анализатором XML всякий раз, когда встречается открывающий тег. Поскольку нас действительно интересует, что происходит между тегами <item> , мы сначала проверим, действительно ли мы находимся внутри <item> :

function startElement($parser, $tagName, $attrs) { global $insideitem, $tag; if ($insideitem) {

Обратите внимание на глобальный оператор в начале функции, который указывает, что этой функции потребуется доступ к глобальным переменным $insideitem и $tag . Теперь, если $insideitem имеет значение true , это означает, что мы захотим принять к сведению $insideitem тег, чтобы мы знали, что делать с символьными данными, содержащимися в нем, что приведет к следующему вызову characterData . Поэтому мы записываем имя тега ( $tagName ) в нашу глобальную переменную $tag :

$tag = $tagName;

Если, с другой стороны, мы не находимся внутри <item> , то единственным открывающим тегом, который может нас заинтересовать, будет фактический <item> , и в этом случае мы установим для $insideitem значение true для указать, что мы вводим один из этих тегов:

} elseif ($tagName == "ITEM") { $insideitem = true; } }

Обратите внимание, что мы проверяем, имеет ли $tagName "ITEM" , поскольку имена тегов $tagName в верхний регистр.

Это делает это для открытия тегов. Следующим шагом в разборе нашего документа RSS является обработка символьных данных, которые появляются между тегами, и это работа нашей функции characterData :

function characterData($parser, $data) { global $insideitem, $tag, $title, $description, $link;

Эта функция требует доступа ко всем пяти нашим глобальным переменным, как мы скоро увидим. Теперь, как и прежде, единственный раз, когда мы заинтересованы в символьных данных в XML-файле, это когда мы находимся внутри <item> , поэтому сначала снова нужно проверить, так ли это:

if ($insideitem) {

Теперь есть три разных тега, которые могут появляться внутри тегов <item>, которые нас интересуют: <title> , <description> и <link> . Теперь, так как мы хотим отобразить заголовок каждой статьи над ее описанием и со ссылкой на URL, указанный в <link> , мы не можем просто вывести символьные данные, как они встречаются в файле XML. Вместо этого нам нужно собрать все данные для каждого <item> а затем распечатать все сразу. Наши глобальные переменные $title , $description и $link будут использоваться именно для этой цели. Мы будем использовать оператор switch чтобы определить, с каким тегом мы имеем дело, и сохранить $data в соответствующей переменной. Напомним, что имя текущего тега хранится в глобальной переменной $tag .

switch ($tag) { case "TITLE": $title .= $data; break; case "DESCRIPTION": $description .= $data; break; case "LINK": $link .= $data; break; } } }

Обратите внимание, что мы добавляем ( .= ) $data к рассматриваемой переменной, а не просто присваиваем ее ( = ), поскольку содержимое одного тега может быть получено как несколько последовательных событий characterData .

После обработки символьных данных для тега следующее событие будет вызывать нашу функцию endElement чтобы указать закрывающий тег. В этом приложении единственным тегом, который потребует действий с нашей стороны после его закрытия, является <item> . При обнаружении </item> мы получим все данные $title , $description и $link для элемента, и затем мы можем вывести его в виде HTML:

function endElement($parser, $tagName) { global $insideitem, $tag, $title, $description, $link; if ($tagName == "ITEM") { printf("<p><b><a href='%s'>%s</a></b></p>", trim($link),htmlspecialchars(trim($title))); printf("<p>%s</p>",htmlspecialchars(trim($description)));

Не стесняйтесь использовать операторы echo если вы не привыкли к более удобной printf я использовал выше. В любом случае, после вывода URL-адреса, заголовка и описания <item> , вы можете очистить глобальные переменные, чтобы они были готовы получить символьные данные для следующего <item> в документе:

$title = ""; $description = ""; $link = "";

И, наконец, установите $insideitem в false чтобы указать другим нашим функциям, что мы больше не внутри <item> .

$insideitem = false; } }

Это оно! Чтобы увидеть этот скрипт в действии, нажмите здесь . Вы также можете просмотреть полный исходный код (используйте команду просмотра исходного кода в браузере, если исходный код не отображается в виде текстового файла).

Еще одна ОПЦИЯ

Более опытные программисты в аудитории, возможно, съежились, как только я упомянул об использовании глобальных переменных. Существует точка зрения, что глобальные переменные являются просто признаком ленивого программирования, и, действительно, возможности объектно-ориентированного программирования (ООП) в PHP предоставляют лучший вариант. Вот альтернативная версия скрипта, который мы только что разработали. Вместо функций для обработки событий XML-документа мы используем методы объекта PHP. Данные, которые эти методы должны совместно использовать, могут быть сохранены как переменные экземпляра объекта, что устраняет необходимость в глобальных переменных в нашем скрипте.

class RSSParser {

var $ insideitem = false;
var $ tag = «»;
var $ title = «»;
var $ description = «»;
var $ link = «»;

function startElement ($ parser, $ tagName, $ attrs) {
if ($ this-> insideitem) {
$ this-> tag = $ tagName;
} elseif ($ tagName == «ITEM») {
$ this-> insideitem = true;
}
}

function endElement ($ parser, $ tagName) {
if ($ tagName == «ITEM») {
printf («<p> <b> <a href=’%s’>% s </a> </ b> </ p>»,
отделка ($ this-> ссылка), htmlspecialchars (облицовка ($ this-> название)));
Е ( «<р>% s </ р>»,
htmlspecialchars (облицовка ($ this-> описание)));
$ this-> title = «»;
$ this-> description = «»;
$ this-> link = «»;
$ this-> insideitem = false;
}
}

function characterData ($ parser, $ data) {
if ($ this-> insideitem) {
switch ($ this-> tag) {
кейс «TITLE»:
$ this-> title. = $ data;
перемена;
кейс «ОПИСАНИЕ»:
$ this-> description. = $ data;
перемена;
кейс «ССЫЛКА»:
$ this-> link. = $ data;
перемена;
}
}
}
}

$ xml_parser = xml_parser_create ();
$ rss_parser = new RSSParser ();
xml_set_object ($ XML_Parser, & $ rss_parser);
xml_set_element_handler ($ xml_parser, «startElement», «endElement»);
xml_set_character_data_handler ($ xml_parser, «characterData»);
$ fp = fopen («http://www.sitepoint.com/rss.php», «r»)
или умри («Ошибка чтения данных RSS.»);
while ($ data = fread ($ fp, 4096))
xml_parse ($ xml_parser, $ data, feof ($ fp))
или умереть (sprintf («Ошибка XML:% s в строке% d»,
xml_error_string (xml_get_error_code ($ XML_Parser)),
xml_get_current_line_number ($ XML_Parser)));
fclose ($ FP);
xml_parser_free ($ XML_Parser);

Если вы знакомы с объектно-ориентированным программированием на PHP, то единственная строка, которая может быть вам источником беспокойства, заключается в следующем:

xml_set_object($xml_parser,&$rss_parser);

Поскольку xml_set_element_handler и xml_set_character_data_handler не могут принимать ссылки на методы объектов в качестве имен функций обработчиков событий (т.е. xml_set_element_handler($xml_parser,"$rss_parser->startElement",... не будет работать) синтаксический анализатор XML для вызова методов объекта $rss_parser вместо базовых функций. xml_set_object делает именно это, xml_set_object синтаксический анализатор и ссылку (обратите внимание на & ) на объект, методы которого вы хотите, чтобы он вызывал.

Загрузите полный код этой объектно-ориентированной версии здесь .

Резюме и ресурсы для дальнейшего чтения

В этой статье мы узнали, как анализировать XML-документы (на конкретном примере документа RSS 1.0) с использованием PHP. Хотя его основанный на событиях API для обработки документов XML может потребовать некоторого размышления, чтобы определить, как обрабатывать даже простую структуру документа XML, мы увидели, что размышление о событиях, происходящих при обработке документа, может помочь определить, что каждый из методы обработки событий должны делать.

В полностью закодированном примере я продемонстрировал, как анализировать RSS-файл SitePoint, в котором перечислены сопроводительные статьи на SitePoint.com в любой момент времени. Не стесняйтесь адаптировать этот код для использования на вашем сайте, чтобы отображать текущие заголовки на SitePoint.com в любом формате, который подходит вашему сайту! Для тех, кто занимается программированием, я предоставил альтернативную версию кода, которая позволяет избежать использования глобальных переменных путем инкапсуляции обработчиков событий и переменных, которые они должны совместно использовать в классе PHP. Приложив немного больше работы, вы можете инкапсулировать весь код обработки XML внутри класса и поместить этот класс во включаемый файл, тем самым сократив фактический код в вашем документе для отображения заголовков SitePoint до двухстрочного процесса создания RSSParser. объект, а затем передать один из его методов URL-адрес файла RSS SitePoint ( http://www.sitepoint.com/rss.php ).

Для получения дополнительной информации о спецификации RSS 1.0 посетите веб-сайт рабочей группы RSS . Этот сайт также содержит ссылки на информацию о старых стандартах RSS 0.9x, которые все еще используются многими сайтами в Интернете сегодня, так как RSS 1.0 все еще довольно нов. Вы будете рады узнать, что форматы RSS 0.9x так же легко разобрать с PHP, как RSS 1.0, если не более того.

Чтобы увидеть код, используемый при разборе документов RSS 0.91, см. Статью Марка Робарда « Разбор XML с помощью PHP» . В этой короткой, но информативной статье демонстрируется метод синтаксического анализа XML-данных с помощью PHP, который сводит к минимуму количество глобальных переменных (или переменных экземпляра, если вы используете объектно-ориентированный подход). Лично я предпочитаю читаемый код таким хитрым трюкам, как этот, но этот метод может значительно упростить анализ очень сложных XML-документов.