Статьи

Как объединить RSS-каналы с помощью Php & Zend Framework


Недавно я задумал создать RSS-ленту моих блогов и обновлений в Твиттере. Есть несколько сервисов слияния RSS, а также есть
каналы Yahoo, которые, кажется, наиболее полезны для выполнения различных задач по настройке RSS. В рамках своей игры с Zend Framework я решил создать этот объединенный RSS-канал с использованием
компонента
Zend_Feed . На самом деле, в конце я понял, что эта идея объединенной подачи совершенно бесполезна, но, по крайней мере, этот пост вышел из нее 🙂

Zend Framework становится очень полным набором широко необходимых компонентов для разработки PHP. Поскольку другие платформы предлагают аналогичные компоненты, одно из главных преимуществ Zend Framework — это то, что вы можете использовать его компоненты как отдельные компоненты, а не только как часть структуры MVC . В этом уроке я покажу, как вы можете легко использовать его компонент Zend_Feed для объединения каналов.

Большинство основных действий, таких как импорт RSS-канала, создание RSS-канала и многое другое, описаны в руководстве Zend Framework. В этом посте я более подробно остановлюсь на более сложных темах, таких как сортировка и объединение RSS-каналов, но я также кратко остановлюсь на более элементарных вещах.

Итак, начнем.

Прежде всего нам нужно загрузить RSS-канал, это делается довольно легко:

function loadFeed ($url) {
try {
$feed = Zend_Feed::import($url);
} catch (Zend_Feed_Exception $e) {
// feed import failed
return null;
}
return $feed;
}

Теперь, когда у нас есть канал, мы можем прочитать его свойства, например, напечатать заголовок:

$feed = loadFeed ('http://www.arikfr.com/blog/feed/');

echo $feed->title();

Или переберите все записи фида и напечатайте их заголовки:

foreach ($feed as $entry) {
echo $entry->title();
}

( Zend_Feed_Abstract , который реализуют Zend_Feed_Rss и Zend_Feed_Atom , реализует интерфейс Iterator, поэтому мы можем использовать его в цикле foreach ).

Теперь, когда мы рассмотрели основы, давайте приступим к созданию нового канала. Вы можете создать ленту RSS или Atom, используя Zend_Feed . Мы создадим канал RSS. Создание канала выполняется путем создания массива, который будет содержать новые свойства канала. Есть обязательные поля и некоторые дополнительные поля. В этом примере я расскажу обо всех обязательных (вы можете найти полную структуру здесь ):

$merged_feed = array (
'title' => 'ArikFr.com merged Feed',
'link' => 'http://www.arikfr.com/merged_feed.php',
'charset' => 'UTF-8',
'entries' => array (),
);

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

$rssFeedFromArray = Zend_Feed::importArray($merged_feed, 'rss');

И мы можем вывести его на простой браузер с помощью вызова отправки метода Zend_Feed_Rss объекта:

$rssFeedFromArray->send();

Теперь все, что браузер получит — это пустой RSS-канал:

<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:content="http://purl.org/rss/1.0/modules/content/" version="2.0">
<channel>
<title><![CDATA[ArikFr.com merged Feed]]></title>
<link>http://www.arikfr.com/merged_feed.php</link>
<description></description>
<pubDate>Mon, 25 Feb 2008 20:55:37 +0000</pubDate>
<generator>Zend_Feed</generator>
<docs>http://blogs.law.harvard.edu/tech/rss</docs>
</channel>
</rss>

К настоящему времени все, что мы сделали, было довольно простым и очень простым. Теперь давайте начнем импортировать записи из каналов, которые мы хотим объединить:

function getEntriesAsArray ($feed) {
$entries = array();
foreach ($feed as $entry) {
$entries[] = array (
'title' => $entry->title(),
'link' => $entry->link(),
'guid' => $entry->guid(),
'lastUpdate' =>strtotime($entry->pubDate()),
'description' => $entry->description(),
'pubDate' => $entry->pubDate(),
);
}
return $entries;
}

Все, что делает эта функция, выполняет итерацию по всем элементам данного канала и возвращает их в виде массива. Мне не очень нравится этот подход, и у него есть некоторые недостатки, потому что не все каналы имеют одинаковые поля, и иногда было бы лучше просто скопировать исходный элемент узла в новый канал. Насколько я знаю, это невозможно сделать (по крайней мере, легко) с Zend_Feed (во всяком случае, я не нашел пути, так что если вы знаете другое — пожалуйста, напишите мне). В любом случае, приведенный выше пример кода создает запись фида с самыми основными (и обязательными) полями.

Также обратите внимание, что я конвертирую дату публикации каждого элемента RSS в метку времени, используя функцию strtotime . Эта функция может принимать значения времени или даты в различном форматировании и преобразовывать их обратно в метку времени — очень полезно.

Теперь, если мы просто используем это как есть, то есть

$merged_feed['entries'] = array_merge (
getEntriesAsArray ($feed1),
getEntriesAsArray ($feed2));

В новом фиде будут все записи из обоих блогов, но они не будут отсортированы. Основные функции сортировки массива в PHP могут легко сортировать числа или строки, но если мы хотим отсортировать что-то более сложное, нам нужно использовать функцию usort . Если вы не знакомы с usort — это функция сортировки массива, которая использует пользовательскую функцию для сравнения элементов (см. Страницу руководства для получения дополнительной информации). Итак, давайте напишем нашу функцию сравнения:

function cmpEntries ($a , $b) {
$a_time = $a['lastUpdate'];
$b_time = $b['lastUpdate'];

if ($a_time == $b_time) {
return 0;
}
return ($a_time > $b_time) ? -1 : 1;
}

Эта функция сортирует временную метку записей в порядке убывания — как и должно быть. Теперь, когда у нас есть функция сравнения, сортировка записей RSS довольно проста:

usort ($merged_feed['entries'], 'cmpEntries');

И теперь мы практически закончили. На рисунке ниже показано, как выглядит объединенный канал перед сортировкой (слева) и после сортировки (справа):

До и после сортировки

(Мне жаль, что метки времени на иврите — это из-за настроек моего компьютера. Просто обратите внимание на время и даты).

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

Он вышел дольше, чем я себе представлял, наверное, из-за всех примеров исходного кода. Любые комментарии, идеи и лучшие практики в основном приветствуются!