Construindo uma classe PHP para acessar a API do BlogBlogs

Postei nesse último final-de-semana uma classe em PHP que fiz para acessar a API do BlogBlogs de uma forma fácil.
A API do BlogBlogs retorna através da arquitetura REST arquivos XML que representam informações do perfil de um usuário do BlogBlogs e de blogs cadastrados no sistema.

Download da classe aqui!

Primeiramente, criei duas classes, que abstraem esses dados em forma de objetos. Uma classe é a BlogBlogsBlog, um bean para a entidade blog, e outra a BlogBlogsUser, que por sua vez é um bean para o usuário do sistema. Cada classe recebe no construtor os atributos.

/**
* Dados do usuário do BlogBlogs
*/
class BlogBlogsUser {
    function __construct($username, $firstname, $lastname, $fullname, $thumbnailpicture, $profilepage, $blogs, $favorites, $fans) {
        $this->userName = $username;
        $this->firstName = $firstname;
        $this->lastName = $lastname;
        $this->fullName = $fullname;
        $this->thumbnailPicutre = $thumbnailpicture;
        $this->profilePage = $profilepage;
        $this->blogsCount = $blogs;
        $this->favoritesCount = $favorites;
        $this->fansCount = $fans;
        $this->blogs = array();
    }
   
}

/**
* Dados de um blog do BlogBlogs
*/
class BlogBlogsBlog {
    function __construct($name, $url, $rssurl, $blogpage, $lastupdate, $inboundblogs, $inboundlinks, $rank, $lang) {
        $this->name = $name;
        $this->url = $url;
        $this->rssUrl = $rssurl;
        $this->blogPage = $blogpage;
        $this->lastUpdate = $lastupdate;
        $this->inboundBlogs = $inboundblogs;
        $this->inboundLinks = $inboundlinks;
        $this->rank = $rank;
        $this->lang = $lang;
    }
}

Com isso, podia continuar a implementação. Criei a classe BlogBlogsService. Guardei no atributo apiUrl a url da API, e no construtor da classe recebo o valor da chave da API do BlogBlogs (semelhante ao processo de se usar a API do Google Maps, por exemplo).
Então criei alguns métodos, e os que são úteis para o desenvolvedor:

  • setUserName(): seta o username do usuário pelo qual faremos a busca no sistema do BlogBlogs. O método automaticamente busca todos os blogs e os dados do perfil do usuário
  • getBlogByUrl(): faz a busca por um blog através da sua url

Assim, temos implementadas todas as buscas possíveis pela API.

class BlogBlogsService {
    private $apiUrl = "http://api.blogblogs.com.br/api/rest/";
    private $userKey;
    private $doc;
   
    /**
    * construtor: recebe a chave da API do BlogBlogs
    * para gerar, acesse http://blogblogs.com.br/dev/chave
    */
    function __construct($k) {
            $this->userKey = $k;
            $this->user = null;
    }
   
    /**
    * recebe o userName no blogblogs e faz a consulta
    */
    function setUserName($userName) {
        $path = $this->apiUrl . "userinfo?key=" . $this->userKey . "&username=" . $userName;
        $this->doc = new DOMDocument();
        $this->doc->load($path);
        //verifica erros: caso tenha um nó com namespace error
        if($this->doc->getElementsByTagName("error")->length==1) {
            $this->errorMsg = $this->doc->getElementsByTagName("error")->item(0)->nodeValue;
            throw new Exception($this->errorMsg);
            return false;
        }
       
   
        if($this->doc->getElementsByTagName("result")->length!=1)
            return;
        //dados do usuário
        $userset = $this->doc->getElementsByTagName("result")->item(0);
        $blogBlogsUser = new BlogBlogsUser($userset->getElementsByTagName("username")->item(0)->nodeValue,
                                              $userset->getElementsByTagName("firstname")->item(0)->nodeValue,
                                              $userset->getElementsByTagName("lastname")->item(0)->nodeValue,
                                              $userset->getElementsByTagName("fullname")->item(0)->nodeValue,
                                           $userset->getElementsByTagName("thumbnailpicture")->item(0)->nodeValue,
                                              $userset->getElementsByTagName("profilepage")->item(0)->nodeValue,
                                           $userset->getElementsByTagName("blogs")->item(0)->nodeValue,
                                              $userset->getElementsByTagName("favorites")->item(0)->nodeValue,
                                           $userset->getElementsByTagName("fans")->item(0)->nodeValue);
        //dados de cada blog
        foreach($this->doc->getElementsByTagName("weblog") as $blog) {
            $blog = $this->createBlogWithXMLNode($blog);
            //$blogBlogsUser->addBlog($blog);
            $this->addBlogToUser($blogBlogsUser, $blog);
        }
        $this->user = $blogBlogsUser;
        return true;
    }
   
    /**
    * recupera um blog por uma url
    */
    function getBlogByUrl($url) {
        $path = $this->apiUrl . "bloginfo?key=" . $this->userKey . "&url=" . $url;
        $this->doc = new DOMDocument();
        $this->doc->load($path);
        if($this->doc->getElementsByTagName("weblog")->length!=1)
            return;
        $blog = $this->doc->getElementsByTagName("weblog")->item(0);
        $myBlog = $this->createBlogWithXMLNode($blog);
        return $myBlog;
   
    }
   
    function createBlogWithXMLNode($node) {
        $blog = new BlogBlogsBlog($node->getElementsByTagName("name")->item(0)->nodeValue,
                                  $node->getElementsByTagName("url")->item(0)->nodeValue,
                                  $node->getElementsByTagName("rssurl")->item(0)->nodeValue,
                                  $node->getElementsByTagName("blogpage")->item(0)->nodeValue,
                                  $node->getElementsByTagName("lastupdate")->item(0)->nodeValue,
                                  $node->getElementsByTagName("inboundblogs")->item(0)->nodeValue,
                                  $node->getElementsByTagName("inboundlinks")->item(0)->nodeValue,
                                  $node->getElementsByTagName("rank")->item(0)->nodeValue,
                                  $node->getElementsByTagName("lang")->item(0)->nodeValue);
   
        return $blog;
    }
   
    function addBlogToUser($user, $blog) {
        array_push($user->blogs, $blog);
    }
}

Vamos ver um passo-a-passo de como ver um atributo de um usuário:

$minha_chave = “XXXXXXXXXXXXXXX”;
$bbService = new BlogBlogsService($minha_chave);
$bbService->setUserName("ChrisB");
echo $bbService->user->fullName;

Se quisermos listar os blogs dele, exibindo as urls dos mesmos, acessamos a lista de BlogBlogsBlog que o BlogBlogsUser possue:

foreach($bbService->user->blogs as $blog) {
$blog->url . “<br />”;
}

Ainda podemos fazer a busca diretamente po um blog, pela sua url:

$bbService->getBlogByUrl(”http://www.chrisb.com.br/blog/”)
//para exibir o nome do blog
$bbService->getBlogByUrl(”http://www.chrisb.com.br/blog/”)->name;

Creio que os exemplos, mesmo simples, mostram a facilidade de uso. []s!

Crie um cronômetro em javascript

Como saber qual forma de implementar um código em javascript roda mais rápido, com melhor perfomance? Ou, ainda, qual browser interpreta um código mais rápido?

Criei uma classe bem simples: instancia-se ela, chama o método start() para iniciar o cronômetro e o stop() para parar. O stop() retorna a diferença de tempo em segundos.

A implementação da classe é:

/*
classe para contador de tempo
*/
function TimeCounter() {
    this.startDate = null;
    this.ellapsedTime = null;
       
    //inicia o timer
    this.start = function() {
        this.startDate = new Date();
    }
       
    /*
    para o timer e retorna a diferença entre a hora atual e a em que foi startado/ dividindo
    por mil pois a diferença é dada em milisegundos
    */
    this.stop = function() {
        return (new Date() – this.startDate)/1000;
    }
}

Segue um uso da mesma; ao carregar a página (onload() da janela) inicio o cronômetro e crio 1000 divs, adicionando-as ao body. Após acabar o laço, paro o cronômetro. *faça o teste em diversos browsers e veja a diferença de performance

<!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>Time Counter</title>
<script type="application/javascript">
    function TimeCounter() {
        this.startDate = null;
        this.ellapsedTime = null;

        this.start = function() {
            this.startDate = new Date();
        }
       
        this.stop = function() {
            return (new Date() – this.startDate)/1000;
        }
    }
   
    var timeCounter = new TimeCounter();
    window.onload = function() {
        timeCounter.start();
        for(var i=0; i<1000; i++) {
            var div = document.createElement("div");
            div.appendChild(document.createTextNode(i));
            document.body.appendChild(div);
        }
        alert(timeCounter.stop());
    }
</script>
</head>
<body>
</body>

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]