Redis e PHP: Who is online?

Redis e PHP: Who is online?

Em um micro projeto precisei manter uma lista de usuários online, são usuários de várias fontes em conexões não persistentes, então fiz uma solução simples em PHP e Redis.

Primeiro você precisa conhecer o Redis e te-lo rodando, o que é bem simples:

$ git clone git://github.com/antirez/redis.git
$ cd redis
$ make
$ ./src/redis-server

Com o redis funcionando vamos usar a biblioteca Predis para trabalhar com Redis em PHP, pegue uma cópia atual e prepare o Phar do projeto:

$ git clone git://github.com/nrk/predis.git
$ cd predis
$ php bin/createPhar.php
$ mv predis-*.phar seu/projeto/predis.phar

Com as dependências prontas vamos a lógica para os usuários online: Vou implementar um método “ping” em um controller qualquer, a cada ação do usuário uma chamada ao “ping” vai ocorrer.

A cada ping o nome do usuário vai ser inserido em um “set” no redis, vou ter um “set” para cada minuto, mas usando “expire” só vou manter os sets dos ultimos 5 minutos.

Para descobrir os usuários online faço “sunion” dos ultimos 5 minutos para saber quem está online. Você pode ajustar o tempo como achar mais justo para sua aplicação.

Segue o “pseudo-controller”, devidamente comentado (no projeto é meio diferente, mas a base é essa):

PS: o Predis é para PHP 5.3, o que não deve ser um problema, certo?

Desenvolvimento de módulos para o Drupal

Desenvolvimento de módulos para o Drupal.

Minhas atividades web receberam recentemente um adereço interessante: O Drupal. E uma das minhas tarefas recentes foi o estudo para uma apresentação sobre desenvolvimento de módulos para o mesmo. Embora apresentação não esteja tão boa quanto gostaria estou disponibilizando o conteúdo. Pretendia acertar algumas coisas antes, mas vou deixar isso para depois.

A apresentação tem duas partes, os slides e um documento que criei antes para me basear.

Você baixar o PDF da Apresentação sobre Desenvolvimento de Módulos para Drupal. Vou reproduzir o documento (rascunho) na integra abaixo.

O Drupal e desenvolvimento de módulos para Drupal

O que é o Drupal:

Drupal é um framework para gestão de conteúdo. É uma ferramenta livre, de código aberto e comunidade atuante.
Drupal fornece a base para construção de portais de internet e intranet para gestão de conteúdo (e conhecimento), permitindo e incentivando a atuação colaborativa, compartilhamento de recursos e publicação de conteúdo de uma forma geral.
É extensível através de módulos, que são como plugins ou add-ons para acrescentar ou alterar funcionalidades no sistema. Quase tudo no Drupal é um módulo.
É desenvolvido em PHP e roda sobre a plataforma Apache + MySql (ou PostgreSQL).
Praticamente todas as funcionalidade do Drupal são módulos, como o conteúdo, o controle de acesso e etc, e todo funcionamento do Drupal é padronizado através de suas interfaces de Hooks e API. 

Drupal: http://drupal.org/

Conteúdo (nodes):

O Conteúdo no Drupal tem sua base em alguns elementos:

  • Campos (Fields)
  • Tipos de conteúdo (Content type)
  • Conteúdo (nó ou node)

Os tipos de conteúdo possuem uma série de campos que podem ser de vários tipos (texto, mapa, data, arquivos, imagens, relacionamentos com outros tipos e etc) que definem o modelo de um conteúdo. Cada conteúdo (ou página) é um nó no drupal, e possui um URL, identificador e o conteúdo dos seus campos de acordo com o tipo de conteúdo ao qual pertence.

Hooks:

Hooks são as formas dos módulos se conectarem ao Drupal e aos outros módulos. O próprio núcleo (core) do Drupal implementa os Hooks que precisa.

Um hook é um gancho a uma chamada do sistema, são funções definidas em pontos chaves do sistema (como carregar um conteúdo, renderizar um bloco, listar itens de menu, salvar um conteúdo, enviar um formulário, etc), assim os módulos registram funções próprias para serem chamadas nesses pontos do sistema.

Quando o sistema vai executar uma ação que possua um gancho (hook), ele executa então todas as funções registradas para esse gancho, fazendo assim a integração do sistema.

Para se registrar um hook basta criar uma função com o nome sendo o o nome do módulo + “_” + o nome do hook. Assim para implementar o hook “menu” no módulo “meumodulo” basta criarmos a função “meumodulo_menu”. 

Exemplo:

A ação de visualizar uma página especifica de um conteúdo, no momento de carregar esse conteúdo existe um gancho chamado “nodeapi”. Então quando o sistema prepara esta página, ao carregar o conteúdo ele passa esse “node” a ser exibido para todas os módulos que registraram uma função no hook “nodeapi”, ele podem então executar ações e alterar esse node, que depois será exibido para o usuário, através da implementação da hook “nodeapi” do próprio módulo “node”.

Mesmo módulos podem criar hooks para que outros módulos possam usar. É assim que drupal integra suas próprias funcionalidades.

São mais de 80 Hooks no núcleo do drupal, mais o que cada módulo pode oferecer (cck, node, contentaccess, etc).

Alguns Hooks importantes são:
 
  • form – Formulários
  • nodeapi – Trabalhar com nodes
  • menu – Itens de menu e páginas (urls)
  • block – Blocos de conteúdo
  • user – Controle de usuário
  • cron – Ações agendadas
Para mais sobre hooks: http://api.drupal.org/api/group/hooks.

APIs:

Outra parte essencial do Drupal são as APIs. APIs são conjuntos de funções relacionadas a alguma utilidade ou parte do sistema. Diferentes dos hooks elas são para serem chamadas livremente aonde quer que os módulos precisem de suas funcionalidades. As APIs são basicamente todas as funções fornecidas pelo core e por todos os módulos do Drupal.

Toda função de todo módulo é disponível para todo outro módulo, tornando o sistema extremamente padronizado através do reuso de recursos.

Então as APIs do drupal são funções fornecidas pelo sistema, como:

  • db_query – Para consultas ao banco de dados
  • t – Para recuperar string traduzidas (internacionalização)
  • drupal_set_message – Mensagens para o usuário
  • node_load – Carregar módulos
  • user_load – Buscar dados de um usuário
  • form_set_error – Mensagens de erro

São milhares de funções disponíveis: Cache API, File API, Field API, Database API, RDF API, Schema API, etc. Mais o que cada módulo pode disponibilizar, e existem muitos módulos que lançam extensas APIs: CCK, Openlayers e etc oferecem APIs.

Para mais sobre API: http://drupal.org/node/326 .

Desenvolvendo seu módulo:

Você não precisa desenvolver um módulo.

O sistema de conteúdo do Drupal é muito flexível, ele pode por si só atender uma grande demanda. E também existem milhares de módulos prontos para as mais variadas funções, alguns extremamente flexíveis como o CCK (que estende o sistema de conteúdo do Drupal). Ainda assim alguém já pode estar trabalhando em um módulo parecido com o que você acha que precisa, e é preferível que se unam os esforços em um só módulo, ou até se já existe um módulo com funcionalidade próxima é mais aconselhável que se unam num módulo só.

A comunidade drupal preza muito por padronização e qualidade, e sempre recomenda (e efetua) a fusão de módulos semelhantes por um módulo mais completo e de maior qualidade.

Mais ainda assim não existe tudo pronto, e em casos de necessidades especificas pode-se precisar de um novo módulo.

O primeiro passo é o http://drupal.org/project/modules aonde você pode conferir os módulos já existentes e ver tem algum que vale a pena usar ou adaptar. A comunidade Drupal é realmente muito forte em integração e união. 

O segundo lugar a visitar é http://groups.drupal.org/contributed-module-ideas , é um grupo de ideias e pedidos para módulos, da mesma forma que pode achar o que precisa  já pronto, pode-se achar ainda em planejamento e até inserir sua ideia e conseguir colaboradores (ou alguém que indique aonde isso já foi feito).

Se for então começar a colocar a mão na massa, a principal referência é http://drupal.org/contributors-guide .

Desenvolvendo seu módulo, mão na massa:

Decidido o módulo que quer, as funcionalidade e etc é hora de começar a programar mesmo. Existe uma padronização importante no código do drupal, que facilita manutenção e colaboração, e está definido no http://drupal.org/coding-standards , de fácil e rápida leitura. A parte de SQL (http://drupal.org/node/2497) também é importante e simples.

A estrutura mais básica de um módulo consiste em três arquivos:

  • nome_do_modulo.info
  • nome_do_modulo.module
  • nome_do_modulo.install

O arquivo de INFO contêm a descrição e informações básicas do módulo, como um arquivo de propriedades, as principais informações são:

  • name: O nome do módulo
  • description: A descrição
  • core: Versão do drupal ao qual se destina
  • version: Versão da instalação [opcional se hospedado no drupal.org]
  • dependencies: Módulos do qual depende [opcional]
  • package: O “pacote” de módulos ao qual pertence, ex: “content”, “views”,”others” [opcional]

Segue um exemplo de arquivo .info:

name = Meu Primeiro Módulo
description = Testa o sistema de módulos do Drupal
version = 0.1
core = 6.x
dependencies[] = cck
O arquivos INSTALL contêm as instruções de instalação (se houver alguma), e é escrito em PHP seguindo o esquema de “hooks”. O arquivo pode implementar dois hooks: “install” e “uninstall”, então se o o módulo se chama “meumodulo”, basta implementar esses dois hooks criando as funções “meumodulo_install” e “meumodulo_uninstall”:
<?php
function meumodulo_install() {
}
function meumodulo_uninstall() {
}

?>

Dentro das funções programa-se os passos da instalação.

Parte comum da instalação de um módulo é a criação de tabelas no banco de dados, para isso usa-se o hook de schema do drupal, implementa o hook como “meumodulo_schema”, no mesmo arquivo:

function meumodulo_schema() {
    $schema[‘meumodulo’] = array(
    ‘description’=>’descrição’,
    ‘fields’=>array(
        ‘id’=>array(‘type’=>’int’,’not null’=>true),
        ‘name’=>array(‘type’=>’text’,’default’=>”)
    ) ,
    ‘primary key’=> array(‘id’)
    );
    return $schema;    
}

Nesse caso precisamos ainda definir que queremos instalar um schema nos hooks install e uninstall:

function meumodulo_install() {
    drupal_install_schema(‘meumodulo’);
}
function meumodulo_uninstall() {
    drupal_uninstall_schema(‘meumodulo’);
}

Finalmente o arquivo MODULE contém os códigos do modulo em si, implementando os hooks e as funcionalidades que o módulo vai oferecer, também é um arquivo PHP comum. Supondo que o módulo adicione uma página simples que exibe alguns dados salvos no banco de dados. Primeiro para criar uma página implementamos o hook “menu” para essa url:

<?php
function meumodulo_menu() {
    $menu = array();
    $menu[‘meumodulo/lista’] = array(
        ‘title’ => ‘Lista de itens do meu modulo’,
        ‘description’ => ”,
        ‘page callback’ => ‘meumodulo_lista’,
        ‘type’ => MENU_NORMAL_ITEM
    ) ;
    return $menu;
}

Agora implementamos o “meumodulo_lista” para montar a página:

function meumodulo_lista() {
    $query = db_query(‘SELECT nome FROM {meumodulo}’);
    $rows= array();
    while($row = db_fectch_array($query)) {
        $rows[] = array($row[‘nome’]);        
    }   
    $header = array(‘Nomes’);
    return theme(‘table’,$header,$rows);        
}

Nessa função usamos 2 APIS importante: A de banco de dados (db_*) e de temas.

A de banco de dados, db_query monta uma consulta SQL e realiza essa no banco de dados usado no Drupal, enquanto db_fetch_array transforma o resultado da consulta em um array e avança o cursor nesse resultado.

A função theme recebe o tipo de thema a ser usado, e os parâmetros de acordo com esse tema, e então monta o html de acordo com o tema atual do drupal. No caso usamos o template de table e passamos a lista de cabeçalhos e as linhas da tabela. Combinações mais complexas são possíveis.

Agora vamos adicionar uma página que permitiria inserir esses dados no banco de dados, primeiro acrescentariamos na nossa implementação do “menu” a nova página:

function meumodulo_menu() {
(…)
    $menu[‘admin/settings/meumodulos’] = array(
        ‘title’ => ‘Admin da lista de items do meu modulo’,
        ‘description’ => ”,
       ‘page callback’ => ‘drupal_get_form’,
        ‘page arguments’ => array(‘meumodulo_admin’),
        ‘access arguments’ => array(‘access administration pages’),

        ‘type’ => MENU_NORMAL_ITEM
    ) ;
(…)
}

Dessa vez adicionamos a restrição de apenas quem tenha acesso a páginas administrativas, e ao invés de montarmos a página vamos usar a api de formulário drupal_get_form, implementamos então o meumodulo_admin:

function meumodulo_admin {
    $form = array();
    $form[‘meucampo’] = array(
        ‘#type’=>’text’,
        ‘#title’=>’Nome’,
        ‘#default_value’=>’Insira um nome’
    );
    $form[‘submit’] = array(
        ‘#type’=>’submit’,
        ‘#value’=>’Save’,
        ‘#submit’=>array(‘meumodulo_admin_submit’)
    );
    return $form;
}
function meumodulo_admin_submit($form,$state) {
    $novo_nome = $state[‘meucampo’];
    db_query(‘insert into {meumodulo}  (nome) values (‘%s’);’,$novo_nome);
}

E assim a primeira funcionalidade do módulo estaria pronta, e pronto para ser instalado.

A instalação de um módulo no Drupal se dá apenas copiando sua pasta para a pasta “modules” do drupal e ativando o módulo através da interface administrativa do drupal.

E depois?

Acontece que geralmente um módulo não é tão simples, e precisa de várias funcionalidades, e como não é possível saber todas ou cobrir todas aqui, segue um roteiro para facilitar o desenvolvimento:

  1. Pense bem no que o módulo vai fornecer e no esquema de banco de dados
  2. Separe as funções do módulo em partes funcionais:
    1. Páginas
    2. Formulários
    3. Listas
    4. Integrações com outros módulos (cck, biblio, openlayer)
    5. Interfaces (blocos/views/temas)
    6. Interações com conteúdo (nodes)
    7. Etc
  3. Para cada função do módulo consulte a lista de hooks para ver qual pode ser usado.
  4. Escolhido o(s) hook(s) a implementar veja se há uma API que ajude na tarefa
  5. Implemente conforme imaginado
  6. Teste
  7. Publique

Outros links importantes:

PHPDocumentor: Documentação de API em PHP

Outra parte importante em um projeto, principalmente se está tratando de pacotes públicos e frameworks, é uma documentação completa. Mas documentar é um saco.

Parte da documentação incluí a API de classes, com os pacotes, classes, métodos e suas descrições. O padrão para documentação destes são o docblocks, ou phpdoc, que são comentários no código, sobre a classe, propriedade ou método, e possuem descrição e tags padrões para completar a documentação.

< ?php

/**
* Class Foo
*
* This class implements a hello speaker
* @package hello
*/
class Foo {
/**
* this is some useless var
*/
private $bar ;

/**
* Says hello to provided name
* @param string $name this is the name to be said
* @return Foo
*/
public function hello($name="world") {
echo "Hello, ".ucfirst($name)."!";
return $this
}
}

Esses comentários comuns são mais fáceis de escrever durante o desenvolvimento, e acrescentam muito a documentação. O padrão permite descriçoes muito mais complexas que isso, mas na maior parte das vezes não é necessário.

Agora que esses comentários estão em seu lugar basta usar uma ferramenta para transformar o código em documentação de verdade, pronto para ir online e estar disponível para consulta.

O PhpDocumentor é extremamente fácil de usar e instalar, e gera documentações razoáveis. Ele possui bastante opções de formato de saída (html, pdf, xml…) e templates, mas vou fazer só o básico aqui.

Para instalar o PhpDocumentor você pode usar o PEAR ( se você não conhece, está perdendo muito), basta rodar o comando a seguir:

# pear install PhpDocumentor

Se tudo correr bem você pode agora transformar seu código em documentação com a linha a seguir:

$ phpdoc -t docs -o HTML:default:eathli -d src

Nesse caso a documentação será salva na pasta docs, no formato HTML usando o template earthli, através dos arquivos da pasta src.

Basta apontar seu navegador para a pasta docs e navegar na documentação agora.

 

Documentação em PHP

 

 

Confira mais opções comentários, anotações e tudo mais no Manual do PhpDocumentor.

Testes Unitários em PHP com simpletest

Testes automatizados são parte importante do desenvolvimento de software, apesar de ser a mais demorada prática a se aceitar, é a que mais traz benefícios. O seu uso deve ser simples, e por isso é bom o uso de um framework prático para tal.

No começo escrevi scripts de testes “na mão” mesmo, com ifs e elses, mas, apesar de ser melhor que nada, não é nada prático se comparado ao uso de um bom framework. Então tentei usar o phpunit uma vez, mas achei mais complexo que o necessário, então parti para o simpletest.

O simpletest é realmente simples, basta fazer o download do pacote e descompacta-lo na sua pasta de testes, e em seguida é só escrever sua suite de testes.

Uma suite de testes é escrita da seguinte forma:


< ?php

include_once 'simplestest/autorun.php';

class AllTests extends TestSuite {
function allTests() {
$this->TestSuite("Meus testes");
$this->addTestCase(new MeuCasoDeTests1);
$this->addTestCase(new MeuCasoDeTests2);
}
}

?>

E cada caso de teste se define da sequinte forma:


< ?php

include_once 'simpletest/autorun.php';

class MeuCasoDeTests1 extends UnitTestCase {
function testFoo() {
$this->assertEqual("foo","foo");
}
function testBar() {
$this->assertEqual("foo","bar");
}
}
?>

Cada função no caso de teste iniciada por “test” será executada na ordem declarada, e o cada “assert” é um teste. Existem vários “asserts” disponíveis.

Basta acessar pelo browser a url do caso de testes ou da suite de testes e ter certeza de testar tudo, para garantir a qualidade do software.

Google App Engine DataStore com JDO e PHP

No artigo que explico a configuração inicial para rodar aplicações em PHP no Google App Engine (GAE), cito um pouco sobre a parte de persistência. Neste artigo pretendo aprofundar nesta parte, na prática.

Primeiro é importante lembrar que o GAE não oferece a API do JDBC, assim não podemos acessar bancos externos. Então no PHP não temos as funções comuns de BD, como mysql_connect ou PDO.

O banco de dados usado no GAE não segue um modelo relacional, como a maioria (principalmente no php) esta acostumado. GAE usa o datastore como um banco de dados baseado em Entidades. Essas entidades são como objetos, são na verdade estruturas de dados com chaves e valores.

Interessante notar que as estruturas são “schema-less”, ou seja, não precisam ser pré-definidas, os pares de chave=>valor serão gerados conforme forem enviados. Assim mudanças nos objetos não precisam serem “migradas” ao banco de dados.

Outro ponto importante nisto, é que, como não estamos lidando com dados relacionais, o pensamento de JOINs deve ser repensado. Na verdade, de preferencia, esquecidos. O datastore funciona muito bem em casos de relações proprietárias exclusivas, como classes “embedded”, mas JOIN como estamos acostumados em bancos relacionais são um inferno.

As entidades devem ser definidas como classes Java, usando as anotações devidas, e em seguida precisam ser “Enhanced” para gerarem o código da persistência em si. Vamos começar agora a gerar as nossas entidades.

O projeto será simples, como o : Teremos apenas User e Note. O pacote principal será o “app”, e o modelo estará em “app.model”. Então precisamos das pastas “src/app/model”. Faça o download do projeto e o coloque próximo ao SDK.

Vamos definir primeiro uma interface para identificar nossos objetos persistentes, essa será útil mais tarde. Em /src/app/model crie IModel.java .

Como disse, primeiro definimos nossas entidades, em Java. Primeiro a classe User, em src/app/model/User.java . Sem as partes das anotações, definimos a classe normalmente. Ele possui alguns dados básicos, e uma lista de Notas. Os atributos são privados e usamos acessores.

Agora vamos adcionar as anotações, e temos o User persistente. A primeira coisa é a anotação “PersistenceCapable” que define que a classe é persistente. Temos ainda a tag “PrimaryKey”, e a tag “Persistent”, que define que o atributo deve ser salvo. O “ValueStrategy” define que esse atributo será auto-gerado, então o próprio datastore vai gerar nossas chaves.

É importante ainda o “deatached=true” pois permite usar o objeto mesmo quando o datastore for fechado.

Repare porem que a lista de notas não é persistente, e sim “NotPersistent”, a tag é colocada para garantir que não vai haver conflito. Como disse antes vamos fazer as junções na mão.

Em seguida mapeamos a classes Note, sem a anotações. Temos alguns dados. Reparem no campo Text. Uso Text ao invés de String porque na datastore o string tem limite, então usamos o Text. Pode reparar ainda que temos o “User owner” que a parte de cá da relação, mas também temos o “Long userId” que é o que vai ser usado na realidade. Repare no método que define o “owner” automaticamente estou definindo o “userId”.

Fazemos então as anotações, dessa vez nada novo.

Agora que temos nossas classes mapeadas, podemos partir para parte de acesso ao banco de dados. Vou usar um modelo bem simples, além do PMF (padrão, já tem no blankphp) vou usar apenas uma classe auxiliar Storage (em /src/app) que vai me permitir acessar de forma geral o datastore. Agora vamos acompanhar a classe src.app.Storage método a método.

O primeiro método é o “getAll”, ele me permite recuperar todos os objetos de uma determinda classe, basta receber uma String que é o nome completo da classe ( “app.model.User” ou”app.model.Note”), e me retorna uma lista com elas.

Na primeira linha eu faço o acesso ao gerenciador de persistência, e digo que meus objetos não devem ficar alocados nele. Depois declaro a lista de model que vou retornar., crio a query, e a executo e armazeno o seu retorno em uma lista temporária.

Pegadinha número um. Para liberar os objetos vou passa-los a uma segunda lista.

Então, se houver objetos a retornar ( !=null e size >=1), eu crio minha lista, e a preencho com os objetos da temporária. Ainda garanto a associação com a persistência deles.

Caso qualquer coisa dê errado ou não tenha objetos, eu retorno nulo mesmo. E , lembre-se sempre, fecho o datastore.

O segundo , getWhere, funciona idêntico ao primeiro exceto que recebe uma clausula “where” e a adciona a query.

O terceiro, get, retorna um objeto pelo seu ID, sua chave primaria. Ele funciona de forma semelhante aos anteriores, exceto que na query eu identifico que quero pelo ID. E, ao invés de formar uma lista, eu recupero o primeiro item desta. Retorno este item (ou nulo) e, sempre, fecho o datastore.

O método sequinte, o quarto, é o put. Ele é o encarregado por salvar (tanto inserir, quanto atualizar) um objeto no datastore. Como pode ver a chamada é simples, temos apenas o “makePersistent”, que já usamos antes.

A lógica é simples, se eu passo um objeto ao makePersistent que não veio de uma consulta (que eu não recuperei com um dos métodos acima) será gerado um INSERT, caso seja um objeto já persistente, ou seja, que veio do datastore ele será atualizado.

O último método agora é o delete, sem mistérios.

Agora podemos compilar as classes com a seguinte linha, na raiz do projeto, use o comando:

“ant compile”

E “enhance” as classes com o comando:

“java -cp ../appengine-java-sdk/lib/appengine-tools-api.jar:war/WEB-INF/classes:../appengine-java-sdk/lib/user/appengine-api-1.0-sdk-1.2.1.jar com.google.appengine.tools.enhancer.Enhance war/WEB-INF/classes/app/model/User.class war/WEB-INF/classes/app/model/Note.class”

Enfim, a parte em Java esta pronta, no próximo vamos partir para a parte em PHP.

RestClient indicado ao Innovation Award do phpclasses.org

Post rapidinho para comemorar que o RestClient foi indicado ao Innovation Award de Julho no phpclasses.org.

Outra classe minha lá , a RestServer, foi indicada em Março e ficou em segundo lugar. Agora o RestCliente, que é bem mais simples mas também é Rest :)

O RestClient provê uma API simples para chamadas HTTP, focando em Webservices RESTful.

Enfim, veja os candidatos e faça seu voto. O código de ambas está no Github. 

Testes automáticos para webservices Restful, em PHP

Testes unitários é uma ferramenta essencial para manter a qualidade do código e garantir que esteja tudo funcionando entre manutenções e upgrades. Se você não aplica, deveria :)

O fato é que geralmente testamos a aplicação “por dentro”,  com testes unitários para as classes e methodos dessas, mas na construção de um webservice pode ser interessante testar a aplicação “por fora”, realmente acessando a API com entradas válidas e inválidas para garantir que esteja respondendo bem às chamadas de clientes com os devidos cabeçalhos (Content-Type, Response Code e Message, etc) e o conteúdo em si esta correto. Teste de integração e outros são comumente negligenciados (inclusive por mim…).

Com isto em mente preparei um pequena classe em PHP, parte do pacote RestServer: a RestClient. É uma classe de uso geral para acesso HTTP através de cURL, pensando em testes para APIs de webservices.  O uso é simples, pode ser ver no exemplo/teste, veja por exemplo com atualizar o twitter:

<? 

$twitter = RestClient::post(
“http://twitter.com/statuses/update.json”
,array(“status”=>”Working with RestClient from RestServer!”)
,”username”
,”password”);
 

var_dump($twitter->getResponse());
var_dump($twitter->getResponseCode());
var_dump($twitter->getResponseMessage());
var_dump($twitter->getResponseContentType());

 ?>

 Você pode então combina-la com sua suite testes favorita (ou escrever na mão, se for chato) para testar sua API, veja partes dos testes do IdeasWall.org :

// Authorized access should be forbidden
$http = RestClient::get($base.”/ideas.json”);
if($http->getResponseCode() != 401) {
echo “Unauthorized access: Wrong response code\n”;
echo $http->getResponseCode().”\n”;
return false;
}

if($http->getResponse() != “Unauthorized”) {
echo “Unauthorized access: Wrong response\n”;
echo $http->getResponse().”\n”;
return false;
}

 // Will now create an idea , should create
$http = RestClient::post($base.”/ideas.json”,array(“idea”=>”I Rock”,”tags”=>”test”,”priori”=>”1″),”diogok”,”123″);
if($http->getResponseCode() != 201) {
echo “Create idea: not created.n”;
echo $http->getResponseCode().”\n”;
echo $http->getResponse().”\n”;
return false;
}
$ideaJson = $http->getResponse(); // This is the idea created;
$idea = Zend_JSon::decode($ideaJson,Zend_Json::TYPE_OBJECT);
if($idea->idea != “I Rock”) {
echo “Idea not created properly: Wrong idea\n”;
var_dump($idea);
return false;
}

// save map
$http = RestClient::post($base.”/ideas/”.$id.”/map.json”,array(“x”=>100,”y”=>200),”diogok”,”123″);
if($http->getResponseCode() != 200) {
echo “Erro at save map\m”;
echo $http->getResponseCode().”\n”;
echo $http->getResponse().”\n”;
return false;
}

$ideaJson = $http->getResponse(); // This is the idea updated;
$idea = Zend_JSon::decode($ideaJson,Zend_Json::TYPE_OBJECT);

if($idea->map->x != “100” || $idea->map->y != “200”) {
echo “Error in return idea map\n”;
var_dump($idea);
return false;
}

Claro que há muitas linha no arquivo de teste, mas deu para entender. 

Entrando na computação nas nuvens, com PHP e o Google App Engine

Continuando as aventuras em diferentes plataformas, hoje vamos levar o PHP para as nuvens com o Google App Engine. Se alguem já rodou o PHP no Quercus, fique tranquilo por que este é bem mais fácil.

Você vai precisar criar um conta no GAE na plataforma Java, que é early access ainda. Enquanto espera a liberação da conta, vamos preparar o ambiente de desenvolvimento.

Primeiro baixe o SDK java do google app engine da página do google, é o appengine-java-sdk. Extraia-o em algum lugar onde possa trabalhar com ele.

Agora pegue o projeto em branco do PHP, já pronto para o GAE, e o extraia proximo ao SDK.

Para ver o “hello world”, você precisa executar o servidor de desenvolvimento para esta aplicação. O SDK vem com o utilitário para tal, a linha de comando para iniciar o servidor é esta:

$ appengine-java-sdk/bin/dev_appserver.sh blankphp/war

Basta chamar o script do servidor e passar o caminho do war da aplicação, agora esta acessivel em http://localhost:8080

Agora para começar seu próprio projeto crie um cópia do blankphp com o nome do seu app. Nesta nova pasta edit no arquivo  war/WEB-INF/web.xml, próximo do fim, a entrada servlet-mapping que aponta para phpblank para apontar para o novo projeto. Depois edite o appengine-web.xml na mesma pasta para o application corresponda a sua aplicação. Agora basta apontar o servidor para a nova pasta.

O desenvolvimento se dá colocando o conteúdo do “site” na pasta war, a partir da raiz. É legal colocar também um favicon.ico na raiz, senão a app engine reclama disso. No arquivo WEB-INF/urlrewrite.xml é possivel configurar redirecionamentos de URL como o Apache, no formato <rule><from>^$</from><to>index.php?$1</to></rule>.

Conforme a colaboração do Igor Cemim (lá no comentários), veja que por padrão o GAE não assume o index.php como arquivo de entrada padrão, ou seja ao entrar na URL sem /index.php não aparece nada. A solução é incluir a seguinte regra no urlrewrite.xml. Valeu Igor!

<rule>
<from>^/$</from>
<to>index.php</to>
</rule>

Graças ao Quercus, você pode fazer chamadas das classes java direto dentro do PHP, seguindo o exemplo:

<?

import  com.google.appengine.datastorage.Text ;
$text = new Text( “String”);
echo $text->getValue();

?>

Veja que rodar uma aplicação no GAE não é a mesma coisa que no LAMP, a maior mudança é , sem dúvidas, no banco de dados. Por que não tem.

Não é que não tenha, mas como o JDBC não esta disponível, também não estão o PDO e *_connect() do PHP/Quercus. A solução de storage do GAE é o datastorage, atráves de JDO, JPA ou JDOQL.

Para usar o JDO você precisa criar as entidades em Java, no blankphp vem um exemplo bem fraco de como faze-lo. Você vai precisar compilar as classes com o ant (o build.xml já esta configurado), usando:

$ ant compile
E “enhance” a classe a ser persisitida (caso não esteja no build):

$ java -cp ../appengine-java-sdk/lib/appengine-tools-api.jar:war/WEB-INF/classes:../appengine-java-sdk/lib/user/appengine-api-1.0-sdk-1.2.1.jar com.google.appengine.tools.enhancer.Enhance war/WEB-INF/classes/app/JsonJDO.class

Lembrando de corrigir os caminhos para os JAR de acordo. Para usar o exemplo no PHP você poderia fazer assim:

<?

import Storage ;
$st = new Storage ;
$obj = $st->get($_GET[‘id’]);
$json = $obj->getJson()->getValue();

$jsonNovo = ‘{“nome”:”Diogo”}’;
$st->put($_GET[“id’],$jsonNovo);

?>

Agora, depois de escrever suas classes Java, seus arquivos PHP, frameworks etc, e tiver testado bastante (localhost:8080), é hora de enviar para o GAE, com a seguinte linha:

$ appengine-java-sdk/bin/appcfg.sh update blankphp/war

Trocando, claro, o blankphp pelo nome do projeto, depois vai pedir login e senha, e pronto, basta acessar seu app no GAE e ver tudo funcionar(ou não!).

Rodando PHP no Quercus, no Linux

Conhece o Quercus? Se não, é um servlet "mui legal" capaz de rodar PHP em cima da plataforma Java, que tem ótima performance e capaz de integrar Java e PHP de forma transparente. Você pode inclusive usar as Classes do Java no PHP diretamente, e ter recursos como Threads e os frameworks PHP rodando lado-a-lado. 

E por que precisamos disso se já temos  o próprio PHP? Primeiro é sempre bom ter opções, mas também legal para configurar o PHP em ambiente já pronto para o Java, como o Google App Engine, e que por algum motivo ainda não suporta PHP. 

Agora vamos ao trabalho sujo, como configurar o Quercus com o Jetty no Linux. Primeiro, instale o Jetty, baixe a versão mais recente do Codehaus, como root:

# mkdir  /opt/jetty 
# unzip jetty*.zip
# cp -R jetty-version/* /opt/jetty

Por padrão o jetty usa a porta 8080, então depois de inicia-lo basta chamar "localhost:8080" no navegador, para lançar o jetty use o sequinte comando

# cp /opŧ/jetty
# java -jar start.jar

Agora é a vez do Quercus, vá ao site dele e baixe a versão war mais recente.(quase no fim da página), e então:

# jar -xv quercus*.jar
# cp WEB-INF/lib/* /opt/jetty/lib/

Agora é configurar o Jetty para usar o quercus, vamos adcionar a webapp no /opt/jetty/test/WEB-INF/web.xml , adcione dentro da tag <webapp> :

<servlet>
  <servlet-name>Quercus Servlet</servlet-name>
  <servlet-class>com.caucho.quercus.servlet.QuercusServlet</servlet-class>
</servlet>
<servlet-mapping>
  <servlet-name>Quercus Servlet</servlet-name>
  <url-pattern>*.php</url-pattern>
</servlet-mapping>
<welcome-file-list>
  <welcome-file>index.html</welcome-file>
  <welcome-file>index.php</welcome-file>
  <welcome-file>index.jsp</welcome-file>
</welcome-file-list>

Depois crie um arquivo webapps/test/index.php com <? phpinfo() ?>, e esta pronto. Basta acessar http://localhost:8080/index.php e ver o quercus em ação.

Anotações e Persistencia, no PHP.

Então eu já falei sobre anotações , e também sobre persistência. Vamos juntar as peças?

Um pacote em PHP para leitura de classes com anotações e geração da configuração do ORM a partir dessa. Por enquanto o ORM é o Outlet, pretendo ainda usar o Doctrine. Para annotation usa o addendum.

Então, como usar?

 $config = new PersistenceAnnotation('mysql:host=localhost;dbname=encontros','encontros','123');
 $config->addClass('User')->addClass('Encounter')->addClass('Device');
 // Starts the Persistence Framework
 Outlet::init($config->getConfig());
 $outlet = Outlet::getInstance();
 $outlet->createProxies();

E, é claro, os modelos, devidamente anotados:

  /** @Persistence('devices') */
  class Device {
  /** @Persistence(type='int',props={
  pk=true,autoIncrement=true}) */
  private $id ;
  /** @Persistence() */
  private $mac ;
  /** @Persistence() */
  private $time ;
  /** @Persistence() */
  private $user_id ;
  /** @Persistence({'many-to-one','User',{key='user_id',plural='user'}}) */
  private $user
 // resto da classe
  }

Basicamente é isso, e então continuar a usar o framework normalmente, mas sem escrever os arquivos de configurações do mesmo.

Pode-se obter o código no meu pacote no github