RSS é um formato amplamente usado atualmente para distribuir conteúdo dinâmico na internet. Com a quantidade de informação disponível na web, se torna impraticável pensar em acessar cada site regularmente para saber se existe algo de novo. É exatamente aí que a necessidade pelos feeds entra no jogo.
O que é RSS?
RSS nada mais é que um formato de distribuição de conteúdo baseado em XML, o que é ótimo, pois não exige que o desenvolvedor tenha que aprender uma nova linguagem para cria-lo.
Existem basicamente dois formatos principais de distribuição, que são Atom e RSS, que apresentam algumas diferenças entre si, mas o objetivo de ambos é o mesmo. O formato RSS tem duas ramificações, uma delas o RSS 1.0 e outra RSS 2.0 . Não vou abordar a diferença entre os dois neste tutorial, e usarei o ramo da versão 2.0, já que é bem mais simples e cumpre muito bem com seu dever.
Criando RSS
Neste tutorial usarei XMLWriter com PHP para escrever os arquivos de XML, mas você também pode usar a extensão SimpleXML para gerar XML . Minha decisão pela extensão XMLWriter se deu pelo fato de eu acha-la bem mais simples e intuitiva para geração desse arquivo.
Sem mais delongas, vamos partir para a prática e ver como podemos gerar nosso próprio RSS.
Estrutura
A primeira coisa que temos que ter em mente é a estrutura do nosso RSS. Algumas tags são obrigatórias, mas a maioria é opcional, nos dando maior flexibilidade de criação. Conforme descrito nas especificações, devemos iniciar nosso documento com uma tag chamada rss e dentro dessa tag devemos ter outra tag, chamada channel. Dentro da tag channel virão todas as informações sobre nosso RSS. Existem três tags obrigatórias dentro do channel, que são title, link e description. Abaixo você vê a estrutura mais básica possível de um RSS.
<?xml version="1.0" encoding="utf-8" ?> <rss version="2.0"> <channel> <title>TC - Feeds</title> <link>http://localhost/tc/rss.xml</link> <description>Blog que traz as novidades de desenvolvimento web</description> </channel> </rss>
Tomei a liberdade de inserir um conteúdo qualquer, para que fique mais fácil identificar como é usado na prática. Neste momento o RSS já pode ser visualizado no navegador (alguns navegadores tem suporte de exibição e inscrição de feeds, mas isso não é uma regra. neste tutorial usei o Firefox para isso, mas existem vários interpretadores de RSS disponíveis, como o Google Reader ou Microsoft Outlook por exemplo), apesar de não ter nenhum conteúdo. O resultado é mostrado abaixo.
Conteúdo
Todo conteúdo do nosso RSS fica dentro da tag item, e podemos tem quantos itens nós quisermos, mas é comum que esse valor esteja entre cinco e vinte (normalmente 10).
A tag item não é obrigatória, mas dada a necessidade de distribuir conteúdo ela acaba se tornando mandatória. Não existe nenhuma tag exigida dentro da tag item, mas pelo menos uma deve existir.
No caso específico desse tutorial irei usar as tags title, link, description, guid e pubDate. Em geral os nomes das tags se auto-explicam, mas de qualquer maneira deixo uma descrição simples a seguir:
title
Título do conteúdo
link
link do conteúdo
description
Conteúdo em si. particularmente acho interessante colocar somente uma introdução.
guid
Uma identificação única do conteúdo. Pode ter qualquer valor, desde que seja único.
pubDate
Data de publicação, no formato RFC-822. Existe uma constante que nos ajuda nessa situação, que é DATE_RSS.
com as informações acima já podemos iniciar a inserção de conteúdo no nosso XML.
<?xml version="1.0" encoding="utf-8" ?> <rss version="2.0"> <channel> <title>TC - Feeds</title> <link>http://localhost/tc</link> <description>Blog que traz as novidades de desenvolvimento web</description> <item> <title>Primeiro Post</title> <link>http://localhost/post/1</link> <description>Este post fala sobre RSS</description> <guid>http://localhost/post/1</guid> <pubDate>Sat, 27 Feb 2010 15:13:53 +0000</pubDate> </item> </channel> </rss>
visualizando no navegador, obtemos o seguinte resultado:
Como pode-se ver, o navegador já percebeu que temos um item, e nos mostra na tela. A seguir vou criar outros dois itens para ver como ele se comporta. Repare como os itens se apresentam de forma decrescente, isso é importante por que sempre se deseja mostrar os mais recentes primeiro.
<?xml version="1.0" encoding="utf-8" ?> <rss version="2.0"> <channel> <title>TC - Feeds</title> <link>http://localhost/tc/rss.xml</link> <description>Blog que traz as novidades de desenvolvimento web</description> <item> <title>Primeiro Post</title> <link>http://localhost/post/3</link> <description>Este post fala ainda mais sobre RSS</description> <guid>http://localhost/post/3</guid> <pubDate>Mon, 01 Mar 2010 15:13:53 +0000</pubDate> </item> <item> <title>Segundo Post</title> <link>http://localhost/post/2</link> <description>Este post fala mais sobre RSS</description> <guid>http://localhost/post/2</guid> <pubDate>Sun, 28 Feb 2010 15:13:53 +0000</pubDate> </item> <item> <title>Primeiro Post</title> <link>http://localhost/post/1</link> <description>Este post fala sobre RSS</description> <guid>http://localhost/post/1</guid> <pubDate>Sat, 27 Feb 2010 15:13:53 +0000</pubDate> </item> </channel> </rss>
Com exceção da tag item, todos as tags que usei dentro de channel são obrigatórias. Agora vou inserir outras opcionais, que eu gosto de usar.
<?xml version="1.0" encoding="utf-8" ?>
<rss version="2.0">
<channel>
<title>TC - Feeds</title>
<link>http://localhost/tc/rss.xml</link>
<description>Blog que traz as novidades de desenvolvimento web</description>
<language>pt</language>
<pubDate>Tue, 02 Mar 2010 14:43:32 +0000</pubDate>
<image>
<title>TC - Feeds</title>
<link>http://www.localhost/tc</link>
<url>http://www.localhost/tc/tc.jpg</url>
<width>120</width>
<height>60</height>
</image>
<item>
<title>Terceiro Post</title>
<link>http://localhost/post/3</link>
<description>Este post fala ainda mais sobre RSS</description>
<guid>http://localhost/post/3</guid>
<pubDate>Mon, 01 Mar 2010 15:13:53 +0000</pubDate>
</item>
<item>
<title>Segundo Post</title>
<link>http://localhost/post/2</link>
<description>Este post fala mais sobre RSS</description>
<guid>http://localhost/post/2</guid>
<pubDate>Sun, 28 Feb 2010 15:13:53 +0000</pubDate>
</item>
<item>
<title>Primeiro Post</title>
<link>http://localhost/post/1</link>
<description>Este post fala sobre RSS</description>
<guid>http://localhost/post/1</guid>
<pubDate>Sat, 27 Feb 2010 15:13:53 +0000</pubDate>
</item>
</channel>
</rss>
As linhas destacadas foram inseridas. A descrição de cada um dos nós pode ser encontrada na especificação RSS.
Neste momento o RSS já foi criado, porém não faz muito sentido cria-lo manualmente, e é aí que entra o poder do XMLWriter no PHP.
Conhecendo o XMLWriter
Usaremos somente alguns métodos do XMLWriter, e todos estão, brevemente, descritos a seguir. Para uma lista completa visite a documentação oficial do XMLWriter
- XMLWriter::openURI
- Abre o arquivo que será usado para salvar o conteúdo do XML. Caso o arquivo não exista ele é criado. Se php://output for passado como parâmetro, o XML será jogado na tela
- XMLWriter::startDocument
- Inicia um documento
- XMLWriter::endDocument
- Finaliza um documento
- XMLWriter::startElement
- Inicia um elemento
- XMLWriter::endElement
- Finaliza um elemento
- XMLWriter::writeElement
- Escreve um elemento diretamente, sem a necessidade de fecha-lo posteriormente
- XMLWriter::writeAttribute
- Escreve um atributo no elemento mais recente.
- XMLWriter::writeCData
- Escreve um conteúdo escapado por CData (necessário quando se quer inserir HTML dentro de um arquivo XML)
- XMLWriter::writeText
- Escreve um texto dentro da tag mais recente
- XMLWriter::flush
- Salva o arquivo ou o joga na tela, dependendo do parâmetro passado para XMLWriter::openURI
Organização das pastas e arquivos
Organizarei minhas pastas da seguinte maneira
Banco de dados
Para que tenhamos conteúdo para trabalhar, vou criar um banco de dados chamado Blog, e dentro desse banco de dados criarei uma tabela chamada posts. Esta tabela terá quatro campos, id, titulo, conteudo e data_criacao.
CREATE DATABASE Blog;
CREATE TABLE posts ( id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY , titulo VARCHAR(200) NOT NULL , conteudo LONGTEXT NOT NULL , data_criacao DATETIME NOT NULL );
Agora basta inserir algum conteúdo
INSERT INTO posts VALUES (NULL,'Primeiro Post','Conteúdo do primeiro post','2010-08-01 00:00:00'), (NULL,'Segundo Post' ,'Conteúdo do segundo post' ,'2010-08-02 00:00:00'), (NULL,'Terceiro Post','Conteúdo do terceiro post','2010-08-03 00:00:00'), (NULL,'Quarto Post' ,'Conteúdo do quarto post' ,'2010-08-04 00:00:00'), (NULL,'Quinto Post' ,'Conteúdo do quinto post' ,'2010-08-05 00:00:00')
Conexão com banco de dados
Primeiro vou criar o arquivo de conexão com o banco de dados, pois este será usado em algumas outras páginas. No arquivo conexao.php a conexão é criada. Usarei a extensão PDO para a interação com o banco de dados, caso você não saiba usa-la, pode aprender todo o CRUD com prepared statements usando PDO neste tutorial aqui do Tutorial City.
$host = 'localhost';
$db_name = 'Blog';
$user = 'root';
$pass = '';
$dsn = 'mysql:host='.$host.';dbname='.$db_name;
try {
$db = new PDO($dsn,$user,$pass);
$db->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);
unset($host);unset($db_name);unset($user);unset($pass);
} catch (PDOException $e) {
echo $e->getMessage();
}
Páginas
Página Inicial
No arquivo index.php, localizado na raiz da nossa estrutura, vai ficar a lógica que mostrará todos os posts
<?php
require_once 'conexao.php';
//ordenar por data de criação de modo decrescente, para mostrar os mais recentes primeiro
$sql = 'SELECT id, titulo, conteudo, data_criacao FROM posts ORDER BY data_criacao DESC';
$query = $db->prepare($sql);
$query->execute();
$query->bindColumn('id' ,$id );
$query->bindColumn('titulo' ,$titulo );
$query->bindColumn('conteudo' ,$conteudo );
$query->bindColumn('data_criacao',$data_criacao );
$query->setFetchMode(PDO::FETCH_BOUND);
?>
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="utf-8">
<title>TC Blog</title>
</head>
<body>
<?php while($query->fetch()):?>
<h1><?php echo utf8_encode($titulo)?></h1>
<p><em><?php echo date('d/m/Y',strtotime($data_criacao))?></em></p>
<p><?php echo utf8_encode($conteudo)?></p>
<p><a href="http://localhost/tc/post.php?id=<?php echo $id?>">Veja Mais</a></p>
<hr />
<?php endwhile;?>
</body>
</html>
Repare que usei a função utf8_encode no título e no conteúdo. Ela é necessária pois os caracteres acentuados não retornavam da maneira desejada. Visitando a página inicial temos o seguinte resultado:
Post único
Agora vou criar a página do post único, que é bastante parecida com a página inicial, porém só pega um post.
<?php
require_once 'conexao.php';
$sql = ' SELECT id, titulo, conteudo, data_criacao ';
$sql.= ' FROM posts';
$sql.= ' WHERE id = ?';
$query = $db->prepare($sql);
$query->bindValue(1,$_GET['id'],PDO::PARAM_INT);
$query->execute();
$query->bindColumn('id' ,$id );
$query->bindColumn('titulo' ,$titulo );
$query->bindColumn('conteudo' ,$conteudo );
$query->bindColumn('data_criacao',$data_criacao );
$query->setFetchMode(PDO::FETCH_BOUND);
?>
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="utf-8">
<title>TC Blog</title>
</head>
<body>
<?php $query->fetch()?>
<h1><?php echo utf8_encode($titulo)?></h1>
<p><em><?php echo date('d/m/Y',strtotime($data_criacao))?></em></p>
<p><?php echo utf8_encode($conteudo)?></p>
</body>
</html>
Gerando XML usando XMLWriter
Toda a base já está pronta nesse momento. Agora basta criar o RSS e inserir na página. Voltando ao arquivo index.php na pasta rss, vou iniciar a criação do XML usando XMLWriter.
date_default_timezone_set('America/Sao_Paulo');
header('Content-type: application/rss+xml');
$rss = new XMLWriter();
$rss->openURI('php://output');
$rss->setIndent(true);
$rss->setIndentString(' ');
$rss->startDocument('1.0','utf-8');
$rss->startElement('rss');
$rss->writeAttribute('version','2.0');
$rss->startElement('channel');
$rss->writeElement('title','TC - Feeds');
$rss->writeElement('link','http://localhost/tc');
$rss->writeElement('description','Blog que traz as novidades de desenvolvimento web');
$rss->writeElement('language','pt');
$rss->writeElement('pubDate',date(DATE_RSS));
$rss->startElement('image');
$rss->writeElement('title','TC - Feeds');
$rss->writeElement('link','http://localhost/tc');
$rss->writeElement('url','http://localhost/tc/rss/tc.jpg');
$rss->writeElement('width','120');
$rss->writeElement('height','60');
$rss->endElement();
$rss->endElement();// final do channel
$rss->endElement();// final do rss
$rss->endDocument();
//envia o xml para o endereço do openURI
$rss->flush();
Existem alguns pontos importantes a se destacar sobre o código acima. É uma boa prática usar a função date_default_timezone_set no documento, para que todas as datas sejam exibidas com fuso horário correto. É importante que se envie um cabeçalho dizendo que o conteúdo a seguir é RSS. Na linha cinco usei php://output como parâmetro do método XMLWriter::openURI, o que significa que eu quero que o conteúdo seja jogado na tela, e não em um arquivo. Nas linhas seis e sete digo que quero recuar meu código, e que usarei quatro espaços como recuo. Apesar disso não ser fundamental, é bom para que o código fique mais legível quando completo. O resto é bem simples, só crio tags e escrevo conteúdo conforme explicado no início do tutorial. Sempre tomo o cuidado de recuar meu código, para que eu saiba quem está dentro de quem, e recomendo que você faça o mesmo.
Se dirigindo à URL http://localhost/tc/rss, o resultado é o seguinte:
Gerando itens dinamicamente
Agora basta inserir cada post dentro de um item no RSS. Primeiro inclui-se o arquivo de conexão no início do arquivo index.php, depois pega-se todos os posts, não esquecendo de ordenar de forma decrescente.
<?php
require '../conexao.php';
$sql = ' SELECT id, titulo, conteudo, data_criacao FROM posts ';
$sql .= ' ORDER BY data_criacao DESC LIMIT 10';
$query = $db->prepare($sql);
$query->execute();
$query->bindColumn('id' ,$id );
$query->bindColumn('titulo' ,$titulo );
$query->bindColumn('conteudo' ,$conteudo );
$query->bindColumn('data_criacao',$data_criacao );
$query->setFetchMode(PDO::FETCH_BOUND);
date_default_timezone_set('America/Sao_Paulo');
header('Content-type: application/rss+xml');
$rss = new XMLWriter();
$rss->openURI('php://output');
$rss->setIndent(true);
$rss->setIndentString(' ');
$rss->startDocument('1.0','utf-8');
$rss->startElement('rss');
$rss->writeAttribute('version','2.0');
$rss->startElement('channel');
$rss->writeElement('title','TC - Feeds');
$rss->writeElement('link','http://localhost/tc');
$rss->writeElement('description','Blog que traz as novidades de desenvolvimento web');
$rss->writeElement('language','pt');
$rss->writeElement('pubDate',date(DATE_RSS));
$rss->startElement('image');
$rss->writeElement('title','TC - Feeds');
$rss->writeElement('link','http://localhost/tc');
$rss->writeElement('url','http://localhost/tc/rss/tc.jpg');
$rss->writeElement('width','120');
$rss->writeElement('height','60');
$rss->endElement();
/***************** ITEM *****************/
while($query->fetch()):
$rss->startElement('item');
$rss->writeElement('title',utf8_encode($titulo));
$rss->writeElement('link','http://localhost/tc/post.php?id='.$id);
$rss->startElement('description');
$rss->writeCData(utf8_encode($conteudo));
$rss->endElement();
$rss->writeElement('guid','http://localhost/tc/post.php?id='.$id);
$rss->writeElement('pubDate',date(DATE_RSS,strtotime($data_criacao)));
$rss->endElement();
endwhile;
/***************************************/
$rss->endElement();// final do channel
$rss->endElement();//final do rss
$rss->endDocument();
//envia o xml para o endereço do openURI
$rss->flush();
Repare que para escrever o conteúdo eu usei o método XMLWriter::writeCData, pois o conteúdo pode apresentar tags HTML, e estas não podem vir diretamente em um arquivo XML, tendo que ser escapadas usando CData.
Visualizando o código no navegador temos o seguinte:
Para mostrar como pode-se inserir HTML no conteúdo, vou modificar o conteúdo do segundo post
UPDATE posts SET conteudo = `<h3>conteúdo</h3> do <code>segundo</code> post` WHERE id = 2;
Agora voltando ao RSS, o resultado é o seguinte:
Criando a referência do RSS no HTML
Neste momento tudo já está pronto, e já podemos assinar nosso RSS. Os navegadores mais recentes exibem um ícone na barra de endereços indicando quando um site tem conteúdo RSS, mas isso não acontece automaticamente, até por que o navegador não sabe se você tem um arquivo pra isso, a menos que você diga a ele que você tem RSS para disponibilizar. Para fazer isso basta adicionar uma linha ao cabeçalho do nosso HTML, como visto a seguir:
<head> <meta charset="utf-8"> <title>TC Blog</title> <link rel="alternate" type="application/rss+xml" title="RSS" href="http://localhost/tc/rss" /> </head>
Agora olhando para a barra de endereços vemos um ícone de RSS, indicando que o site apresenta conteúdo no formato RSS.
Validação
O último passo é validar nosso RSS. Para isso basta visitar o site oficial do validador. Como meu conteúdo está offline, no meu servidor local, tenho que selecionar a opção de jogar o RSS diretamente na tela (segunda aba). Para coletar nosso XML basta acessar http://localhost/tc/rss e usar o atalho CTRL + U no Firefox, para ter acesso ao código fonte. Daí basta copiar e colar no validador. o resultado no meu caso foi o seguinte:
O RSS validou, mas o validador recomenda que usemos rel=”self”, que nada mais é que uma referência ao próprio arquivo de RSS. Isto é importante para que os diversos leitores consigam entender melhor nosso feed.
Voltando ao nosso index.php na pasta rss, modifiquei as linhas marcadas, para atender ao pedido do validador
require '../conexao.php';
$sql = ' SELECT id, titulo, conteudo, data_criacao FROM posts ';
$sql .= ' ORDER BY data_criacao DESC LIMIT 10';
$query = $db->prepare($sql);
$query->execute();
$query->bindColumn('id' ,$id );
$query->bindColumn('titulo' ,$titulo );
$query->bindColumn('conteudo' ,$conteudo );
$query->bindColumn('data_criacao',$data_criacao );
$query->setFetchMode(PDO::FETCH_BOUND);
date_default_timezone_set('America/Sao_Paulo');
header('Content-type: application/rss+xml');
$rss = new XMLWriter();
$rss->openURI('php://output');
$rss->setIndent(true);
$rss->setIndentString(' ');
$rss->startDocument('1.0','utf-8');
$rss->startElement('rss');
$rss->writeAttribute('xmlns:atom','http://www.w3.org/2005/Atom');
$rss->writeAttribute('version','2.0');
$rss->startElement('channel');
$rss->startElement('atom:link');
$rss->writeAttribute('rel','self');
$rss->writeAttribute('type','application/rss+xml');
$rss->writeAttribute('href','http://localhost/tc/rss/');
$rss->endElement();
$rss->writeElement('title','TC - Feeds');
$rss->writeElement('link','http://localhost/tc');
$rss->writeElement('description','Blog que traz as novidades de desenvolvimento web');
$rss->writeElement('language','pt');
$rss->writeElement('pubDate',date(DATE_RSS));
$rss->startElement('image');
$rss->writeElement('title','TC - Feeds');
$rss->writeElement('link','http://localhost/tc');
$rss->writeElement('url','http://localhost/tc/rss/tc.jpg');
$rss->writeElement('width','120');
$rss->writeElement('height','60');
$rss->endElement();
/***************** ITEM *****************/
while($query->fetch()):
$rss->startElement('item');
$rss->writeElement('title',utf8_encode($titulo));
$rss->writeElement('link','http://localhost/tc/post.php?id='.$id);
$rss->startElement('description');
$rss->writeCData(utf8_encode($conteudo));
$rss->endElement();
$rss->writeElement('guid','http://localhost/tc/post.php?id='.$id);
$rss->writeElement('pubDate',date(DATE_RSS,strtotime($data_criacao)));
$rss->endElement();
endwhile;
/***************************************/
$rss->endElement();// final do channel
$rss->endElement();//final do rss
$rss->endDocument();
//envia o xml para o endereço do openURI
$rss->flush();
O efeito das mudanças no código fonte é mostrado abaixo:
<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
<channel>
<atom:link rel="self" type="application/rss+xml" href="http://localhost/tc/rss/"/>
<title>TC - Feeds</title>
<link>http://localhost/tc</link>
<description>Blog que traz as novidades de desenvolvimento web</description>
<language>pt</language>
<pubDate>Sat, 14 Aug 2010 19:45:14 -0300</pubDate>
Voltando ao validador e refazendo o teste veremos que outro erro ocorrerá, dizendo que o arquivo não foi encontrado no endereço que passamos. Esse erro só acontece porque o arquivo está offline, e o validador não consegue acessá-lo. Quando o RSS ficar online vai estar 100% validado e pronto pra ser usado.
Conclusão
Neste momento você já é capaz de criar feeds RSS em seus sites, tornando-os muito mais ricos. Existe muito sobre RSS que não foi mencionado nesse tutorial, então te encorajo a pesquisar sobre o assunto. usando XMLWriter é muito fácil criar qualquer arquivo XML, mas existem vários métodos que não usei, e também recomendo que dê uma lida na documentação oficial para se tornar ainda melhor.
Agradeço por ter seguido todo o tutorial, e não se esqueça de fazer perguntas caso tenha alguma dúvida. Até a próxima!
Muito bom o tutorial! Parabéns e muito obrigado!
A única coisa é que quando apago os feeds no gerenciador (utilizo o Brief – Firefox) e os retiro da lixeira, na próxima busca, ele traz os novos e os feeds que já foram apagados.
Você sabe me dizer onde estou errando e o que posso fazer para corrigir isto?
té+
Se isso acontece com todos os feeds, é bastante provável que seja um bug do Brief, até porque a única responsabilidade dos feeds é disponibilizar o conteúdo, já o leitor é responsável por armazenar e exibir as informações.
Recomendo que teste com outros aplicativos pra tirar a prova.
Abraços
Obrigado por esse tuto. É único tutorial de jeito que encontrei pela net que funciona e que está bastante completo. Grande trabalho
Amigo,
Obrigado e parabéns pelo tutorial.
Duvida, como contabilizar o numero de inscrições do RSS?
Usa o Feedburner.
Obrigado e parabéns pelo tuto