Lendo um RSS e exibindo-o na tela – por PHP

Estou voltando á programação em PHP depois de muitos meses sem fazer muitas coisas. Vou mostrar aqui como ler um RSS (2.0) e gerar um html a partir dele, usando orientação a objetos. Para ajudar na leitura, vale a pena comentar que eu usei o termo Feed para o arquivo XML lido e FeedEntry cada entrada (num RSS, abstraímos isso normalmente para uma notícia ou matéria).

Criei três arquivos:

  • uma classe que segue o conceito de JavaBean para armazenar os dados de cada FeedEntry
  • uma classe que funcionaria como um service: lê o XML e cria a partir dele uma lista de FeedEntries
  • um arquivo PHP que instancia a classe de serviço e gera adequadamente o código html

 O código da classe FeedEntry.php

 

<?php
class FeedEntry {
    private $title;
    private $description;
    private $pubDate;
    private $link;
   
    function __construct($title, $description, $pubDate, $link) {
        $this->title = $title;
        $this->description = $description;
        $this->pubDate = date("d-M-Y", strtotime($pubDate));
        $this->link = $link;
    }
   
    public function getTitle() {
        return $this->title;
    }
   
    public function getDescription() {
        return $this->description;
    }
   
    public function getPubDate() {
        return $this->pubDate;
    }
   
    public function getLink() {
        return $this->link;
    }
}
?>

 

Eis o código da classe de serviço (FeedService.php), devidamente comentado:

 

<?php
require("FeedEntry.php");
class FeedService {
    private $file;
    private $xml;
    private $doc;
    private $feedEntries = array();
   
    /* construtor: recebe a URL do feed e instancia objeto DOMDocument */
    function __construct($feedUrl) {
        $this->file = $feedUrl;
        $this->doc = new DOMDocument();
    }
   
    /**
    * carrega o arquivo XML do feed
    *
    */
    private function readXML() {       
        $this->doc->load($this->file);
    }
   
    /**
    * lê os nós
    */
    private function readNodes() {
       
        /**
        * percorre o objeto do tipo DOMDocument através do método getElementsByTagName()
        * cria um objeto do tipo FeedEntry e adiciona no array
        *
        * cada feed é declarado no RSS com uma tag <item> e tem:
        * uma tag <title> para o título do feed
        * uma tag <descritpion> para a descrição do feed
        * uma tag <pubDate> para a data de publicação do feed
        * uma tag <link> para o link do feed
        */
        $dataset = $this->doc->getElementsByTagName("item");
        foreach($dataset as $feed) {
            $feedEntry = new FeedEntry($feed->getElementsByTagName("title")->item(0)->nodeValue,
                                       $feed->getElementsByTagName("description")->item(0)->nodeValue,
                                       $feed->getElementsByTagName("pubDate")->item(0)->nodeValue,
                                       $feed->getElementsByTagName("link")->item(0)->nodeValue);
            array_push($this->feedEntries, $feedEntry);
        }
    }
   
    /**
    * chama métodos que lê o XML, percorre os nós transformando-os em objetos FeedEntry e retorna a lista
    * desses objetos
    */
    public function consume() {
        $this->readXML();
        $this->readNodes();
        return $this->feedEntries;
    }
}
?>

 

E, finalizando, o código da página PHP (chamei de consume.php) que vai consumir esse RSS:

 

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Exemplo - consumo de um RSS</title>
</head>
<body>
<h1>Exemplo de uso da classe FeedService</h1>
<dl>
<?php
    //inclui Service que faz a manipulação de Feed
    require("FeedService.php");
    //instancia objeto do tipo
    $feedService = new FeedService("http://feeds.feedburner.com/AndAfter");
    $feedEntries = $feedService->consume(); //método do service retorna um array de objetos FeedEntry
    foreach($feedEntries as $feedEntry) { //percorre a lista de feeds, retornando a cada iteração objetos do tipo FeedEntry
?>
    <dt><a href="<?php echo $feedEntry->getLink(); ?>"><?php echo $feedEntry->getTitle() . " (" . $feedEntry->getPubDate() . " )"; ?></a></dt>
    <dd><?php echo $feedEntry->getDescription(); ?></dd>
<?php
    }
?>
</dl>
</body>
</html>
 

 

Tentei separar um pouco as canadas, mas isso nem de perto chega no conceito de MVC. Mas é um começo, já criando um bean e uma classe específica para acessar dados – note que não há mistura no FeedService de marcação html, ou seja, essa classe pode ser utilizada normalmente em qualquer outro projeto.

Para facilitar, coloquei os 3 arquivos para baixar: http://www.chrisb.com.br/files/consumeFeed.zip
É só descompactar no mesmo diretório e acessar, que funcionará.

Classe para gravar e ler cookies – por javascript

Juntando a idéia desse post do @gserrano sobre cookies (em ASP) e esse outro que fiz semana passada sobre orientação a objetos em javascript usando a biblioteca Prototype, tive a idéia de disponibilizar uma classe que fiz certa vez para gravar e ker cookies da máquina do usuário da sua aplicação.
*não vou aqui discutir a segurança e todos os outros pontos relativos ao não-uso de cookies (o qual concordo, mas certas vezes temos que fazer coisas na nossa vida profissional que vão contra nossos conceitos, né? :-/ )

Esse é o código da classe:

Cookie.prototype = {
    //construtor padrão
    initialize: function() {
         
    },
    /*
     * salva cookie no formato {chave}={valor};
     */
    setCookie: function(key, value) {
        var the_cookie = key + "=" + value + ";" ;
        document.cookie = the_cookie;
    },
   
    /*
     * retorna valor do cookie
     */
    getCookie: function (key) {
        var search = key + "="
        var returnvalue = "";
        if (document.cookie.length > 0) {
            offset = document.cookie.indexOf(search)
            if (offset != -1) {
                offset += search.length
                end = document.cookie.indexOf(";", offset);
                if (end == -1)
                    end = document.cookie.length;
                returnvalue=unescape(document.cookie.substring(offset, end))
            }
        }
        return returnvalue;
    }
}

A classe é bem simples: um método setCookie(key, value), o qual recebe uma chave e um valor; a chave é usada como o identificador do cookie, e o valor… bem, ele é o valor propriamente dito.
O outro método é o getCookie(key), que é chamado sendo passada a chave (identificador) referente ao cookie que você quer saber o valor que havia sido gravado.

Vale ressaltar que document.cookie é um comando (pensando se esse é o termo correto) nativo do javascript

Segue abaixo um exemplo funcional (html + javascript) onde há um formulário com dois campos. Ao clicar em salvar, é chamada a função que instancia o objeto Cookie e grava os valores. A outra função instância outro objeto Cookie e lê do cookie do browser do usuário, procurando a chave salva.

 

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<script type="text/javascript" src="prototype.js"></script>
<title>Cookie.js</title>
<script type="text/javascript">
var Cookie = Class.create();
Cookie.prototype = {
    //construtor padrão
    initialize: function() {
         
    },
    /*
     * salva cookie no formato {chave}={valor};
     */
    setCookie: function(key, value) {
        var the_cookie = key + "=" + value + ";" ;
        document.cookie = the_cookie;
    },
   
    /*
     * retorna valor do cookie
     */
    getCookie: function (key) {
        var search = key + "="
        var returnvalue = "";
        if (document.cookie.length > 0) {
            offset = document.cookie.indexOf(search)
            if (offset != -1) {
                offset += search.length
                end = document.cookie.indexOf(";", offset);
                if (end == -1)
                    end = document.cookie.length;
                returnvalue=unescape(document.cookie.substring(offset, end))
            }
        }
        return returnvalue;
    }
}
function salvarCookie() {
    var ck = new Cookie();
    ck.setCookie($F(´key´), $F(´value´));
}
function lerCookie() {
    var ck = new Cookie();
    alert(ck.getCookie($F(´key´)));
}
</script>
</head>
<body>
<h1>Cookie.js - teste</h1>
<form name="cookie" action="#">
    <label for="key">chave:</label><input type="text" name="key" id="key" />
    <label for="value">valor:</label><input type="text" name="value" id="value" />
    <a href="#" onclick="salvarCookie(); return false;" title="salvar cookie">salvar cookie</a>
    <a href="#" onclick="lerCookie(); return false;" title="ler cookie">ler cookie</a>
</form>
</body>
</html>
 

Criar classes em javascript – usando a Prototype

Orientação a objetos no javascript? É possível, sim. E muito necessário, ainda mais quando lidamos com projetos de grande porte.
Okey, javascript não é uma linguagem OO (abreviação para orientação a objetos). Nem tudo da orientação a objetos em javascript é possível ser implementada; alguns dos (famosos) paradigmas da OO são impossíveis de serem atingidos por limitações técnicas (por exemplo, não existe o conceito de interface, ou métodos que possam ser sobreescritos). O que não inviabiliza o uso da OO no javascript.

Se você não conhece muito de orientação a objetos, sugiro que você procure na web ou na literatura apropriada os conceitos. Esse post falará sobre o uso da (biblioteca) Prototype na ajuda a implementar a OO.

Quem já implementou do zero uma classe em javascript sabe que a dificuldade não é grande, mas que a sintaxe é meio estranha. Por exemplo, vamos criar uma classe que abstrai um objeto Pessoa:
[update]exemplo retirado do blog do Rodrigo, nesse post aqui[/update]

<script language="javascript">
function Pessoa() {
    var nome;
    var idade;
    var email;
    this.getNome = getNome;
    this.getIdade = getIdade;
    this.getEmail = getEmail;
    this.setNome = setNome;
    this.setIdade = setIdade;
    this.setEmail = setEmail;
    this.mostraValores = mostraValores;
    function getNome() {
        return nome;
    }
    function getIdade() {
        return idade;
    }
    function getEmail() {
        return email;
    }
    function setNome(_nome) {
        nome = _nome;
    }
    function setIdade(_idade) {
        idade = _idade;
    }
    function setEmail(_email) {
        email = _email;
    }
    function mostraValores() {
        return ´Nome: ´ + nome +´ Idade: ´+ idade +´ anos Email: ´+ email;
    }
}
</script>

Para consumir essa classe:

<script language="javascript">
    var pessoa = new Pessoa();
    pessoa.setNome("Rodrigo Lazoti");
    pessoa.setIdade(26);
    pessoa.setEmail("[email protected]");
    alert(pessoa.mostraValores());
</script>

Veja que essa classe mais parece um monte de codificação estruturada agrupada, nem não há um construtor para ela.

Vou direto para a codificação de como ficaria tal classe usando a Prototype:

referenciando a prototype:

<script src="http://ajax.googleapis.com/ajax/libs/prototype/1.6.0.2/prototype.js" type="text/javascript"/>
var Pessoa = Class.create();
Pessoa.prototype = {
    //construtor padrão
    initialize: function(nome, idade, email) {
        this.setNome(nome);
        this.setIdade(idade);
        this.setEmail(email);
    },
    setNome: function(nome) {
        this.nome = nome;
    },
    setIdade: function(idade) {
        this.idade = idade;
    },
    setEmail: function(email) {
        this.email = email;
    },
    mostraValores: function() {
        return "Nome: " + this.nome + " Idade: "+ this.idade + " anos - Email: "+ this.email;
    }
}

Quem está acostumado com OO percebe logo de cara que, dessa forma, a semântica usada é muito mais direta e clara; você sempre se refencia aos atributos da classe usando this, e você sempre é obrigado a ter um método initialize que funciona como o construtor.
Para esclarecer o código, na primeira linha é usado um objeto Class da própria prototype e chama-se o seu método create(); a partir do retorno desse método tem-se um objeto que pode ser trabalhado como uma classe da Prorotype. Vale a pena lembrar que o código usado para consumir a classe pode ser exatamente o mesmo enquanto a interface (resumidamente, assinatura dos métodos) das mesmas forem iguais.

Agora, uma coisa bacana (peguei o exemplo lá na API da Prototype) é como extender uma classe. Imagine que você quer criar a partir da classe Pessoa, uma classe Pirata; digamos que o seu Pirata é do passado, e não tem e-mail. Mas tem um navio.
O primeiro passo é extender a classe Pessoa, da seguinte forma:

var Pirata = Class.create(Pessoa, {});

Vamos ver como ficaria o código para sobreescrever o método setEmail() e adicionar um método novo:

var Pirata = Class.create(Pessoa, {
    // sobreescreve o método que define o e-mail
    setEmail: function() {
        this.email = null;
    },
    //cria um método para salvar o nome do navio dele
    setNavio: function(navio) {
        this.navio = navio;
    }
   
});

Sugiro para quem está afim de estudar mais, primeiro olhar a documentação da API da Prototype e procurar exemplos por ae na net, e depois pegar algo que você já fez e funciona e tentar reescrever, do zero, usando orientação a objetos. É uma curva de aprendizado não muito complexa, mas é uma forma bem diferente de se programar para quem não está acostumado!

[update]e é claro que eu esqueci apenas de de falar que é necessário incluir a biblioteca prototype no seu javascript. Você pode fazer download ou referenciar a mesma que é hospedada pelo Google Code[/update]

A variável "Mochileiro das Galáxias" no javascript

O termo é novo, mas o conceito é antigo. Sabe aquela variável que você cria no javascript, global, que você acessa em qualquer função, em qualquer js? Eis o seu mochileiro da galáxia!

O conceito é simples, e foi batizado por autores de livros sobre Java usando o famoso livro Guia do Mochileiro da Galáxia; no livro, o protagonista descobre que seu melhor amigo é um extraterrestre e logo após, a Terra é destruída; a partir daí, começa uma grande viagem por várias galáxias. Na programação Java, é comum ver desenvolvedores criarem objetos (esqueça o conceito de variável por enquanto) que viajam entre diversas camadas da aplicação, funcionando como faz-tudo, passando por cima de qualquer escopo e, muitas vezes, tornando impossível descobrir quem está realmente alterando o quê no seu programa.
* a primeira vez que vi esse termo foi num artigo da revista Mundo Java, exposto em partes aqui.

 Agora, imagine: se em Java que a orientação a objetos é algo inerente ao desenvolvimento, imagine a dor-de-cabeça que tais variáveis (podem ser objetos também) podem causar no javascript, onde o desenvolvedor pode simplesmente implementar da forma que quiser – e, tenham certeza, a forma mais rápida é sempre aquela que dá mais brecha para problemas bizarros.
E digo isso por experiência própria: quantas vezes eu não criei uma variável global no javascript, e meses depois ia lá implementar (ou alguém da equipe ia pegar meu código) algo e do nada coisas começavam a quebrar… até descobrir que eu havia criado 2 variáveis globais!!!

Certamente a orientação a objetos ajuda -e muito – a diminuir essa questão. Para quem desenvolve e acha pataquada, dê um passo atrás e procure pelos benefícios da mesma. Existe claro a curva de aprendizado, há novos paradigmas para quem vêm da programação estruturada, é mais complexa, mas vale a pena a longo prazo.
Ao mesmo tempo, orientação a objetos não é tudo. Se fosse, implementações em Java não teriam tal problema.

Aqui vão algumas sugestões (e que podem valer para outras linguagens):

  • usar o mínimo possível de variáveis globais
  • dar nomes que façam sentido às suas variáveis, isso diminui o problema de você criar uma variável global com nomes iguais (como javascript é interpretado e não compilado, não existe uma verificação de variáveis sendo instanciadas com o mesmo nome, o que não ocorre no primeiro caso)
  • sempre que for criar uma variável, usar a diretiva var. Por exemplo, quando for num laço for usar um contador, fazer:
        for(var i=0; i<array.length; i++)
    

    Porque? Assim você garante que sua variável i comece sempre com o valor igual a 0 (vai por mim, já passei por muitos perrengues por ter variáveis em contadores não inicializadas)

  • documentar as funções/classes – o que elas fazem, quais e de onde vêm os parâmetros utilizados, etc…; isso ajuda muito você a, no futuro, entender porque aquela variável global está fazendo ali e de onde ela veio.
  • nunca, jamais nessa vida, utilizar variáveis globais em retornos de métodos ajax. Porque? Eles são assíncronos. Logo, você pode acessar uma variável que está com um valor qualquer num momento, assumir que está correta, e o valor mudar no instante seguinte – levando você a loucura sem entender porque o fluxo esperado não é seguido. E, denovo, vão por mim: já tive dores de cabeça com isso :-/

 

*se alguém tiver outras sugestões, por favor, enviem 🙂

E eu não estou aqui querendo dizer "não usem variáveis globais"; até porque eu sei o quão difícil é em muitos momentos desenvolver em javascript, devido às suas limitações. A idéia é apenas usar com cuidado 🙂