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á.

Indexando todas as imagens de um texto – jQuery

Como vocês já devem estar cansados de ler, estou desenvolvendo algumas novidades para o And After mas isso está rendendo ótimos frutos, principalmente com jQuery (e ultimamente com SQL).

Para uma das novas features eu precisava indexar todas as imagens utilizadas nos posts do site. Resolvi cadastrar tudo no banco de dados, mas a dúvida era… como automatizar isso tudo?

Como estava estudando jQuery resolvi deixar de lado o server-side, utilizando apenas quando necessário: para inserir os dados no banco. Quase todo o resto foi feito com javascript, especificamente com jQuery.

Dica de leitura: Excelentes truques e dicas para jQuery

Objetivo do script
Encontrar todas as imagens de um texto e inserir estas imagens no banco de dados guardando as informações: url (src), descrição (alt) e qual o texto utilizou a imagem (definido por server-side). Ao final, passar para o próximo post do banco de dados e repetir o processo até que as imagens de todos os posts tenham sido indexadas.

 

Etapas de funcionamento

Explicarei em passos simples o funcionamento do script:

  1. O script server-side seleciona o post no Banco de Dados
  2. Através o jQuery o script "lê" o texto e busca pelas imagens
  3. Para cada imagem encontrada é executado um script server-side (post por ajax) que insere a imagem no banco de dados
  4. Ao final da indexação das imagens daquele post o script redireciona a página para o próximo post

 

Irei publicar aqui apenas a programação client-side, que é o foco deste post para facilitar o entendimento, deixando o script resumido. Também ocultei as funções de redirect (para passar os itens "post" do banco de dados) para o script ficar mais amplo.

O objetivo aqui é mostrar como funciona e estimular idéias 🙂

 

Indexando todas as imagens de um post

/*
 * Finding images in a div and list (or execute any action) with all images
 *
 * Copyright (c) 2009 Guilherme Serrano
 * Dual licensed under the MIT
 * and GPL (GPL-LICENSE.txt) licenses.
 *
 * http://www.guilhermeserrano.com.br
 * https://andafter.org
 *
 */
$(function (){
	var imag = $(´#finder´).find(´img´); //find all images
	var i = 0
	if(imag.length==0){
		item.html(´
  • Nenhuma imagem neste texto
  • ´) } imag.each(function(){ var url = $(this).attr(´src´); //get img url var titulo = $(this).attr(´alt´); //get img description (alt) var item = $(´
  •  
  • ´) //create item list $.post(´server-side.asp?a=add´, { url: url, titulo: titulo, id_post: id_post}, //POST data to server side script function(data){ i = i+1 item.html(data) $(´#imagens´).animate({opacity: 1}, 5000).append(item) //print data and pause 5s }); }) })

     

    No arquivo server-side.asp eu tenho um script que verifica se a imagem já está cadastrada no banco de dados, se ela não está cadastrada então cadastro e retorno um html "Imagem X cadastrada com sucesso no banco" ou "A imagem X já existe no banco". Esta mensagem é a variável data, que é printada pelo jQuery   no meu elemento #imagens.

     

    [update] Depois do comentário do Anderson Baldner resolvi tornar mais intuitivo, adicionando o html de forma "completa", chamando o jQuery e também chamando o arquivo de javascript. [/update]

    Html

    
    
    
    Aqui vai o texto que contém as imagens que você deseja indexar.

     

    Utilizando server-side e algumas alterações neste script criei um "mini-crawler" que percorreu todos os posts do meu banco de dados indexando as imagens e suas informações em um banco de dados, independente as imagens estarem hospedadas no meu servidor ou não.

     

    Curiosidade

    Esse script foi desenvolvido para aplicação "interna", utilizando um delay de 2 segundos por post o script levou 1 hora e 30 minutos para percorrer de forma automática 800 posts do And After e indexar 2.659 imagens no banco de dados. Poderia ser feito de forma reduzindo um pouco o delay, mas como não estava com pressa e não queria "consumir" o banco (sou cagão e não sou programador, então melhor previnir né?) deixei ele light.

     

    É apenas um exemplo do que é possível, dá pra fazer muita coisa com um pouco de criatividade, espero ter ajudado a despertar alguma idéia nestas cabeças geniais que leio por aí! 🙂

    Indexar feeds dos blogs que você gosta, para selecionar posts pelas imagens por exemplo é só umas das idéias possíveis… haha 😉

    Lembre-se que Programar é Grátis, exercite-se!

    Portal Class – crie uma home com widgets como o Netvibes

    Se você precisa criar uma página com widgets, onde o usuário pode arrastar e soltar retrancas que estão separadas em colunas, veja essa solução:

    http://blog.xilinus.com/2007/8/26/prototype-portal-class

    Enquanto a Prototype UI não sai (isso é, se sair algum dia de verdade), o autor dessa classe utilizou a prototype junto com o script.aculo.us (framework para efeitos, feito em cima da prototype) para resolver o problema dele. Aqui você pode ver funcionando:

    http://blog.xilinus.com/prototype-portal/test/index.html

    O código é muito simples:

    • crie a marcação html com quantas colunas quiser:
      <div id="page">
          <div id="widget_col_0"></div>
          <div id="widget_col_1"></div>
          <div id="widget_col_2"></div>
      </div>
    • crie um CSS para exibir de forma adequada as colunas e os widgets:
      #page {
          margin: 10px auto;
      }
      #widget_col_0 {
          float:left;
          width: 30%; 
      }
      #widget_col_1 {
          width: 50%;
          float:left;
      }  
      #widget_col_2 {
      float:left;
          width: 20%; 
      }
    • instancie um objeto da Portal da classe Xilinus, passando a div que contém as colunas como parâmetro:
      var  portal = new Xilinus.Portal("#page div")
    • adicione widgets ao portal escolhendo a coluna em que aparecerá e definindo se quer ou não inserir título, conteúdo, etc…:
      portal.add(new Xilinus.Widget(), 0)
      // Or with title and content
      portal.add(new Xilinus.Widget().setTitle("Widget Title").setContent("bla bla bla"), 1);
    •  

    Altamente recomendavelessa classe; eu fiz uma implementação desse tipo 2 anos atrás, faz as mesmas coisas, e sei qual a dificuldade. E fiz ela muito amarrada ao sistema para o qual criei. Essa indicada é totalmente independente, orientada a objetos. Fácil! E com muitas opções de configuração, livre em termos de layout e com chamada a funções de callback caso você precise efetuar rotinas após mover widgets. Muito bom!

    Velocity – linguagem de templates

    Quando comecei a trabalhar na empresa em que estou até hoje, 4 anos atrás, fui "jogado" no mundo de aplicações java para web. Antes, só tinha mexido de verdade com PHP. E de cara comecei a mexer com o que, hoje em dia, é muito comum: templates.

    Existem inúmeras opções de frameworks que usam templates, para as mais variadas linguagens (inclusive foi lançado um, em PHP, que parece ser bem bacana, o Spaghetti).
    Em java, muito se usa JSP. Mas nem de longe são os mais bacanas: se você quer implementar uma aplicação MVC mesmo, deve usar o JSTL ou Velocity.

    E Velocity é com o que eu trabalho atualmente. Ela é ótima para uma aplicação de grande porte, que usa MVC: você escreve seu XHTML normalmente e, depois, utiliza os objetos que a camada de negócios te dá. Ou seja, você coloca apenas lógica de interface na interface. E a lógica de negócios fica na camada de negócios. E não tem como misturar uma coisa com a outra (ok, até tem, mas vai dar tanto trabalho e é tão mais difícil de fazer isso quando comparado com JSP, que desestimula qualquer POG…).

    Vamos falar um pouco, então, dele:




    Velocity

    Conceito

    É um engine baseado em java para a construção de templates. Um template velocity é um documento que permite a escrita de html com a inserção de lógica usando objetos do Java na sua inteligência, aplicando o conceito de MVC para separar a camada de negócios da camada de apresentação.

    Tipos

    Sendo baseado em Java, o código Velocity tem os mesmos tipos de objetos do Java, e podem ser usados todos os métodos dos objetos Java. Objetos são sempre chamados através do identificador $
    Por exemplo:
    $nome ou $quantidade
    Para definir um valor a um objeto, é usada a diretiva #set(
    objeto = valor)
    Não é necessário definir o tipo do objeto. O engine do Velocity se encarrega de definir a qual tipo ele pertence.
    Por exemplo: #set($nome = "Eduardo") ou #set($quantidade = 5)

    Arrays

    Para criar um array – um objeto que contêm uma lista de outros objetos, há duas formas:
    #set($meu_array = ["a", "b", "c", "d", "e"]) ##instancia-se o objeto e já passa o valor a ele
    ou

    #set($meu_array = []) ##instancia um objeto e cria uma lista vazia
    ##adiciona item a item os objetos da lista, usando o método add() da classe Array
    #set($foo = $meu_array.add("a"))
    #set($foo = $meu_array.add("b"))
    #set($foo = $meu_array.add("c"))
    #set($foo = $meu_array.add("d"))
    #set($foo = $meu_array.add("e"))
    ##como o método add() tem um valor de retorno, jogamos o valor do método em uma variável qualquer, $foo, para que esse valor não seja printado na tela

    Tomada de decisão

    Há um tipo de estrutura de decisão no Velocity, que é definida pela seguinte sintaxe:
    #if(condição)

    #elseif(condição)

    #else

    #end

    Nesse caso, apenas a diretiva #if(condição) e #end são obrigatórios; as demais ficam por conta da necessidade da implementação. A lógica é a mesma de qualquer estrutura de outras linguagens. Segue um exemplo completo:
    #if($c<5)
    ##só entra quando $c for menor do que cinco
    #elseif($c==5)
    ##só entra quando $c for igual a c
    #else
    ##entre nos demais casos
    #end

    Laço de repetição

    Há um tipo de estrutura para laço de peteição no Velocity – ou seja, como percorrer um Array (lista), que é definida pela seguinte sintaxe:
    #foreach($iter in $lista)

    #end

    Usando aquele Array criado no item Arrays, segue um exemplo completo da utilização:
    #set($meu_array = ["a", "b", "c", "d", "e"])
    #foreach($letra in $meu_array)
    $letra ##printa na tela a letra
    #end

    É possível também criar uma repetição de n vezes. Por exemplo, no código abaixo serão feitas 3 iterações (de 1 até 3):
    #foreach($c in [1..3])
    $c
    #end

    Macro
    Uma macro pode ter seu conceito simplificado para
    função carregada em memória. Deve ser utilizada quando um trecho de código é repetido várias vezes em diversos locais do seu código. Por ser carregada em memória e não interpretada em tempo de execução, a performance é maior.
    A sintaxe para criação e posterior acesso a ela segue o seguinte padrão:
    #macro(meu_nome $param1 $param2 .. $paramn)
    ##lógica da macro
    #end
    ##chama a macro
    #meu_nome($p1 $p2 .. $pn)

    Vale ressaltar que n é a quantidade de parâmetros que a macro recebe. Se ela não receber nenhum, é chamada a macro com os parênteses em branco.

    Referência: User Guide

     

    Shuffle – embaralhando listas e arrays com jQuery

    Faz tempo que quero lançar uma nova versão da Vitrine Fácil Buscapé, e isso está sendo desenvolvido. Como agora estudei e domino um pouco mais jQuery isso está facilitando muito os testes e desenvolvimento de novas ferramentas.

    Uma das idéias era permitir a exibição randômica de produtos, para isso pesquisei sobre arrays em javascript (é, eu não sei nada de js mesmo) e acabei descobrindo um interessante plugin desenvolvido por Ca Phun Ung (thanks!).

    O plugin (shuffle) serve para embaralhar um array ou todos os children dentro de um elemento HTML (listas, parágrafos, imagens…).

     

    Shuffle

    /*
     * jQuery shuffle
     *
     * Copyright (c) 2008 Ca Phun Ung
     * Dual licensed under the MIT (MIT-LICENSE.txt)
     * and GPL (GPL-LICENSE.txt) licenses.
     *
     * http://yelotofu.com/labs/jquery/snippets/shuffle/
     *
     * Shuffles an array or the children of a element container.
     * This uses the Fisher-Yates shuffle algorithm
     */
    (function($){
    	$.fn.shuffle = function() {
    		return this.each(function(){
    			var items = $(this).children();
    			return (items.length) ? $(this).html($.shuffle(items)) : this;
    		});
    	}
    	$.shuffle = function(arr) {
    		for(var j, x, i = arr.length; i; j = parseInt(Math.random() * i), x = arr[--i], arr[i] = arr[j], arr[j] = x);
    		return arr;
    	}
    })(jQuery);
    

     

    A utilização é bem simples:

     

    $(´ul´).shuffle();
    

     

    E para array:

    var arr = [1,2,3,4,5,6];
    arr = $.shuffle(arr);
    

     

    Veja um exemplo funcionando.

    Não poderia ser mais prático!

    Agora é só fazer vitrines, produtos, fotos randômicas…  🙂