Avançar para a área de conteúdo

ir para o conteúdo principal

developerWorks Brasil  >  Software livre  >

Introdução à Programação de MVC com Agavi, Parte 1: Abra um Novo Mundo Inteiro com Agavi

Aprenda como construir aplicativos da Web escaláveis com a estrutura Agavi

developerWorks
Opções de documento

Opções de documento que necessitam de JavaScript não são exibidas

Código de amostra


Classificar esta página

Ajude-nos a melhorar este conteúdo


Nível: Intermediário

Vikram Vaswani, Founder, Melonfire

14/Jul/2009

Este é o primeiro de uma série de cinco partes de artigos escritos para o desenvolvedor de PHP interessado em aprender sobre uma estrutura de software livre, flexível e escalável chamada Agavi. Neste artigo, você percorre a instalação da estrutura e os outros componentes necessários, obtém uma visão geral do Agavi e suas funções e cria seu primeiro aplicativo da Web.

Introdução

Acrônimos Usados Frequentemente
  • API: Interface do programa de aplicativo
  • CRUD: Create Read Update Delete
  • CSS: Cascading stylesheet
  • CVS: Concurrent Versions System
  • DNS: Domain Name System
  • HTML: Linguagem de Marcação de Hipertexto
  • HTTP: Protocolo de Transporte de Hipertexto
  • MVC: Model-View-Controller
  • OOP: Programação orientada a objetos
  • ORM: Object-Relational Mapping
  • PEAR: PHP Extension and Application Repository
  • RSS: Really Simple Syndication
  • SQL: Linguagem de Consulta Estruturada
  • SVN: Subversion
  • URL: Localizador Uniforme de Recursos
  • XML: Linguagem de Marcação Extensível
  • XSL: Extensible Stylesheet Language

Se for um desenvolvedor de PHP sério, já conhece (e provavelmente até já usou) estruturas de desenvolvimento de aplicativos PHP como Symfony, CakePHP e Zend Framework. Essas estruturas fornecem uma API bem arredondada que cobre a maioria dos requisitos de aplicativos e são uma base sólida para desenvolvimento de aplicativos PHP. É possível integrá-las facilmente a bibliotecas de terceiros e componentes de desenvolvimento de comunidade para funcionalidade adicional.

Enquanto as estruturas listadas acima são, sem dúvida, as mais populares, não são as únicas; o número de alternativas disponíveis aumenta mensalmente. Esta série foca uma dessas alternativas: Agavi, uma estrutura flexível e escalável que merece séria consideração por qualquer desenvolvedor profissional de PHP.

Nesta série de artigos, eu o guiarei pelo básico do desenvolvimento de aplicativos baseados em MVC com Agavi, introduzirei-o aos conceitos básicos da estrutura e mostrarei como explorar a abordagem exclusiva do Agavi para construir de forma rápida e eficiente uma aplicativo da Web completo desde o início. Através desse processo, você passará a apreciar as sutilezas dessa estrutura, entenderá as decisões de design que a tornam tão segura e extensível e obterá uma inclusão valiosa para seu kit de ferramentas de desenvolvimento de PHP. Então vamos nessa!



Voltar para parte superior


Por que Escolher Agavi?

Vou iniciar com a resposta a uma pergunta muito básica: o que é Agavi e o que o torna tão especial?

Nas palavras de seu Web site oficial, Agavi é "uma estrutura de aplicativo PHP5 poderosa e escalável que segue o paradigma de MVC". Fornece um kit de ferramentas abrangente para construir e implementar aplicativos da Web baseados em PHP e fornece suporte integrado para segurança, armazenamento em cache de dados, internacionalização, validação de entrada e abstrações de banco de dados. Originalmente, uma bifurcação do projeto Mojavi, é atualmente mantido pela Bitextender GmbH, uma empresa de software baseada na Alemanha, e liberado para a comunidade sob a GNU Lesser General Public License 2.1.

Agavi é interessante por diversas razões. Seguem as minhas três principais:

  1. Primeiro, foca muito em capacidade de reutilização de código, permitindo que desenvolvedores criem facilmente diferentes interfaces para a funcionalidade de um aplicativo. Isso é especialmente importante no contexto de aplicativos da Web que frequentemente precisam expor interfaces HTML e SOAP para seus trabalhos internos. Suponhamos, por exemplo, que você deseje construir uma interface SOAP para a funcionalidade do mecanismo de procura existente de seu aplicativo. Com Agavi, isso é tão simples quanto definir um novo tipo de saída e um renderizador que fornece a saída da função existente em um novo formato. Não é necessário ajustar a funcionalidade existente e o processo inteiro é transparente e fácil de realizar.
  2. Segundo, Agavi oferece um mecanismo de roteamento de URL sofisticado, o que permite configuração extensiva de como rotas de URL são mapeadas para funções do aplicativo. Esse mecanismo de roteamento suporta (entre outras coisas) parâmetros opcionais e obrigatórios, valores padrão, rotas aninhadas e funções de retorno de chamada. É, facilmente, um dos recursos mais importantes do Agavi. O mecanismo de roteamento, como todos os outros aspectos da configuração de um aplicativo Agavi, é expressado inteiramente em XML e o subsistema de configuração do Agavi permite acesso de tempo de execução a configurações e variáveis do aplicativo global.
  3. Terceiro, Agavi fornece filtragem de pedido e validação de entrada extremamente rígidos na instalação padrão. Filtros podem ser usados para métodos controladores pré e pós-processo. Os parâmetros de pedido são validados em cada pedido e Agavi remove automaticamente todos os parâmetros desconhecidos, reduzindo, de forma significativa, o risco de injeção de SQL e ataques semelhantes. Diversos validadores integrados existem para tarefas comuns, como validação de cadeias de caracteres, números, registros de data e hora, endereços de e-mail e arquivos. Também é possível executar validação usando expressões regulares ou definir validadores customizados para situações onde os integrados não são suficientes. Todos esses recursos conspiram para tornar o Agavi uma das estruturas mais seguras por aí para desenvolvimento de aplicativos da Web.

Além de tudo acima, o Agavi também fornece:

  • Um mecanismo de armazenamento em cache condicional.
  • Suporte para a maioria dos sistemas de banco de dados comuns (incluindo MySQL, PostgreSQL e SQL Server) e ORMs (incluindo Propel e Doctrine)
  • Um mecanismo de gerenciamento de sessões
  • Modelos customizáveis e conformidade total com princípios OOP.

levando tudo em consideração, é bem interessante...portanto, entre de cabeça e comece!



Voltar para parte superior


Instalando o Agavi

Nesta série de artigos, terei como suposição que você já possui um ambiente de desenvolvimento Apache/PHP/MySQL configurado, que tem um conhecimento funcional de PHP e XML e que se sente à vontade em usar os tipos de dados simples e complexos PHP. Também deve ter um conhecimento razoável de conceitos gerais de OOP e SOAP, assim como algum conhecimento de geração de árvore XML com a extensão Document Object Model (DOM) de PHP. Esta série usa o PHP V. 5.2.6 e o Apache V. 2.2.11.

Para iniciar, crie a estrutura de diretórios básica necessária para um aplicativo Agavi, usando as seguintes etapas:

Etapa 1: Criar a Estrutura de Diretórios de Aplicativos

Acesse o diretório-raiz do documento do servidor da Web (geralmente /usr/local/apache/htdocs no Linux® ou C:\Program Files\Apache\htdocs no Windows®) e crie um novo subdiretório para o aplicativo. Por razões que irei explicar em breve, denomine esse diretório wasp/.

shell> cd /usr/local/apache/htdocs
shell> mkdir wasp

Será feita referência ao diretório ao longo deste artigo como $WASP_ROOT.

Nesse diretório, crie outro subdiretório denominado lib/.

shell> cd wasp
shell> mkdir lib

Etapa 2: Definir Configurações do Host Virtual

Para acesso mais fácil ao aplicativo, defina um novo host virtual e configure-o para o diretório-raiz da Web do aplicativo. Enquanto essa etapa é opcional, recomendo especialmente quando se trabalha em uma máquina de desenvolvimento que contém diversos aplicativos em andamento, pois cria uma réplica mais próxima do ambiente de implementação de destino.

Para configurar um host virtual denominado para o aplicativo, abra o arquivo de configuração do Apache (httpd.conf ou httpd-vhosts.conf) e inclua as seguintes linhas no mesmo:

NameVirtualHost 127.0.0.1
<VirtualHost 127.0.0.1>
    DocumentRoot "/usr/local/apache/htdocs/wasp/pub"
    ServerName wasp.localhost
</VirtualHost>

Essas linhas definem um novo host virtual, http://wasp.localhost/, cuja raiz do documento corresponde ao diretório $WASP_ROOT/pub/. Reinicie o servidor da Web para ativar essas novas configurações. Observe que pode ser necessário atualizar o servidor DNS local de sua rede para que tome conhecimento do novo host também.

Etapa 3: Fazer Download e Instalar o Phing

Agavi usa o sistema de construção Phing para gerar código automaticamente para ações, visualizações, modelos e validadores. Phing requer o PHP 5.0 (ou superior). A maneira mais fácil de instalá-lo é com o instalador PEAR automatizado, que deve estar incluído, por padrão, com a compilação de PHP.

Para instalar o Phing, simplesmente emita os seguintes comandos na linha de comando do shell:

shell> pear channel-discover pear.phing.info
shell> pear install phing/phing

O instalador PEAR agora conectará ao novo canal, fará download do pacote e instalará o mesmo no local apropriado no sistema. Este artigo usa o Phing V. 2.3.3.

Para instalar o pacote manualmente, visite sua página inicial, faça download do archive do código de origem e descompacte manualmente os arquivos no local desejado. Para obter links para a página inicial do pacote, consulte Recursos neste artigo. Observe que a instalação manual pressupõe algum conhecimento da estrutura da organização do pacote do PEAR.

Etapa 4: Fazer Download e Instalar a Agavi

A próxima etapa é fazer download e incluir a estrutura do Agavi para o aplicativo. Nesse ponto, também é necessário decidir se as bibliotecas do Agavi serão empacotadas com o aplicativo ou se usuários farão download e instalarão o mesmo por conta própria. Cada opção possui prós e contras:

  • Requerer que os próprios usuários façam download das bibliotecas do Agavi assegura que eles sempre tenham acesso à compilação mais recente (com as correções de erros mais recentes). No entanto, o processo pode intimidar usuários principiantes e, se as bibliotecas mais novas não forem compatíveis com versões anteriores usadas no desenvolvimento do aplicativo, podem aparecer erros estranhos e difíceis de rastrear.
  • Empacotar as bibliotecas do Agavi com o aplicativo assegura que usuários podem começar a usar o aplicativo na instalação padrão, sem incompatibilidades de versão. No entanto, também bloqueia os usuários em uma versão específica do Agavi, possivelmente tornando mais difícil fazer upgrade para versões mais novas com recursos adicionais ou correções de erros necessárias.

Para propósitos desta série de artigos, suporei que as bibliotecas do Agavi sejam empacotadas com o aplicativo. Portanto, faça download da versão mais recente da estrutura do Agavi (esta série usa o Agavi V 1.0.1) de seu Web site oficial e extraia-a para um local temporário. Em seguida, copie o diretório src/ extraído para $WASP_ROOT/libs/agavi:

shell> cp -R /tmp/1.0.1/src /usr/local/apache/htdocs/wasp/libs/agavi

Também renomeie e copie o arquivo agavi-dist (Linux) ou agavi.bat-dist (Windows) do diretório bin/ extraído para $WASP_ROOT/agavi (Linux) ou $WASP_ROOT/agavi.bat (Windows). Esse é o script de construção do Agavi, que automatiza muitas tarefas comuns de criação do código sob a estrutura do Agavi.

shell> cp /tmp/agavi-1.0.1/bin/agavi-dist /usr/local/apache/htdocs/wasp/agavi
shell> chmod +x /usr/local/apache/htdocs/wasp/agavi

Etapa 5: Configurar e testar o Script de Construção do Agavi

A etapa final é configurar o script de construção do Agavi e indicar a ele os locais dos arquivos para o Agavi no sistema. Abra esse script em um editor de texto e atualize as variáveis $AGAVI_SOURCE_DIRECTORY com o caminho (absoluto é preferencial, mas relativo também funcionará) para $WASP_ROOT/libs/agavi. Segue um exemplo de como pode ser no Linux:

SET AGAVI_SOURCE_DIRECTORY="./libs/agavi"

No Windows, a variável PHP_EXECUTABLE também deve ser configurada e ela define o caminho completo do disco para o binário PHP. Segue um exemplo:

SET AGAVI_SOURCE_DIRECTORY="./libs/agavi"
SET PHP_EXECUTABLE="C:\Program Files\PHP\php.exe"

Salve o arquivo e, em seguida, teste-o, alterando para $WASP_ROOT e executando o seguinte comando:

shell> agavi status


Figura 1. A Saída do Comando agavi status
A Saída do Comando agavi status

A Figura 1 demonstra um exemplo da saída no Linux . A saída lista versões e caminhos de diretórios para PHP, Phing, Agavi e projetos existentes. Se não vir saída semelhante em seu sistema, algo saiu errado e será necessário solucionar problemas. Os Recursos neste artigo incluem links para as seções apropriadas do manual do Agavi para ajudá-lo nesse processo. Se, por outro lado, você vir saída semelhante em seu sistema, as bases de seu aplicativo Agavi estão em vigor e você está pronto para iniciar a inclusão de algum código no mesmo!



Voltar para parte superior


Iniciando um Novo Projeto do Agavi

Antes de iniciar, vale a pena entender o aplicativo de exemplo que será construído. O aplicativo é o Web site de uma revendedora de automóveis fictícia especializada na venda de carros esportes usados. Os planos originais para o Web site da revendedora eram razoavelmente modestos: algumas páginas estáticas com informações básicas de contato e serviço e um formulário de consulta para que os visitantes entrem em contato com os executivos de vendas diretamente. No entanto, em um almoço, a equipe da revendedora sugeriu uma mudança interessante: um sistema de classificados on-line de carros usados, que permita que vendedores façam upload de fotos e descrições de suas Ferraris e Lamborghinis usadas e compradores procurem nessas listas por orçamento, marca e palavra-chave. os moderadores do site têm acesso direto às listas transferidas por upload e aprovam manualmente veículos adequados para exibição nos resultados da procura. O banco de dados das listas de carros também está disponível através de uma interface SOAP para fácil integração com outros aplicativos.

Eles até pensaram em um nome legal para isso: Web Automobiles Sales Platform, ou WASP.

O aplicativo de exemplo WASP é concebido para cobrir requisitos comuns encontrados no desenvolvimento de aplicativos do dia a dia: páginas estáticas, formulários de entrada, upload de fotos, painel de administração protegido por login, paginação e classificação de dados, diversos tipos de saída e procura de palavra-chave. A implementação desses recursos requer que entenda-se os detalhes mais finos de processamento de formulário, validação de entrada, gerenciamento de sessão, autenticação e segurança, operações do banco de dados CRUD, APIs de serviços da Web e integração com bibliotecas de terceiros. Dessa forma, um bom ponto de partida é entender o desenvolvimento de aplicativos com o Agavi.

Para iniciar a construção desse aplicativo, inicialize um novo projeto com o script de construção do Agavi. Altere para $WASP_ROOT e execute o seguinte comando:

shell> agavi project-wizard

Configure um nome para o projeto:

Project name [New Agavi Project]: WASP
Project prefix (used, for example, in the project base action) [Wasp]: WASP

Aceite valores padrão para todos os prompts seguintes, exceto este abaixo:

Should an Apache .htaccess file with rewrite rules be generated (y/n) [n]? y

O assistente do projeto agora funcionará automaticamente para criar um contêiner de aplicativos vazio e preencherá o mesmo com configurações padrão. A Figura 2 possui uma captura de tela em ação.


Figura 2. O Assistente de Projeto do Agavi em Ação
Captura de Tela do Assistente de Projeto do Agavi em Ação

Como parte do processo, o assistente de projeto incluirá alguns diretórios novos na hierarquia de seu aplicativo: $WASP_ROOT/app/ para código do aplicativo; $WASP_ROOT/pub/ para conteúdo acessível pela Web, como imagens, folhas de estilo e páginas HTML estáticas; e $WASP_ROOT/dev/ para arquivos de desenvolvimento.

A Figura 3 indica como deve ser o resultado final:


Figura 3. O Layout do Arquivo para um Aplicativo Agavi
Captura de Tela do Layout do Arquivo para um Aplicativo Agavi

Observe que $WASP_ROOT/dev/ contém uma cópia do arquivo .htaccess do Apache. Destina-se especificamente para uso em projetos usando um sistema de controle de versão distribuída como SVN ou CVS, já que permite manter sua cópia de trabalho sem poluir a área de trabalho compartilhada. Para seus propósitos atuais, esse diretório pode ser excluído com segurança; consulte Recursos para obter um link para a página do manual do Agavi que explica seu utilitário em mais detalhes.

Quando o processo de criação do projeto for concluído, é possível abrir o navegador da Web e visitar o host virtual configurado anteriormente. Insira a URL http://wasp.localhost/. Deve-se ver uma página de boas-vindas do Agavi, como na Figura 4.


Figura 4. A Página de Boas-vindas de um Novo Aplicativo Agavi
A Página de Boas-vindas de um Novo Aplicativo Agavi

Parabéns! Agora, você tem um aplicativo Agavi (embora extremamente simples) ativado e em execução. Em seguida, dê uma olhada mais de perto nas partes internas e, então, comece a modificá-lo para suportar as funções discutidas anteriormente.



Voltar para parte superior


Entendendo Conceitos Básicos

O desenvolvimento de aplicativos no Agavi segue o padrão tradicional Model-View-Controller: os modelos de dados são separados da lógica de processamento, que, por sua vez, é separada da saída vista pelo usuário. O Agavi implementa esse padrão através do uso de Modelos, Ações, Visualizações e Rotas.

  • Modelos representam dados e fornecem funções necessárias para gerenciar, manipular e executar cálculos nesses dados, que são geralmente (apesar de nem sempre) armazenados em um banco de dados. O Agavi inclui adaptadores para a maioria dos bancos de dados comuns, assim como drivers para ORMs Propel e Doctrine.
  • Visualizações representam a saída que o usuário vê. As visualizações são firmemente acopladas a modelos de páginas que contêm o código do layout ou a marcação necessária para apresentar corretamente a visualização para o usuário. As visualizações também podem designar valores para variáveis de modelos; esses valores são interpolados automaticamente nos modelos de páginas no tempo de execução.
  • Ações fornecem o link entre Modelos e Visualizações. Fazem alterações nos dados de aplicativos usando Modelos e, em seguida, chama Visualizações para exibir os resultados para o usuário. Uma Ação pode ter diversas Visualizações e pode chamar uma visualização diferente dependendo do resultado que deve ser mostrado a qualquer tempo.
  • Rotas fornecem o link entre os pedidos do usuário e as Ações. Quando um usuário faz um pedido a uma URL de aplicativo, o sistema de roteamento intercepta esse pedido e decide, baseado no padrão da URL, qual Ação deve ser chamada para atender o pedido. As rotas usam expressões regulares para reconhecimento de padrões e são expressadas usando XML.

Ainda está confuso com relação a como esses quatro componentes se encaixam? A melhor explicação do fluxo típico de um pedido da Web através de um aplicativo Agavi é, provavelmente, fornecida no próprio manual do Agavi (consulte Recursos para obter um link), que informa: "Quando um pedido da Web chega, o mecanismo de roteamento seleciona a Ação inicial a ser executada; a Ação executa as mudanças necessárias no estado do aplicativo, chamando os Modelos; também seleciona uma de suas Visualizações a serem executadas após a conclusão. Após isso, a Visualização apontada executa a renderização da saída do aplicativo."

Por padrão, todas as Ações vivem no módulo Default —o que mais?—. No entanto, frequentemente, você pode querer agrupar Ações (e seus Modelos e Visualizações relacionados) com base nas áreas funcionais diferentes de seu aplicativo da Web. Os módulos fornecem uma maneira de realizar isso. Por exemplo, se seu aplicativo incluir subsistemas para procura, gerenciamento de perfil do usuário e notícias, é possível criar módulos separados denominados Search, Profiles e News e colocar as Ações correspondentes para cada um nesses módulos.

Com todas as informações de base em mãos, volte ao código gerado pelo assistente de projeto e dê uma olhada mais de perto em como a página de boas-vindas padrão do Agavi é gerada. Primeiro, veja dentro da tabela de roteamento do aplicativo, armazenada em $WASP_ROOT/app/config/routing.xml. Na Lista 1, você encontrará uma rota para a página de índice do aplicativo:


Lista 1. A Rota para a Página de Índice do Aplicativo
       
<?xml version="1.0" encoding="UTF-8"?>
<ae:configurations
 xmlns:ae="http://agavi.org/agavi/config/global/envelope/1.0"
 xmlns="http://agavi.org/agavi/config/parts/routing/1.0">
  <ae:configuration>
    <routes>
      <route pattern="" module="Welcome" action="Index" />
      ...
    </routes>
  </ae:configuration>
</ae:configurations>

Uma entrada de rota geralmente contém os atributos pattern, module e action . O pattern especifica o padrão da URL que a rota corresponde, enquanto que os atributos module e action indicam qual módulo e Ação chamar para atender o pedido. Por padrão, o Agavi usará a primeira correspondência localizada; portanto, como regra geral, organize as rotas com as mais específicas primeiro e as mais gerais mais abaixo. Para alterar esse comportamento, inclua um atributo stop=false na rota; isso indica ao Agavi para continuar a processar mesmo quando localizar uma correspondência.

A rota na Lista 1 é uma extremamente geral. Assegura que para qualquer pedido da URL, o Agavi chamará a Ação Index do módulo Welcome. Essa Ação é criada automaticamente (como uma classe PHP) pelo assistente de projeto do Agavi e será localizada em $WASP_ROOT/app/modules/Welcome/actions/IndexAction.class.php:


Lista 2. A Ação Index
     
<?php
class Welcome_IndexAction extends WASPWelcomeBaseAction
{
  public function getDefaultViewName()
  {
    return 'Success';
  }
}
?>

Toda classe de Ação pode ter alguns métodos padrão e o Agavi usará os mesmos para determinar como tratar dos diferentes tipos de pedidos. Por exemplo, um método executeRead() especifica o que fazer com pedidos GET, enquanto que um método executeWrite() especifica como tratar de pedidos POST. Caso nenhum desses métodos sejam definidos, cada Ação deve ter, no mínimo, um método getDefaultViewName() , que especifica a Visualização padrão para a Ação. A partir do código na Lista 2, fica claro que a Visualização padrão para IndexAction é denominada Success.

De acordo com as convenções de nomenclatura do arquivo Agavi, a Visualização Success para uma Ação Index no módulo Welcome deve ser armazenada em $WASP_ROOT/app/modules/Welcome/views/IndexSuccessView.class.php. Navegue até o arquivo e você verá algo como a Lista 3:


Lista 3. A Visualização Success
    
<?php
class Welcome_IndexSuccessView extends WASPWelcomeBaseView
{
  public function executeHtml(AgaviRequestDataHolder $rd)
  {
    $this->setupHtml($rd, 'simple');   
    $this->setAttribute('agavi_release', AgaviConfig::get('agavi.release'));
  }
}
?>

Ao renderizar uma Visualização, o Agavi chama automaticamente um método denominado executeXXX(), onde XXX corresponde ao tipo de saída necessário. Para gerar saída HTML, a Visualização deve conter um método executeHtml() , que configura as variáveis de modelo necessárias e prepara o modelo para renderização. Como verá um pouco mais tarde neste artigo, as Visualizações também podem gerar saída em outros formatos — por exemplo, para gerar saída XML, defina um método executeXml() , para gerar saída YAML, defina um método executeYaml() , etc. É possível definir variáveis de modelo na Visualização através do método setAttribute() e, em seguida, acessar as variáveis no próprio modelo como chaves da array $t .

Vale a pena apontar que a marcação HTML a ser gerada pela Visualização não está armazenada no próprio arquivo de classe da Visualização, mas em um arquivo de modelo separado. Conforme a convenção do Agavi, esse arquivo de modelo é denominado com base na Visualização; portanto, o modelo para um IndexSuccessView está localizado em $WASP_ROOT/app/modules/Welcome/templates/IndexSuccess.class.php. Abra esse arquivo e verá a marcação HTML que gera a saída mostrada na Figura 4.


Lista 4. A Marcação HTML
    
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
 lang="en">
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <base href="<?php echo $ro->getBaseHref(); ?>" />
    <title>Welcome to Agavi!</title>
  </head>
  <body>
    <div>
      <p>You are running <em><?php echo $t['agavi_release']; ?>
       </em></p>
    </div>
  </body>
</html>

Observe no trecho de código na Lista 3 acima como a variável do modelo $t['agavi_release'] é definida na Visualização usando o método setAttribute() e, então, interpolada dinamicamente no próprio arquivo de modelo.

Por fim, observe que você deve armazenar qualquer ativo do arquivo acessível pela Web usado pela Visualização, como os arquivos de imagens, as bibliotecas JavaScript ou as animações Flash, na área pública do aplicativo — neste caso, $WASP_ROOT/pub/— e referido de forma apropriada a partir do modelo.

Portanto, segue um resumo rápido: qualquer pedido HTTP GET para qualquer URL de aplicativo é correspondido automaticamente, através do subsistema de roteamento do Agavi, a Welcome/IndexAction que, em virtude do método getDefaultViewName() , sabe que deve chamar IndexSuccessView. IndexSuccessView chama então seu método executeHtml() para gerar a saída HTML, interpola algumas variáveis de modelo na marcação HTML e envia a saída de volta ao cliente para exibição.

Nessa explicação, algumas coisas surgem imediatamente para você:

  1. Primeiro, desde que denomine e coloque suas Ações e Visualizações corretamente, não há na verdade muito trabalho a ser feito. A estrutura irá localizar e executar arquivos para você automaticamente, sem precisar de nenhuma intervenção manual. E, se você localizar a convenção de nomenclatura de arquivos um pouco complexa de se entender, relaxe — como verá em Fornecendo Conteúdo Estático, um assistente cuida de todos esses detalhes para você!
  2. Segundo, Rotas têm uma função crítica na correspondência de pedidos de URL e direcionamento dos mesmos para Ações apropriadas. Isso também significa que, diferentemente de outras estruturas, Ações podem viver em qualquer parte da hierarquia e as URLs de aplicativos não precisam refletir diretamente sua classificação interna de Ações e módulos em diferentes categorias. De forma semelhante, a segurança e os privilégios são amarrados à própria Ação e não ao módulo.
  3. Terceiro, uma única Ação pode ter diversas Visualizações e cada Visualização pode tratar de diferentes tipos de saída. Isso amplia a capacidade de reutilização de seu código e torna muito fácil, digamos, incluir Visualizações para saída XML ou RSS na mesma Ação posteriormente, à medida que o aplicativo evolui.


Voltar para parte superior


Configurando a Página de Índice do Aplicativo e o Modelo Mestre

Agora que você entende um pouco mais sobre como o Agavi funciona, é possível começar a construir o aplicativo. Enquanto a página de boas-vindas padrão do Agavi é muito boa para os olhos, não é necessariamente a primeira coisa que deseja que seus usuários vejam. Portanto, como a primeira etapa, você criará uma nova página de índice para o aplicativo.

Primeiro, vá para $WASP_ROOT e remova o módulo Welcome que foi gerado automaticamente pelo assistente de projeto, conforme abaixo:

shell> rm -rf app/modules/Welcome
shell> rm -rf app/pub/welcome

Além disso, edite a tabela de roteamento em $WASP_ROOT/app/config/routing.xml e remova as diversas entradas da mesma, de forma que termine com a configuração vazia (por ora) na Lista 5:


Lista 5. A Tabela de Roteamento
    
<?xml version="1.0" encoding="UTF-8"?>
<ae:configurations
 xmlns:ae="http://agavi.org/agavi/config/global/envelope/1.0"
 xmlns="http://agavi.org/agavi/config/parts/routing/1.0">
  <ae:configuration>
    <routes>

    </routes>
  </ae:configuration>
</ae:configurations>

O assistente de projeto já terá criado um módulo Default contendo IndexAction e IndexSuccessView; essa é uma boa opção para a página de índice do aplicativo. Abra o arquivo $WASP_ROOT/app/modules/Default/actions/IndexAction.class.php em seu editor de texto favorito e certifique-se de que ele contenha as linhas da Lista 6:


Lista 6. A Definição da Ação Default/Index
            
<?php
class Default_IndexAction extends WASPDefaultBaseAction
{
  public function getDefaultViewName()
  {
    return 'Success';
  }
}
?>

Em seguida, verifique se o arquivo da Visualização $WASP_ROOT/app/modules/Default/views/IndexSuccessView.class.php existe e contém o código da Lista 7:


Lista 7. A Definição da Visualização Default/IndexSuccess
            
<?php
class Default_IndexSuccessView extends WASPDefaultBaseView
{
  public function executeHtml(AgaviRequestDataHolder $rd)
  {
    $this->setupHtml($rd);
    $this->setAttribute('_title', 'Index');
  }
}
?>

Em seguida, para criar o modelo para a página de índice do aplicativo, modifique o arquivo de modelo $WASP_ROOT/app/modules/Default/templates/IndexSuccess.php para conter o código da Lista 8:


Lista 8. O Modelo Default/IndexSuccess
            
Welcome to WASP, your one-stop shop for used sports cars online.
<p/>
We have a wide selection of used automobiles for your driving pleasure,
 and all our models are backed with our unique 120-day money-back guarantee.
<p/>
Use the links above to navigate this web site.

Por fim, inclua uma entrada na tabela de roteamento para que o Agavi saiba como direcionar todos os pedidos da página de índice para Default/IndexAction (Lista 9):


Lista 9. A Definição da Rota de Default/Index
            
<?xml version="1.0" encoding="UTF-8"?>
<ae:configurations
 xmlns:ae="http://agavi.org/agavi/config/global/envelope/1.0"
 xmlns="http://agavi.org/agavi/config/parts/routing/1.0">
  <ae:configuration>
    <routes>
      <!-- default action for site root "/" -->
      <route name="index" pattern="^/$" module="Default" action="Index" />
    </routes>
  </ae:configuration>
</ae:configurations>

Agora, visite a página de índice do aplicativo em http://wasp.localhost/ novamente em seu navegador da Web e deverá ver a página de índice revisada na Figura 5.


Figura 5. A Página de Índice do WASP
Captura de tela da pagina de índice do WASP

Sim, eu sei — não é muito atraente, é? Definitivamente precisa de uma reformulação da imagem!

Com relação à alteração do layout geral de uma página, pode-se seguir dois caminhos. O primeiro é, obviamente, incluir a marcação de layout no modelo de página selecionado. No entanto, se você fizer isso, será necessário repetir para cada modelo de página de seu aplicativo, o que consome muito tempo e não é muito fácil e simples para atualizar. Uma melhor solução é alterar o modelo mestre do aplicativo, localizado em $WASP_ROOT/app/templates/Master.php, e fazer com que o novo layout seja refletido automaticamente em todos os modelos de página.

A Lista 10 mostra o código para o modelo mestre revisado:


Lista 10. O Modelo Mestre do Aplicativo
            
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <base href="<?php echo $ro->getBaseHref(); ?>" />
    <link rel="stylesheet" type="text/css" href="/css/default.css" />
    <title><?php if(isset($t['_title'])) echo htmlspecialchars($t['_title'])
     . ' - '; echo AgaviConfig::get('core.app_name'); ?></title>
  </head>
  <body>
    <!-- begin header -->
    <div id="header">
      <div id="logo">
        <img src="/images/logo.jpg" />
      </div>
      <div id="menu">
        <ul>
          <li><a href="#">Home</a></li>
          <li><a href="#">For Sale</a></li>
          <li><a href="#">Other Services</a></li>
          <li><a href="#">About Us</a></li>
          <li><a href="#">Contact Us</a></li>
        </ul>
      </div>
    </div>
    <!-- end header -->

    <!-- begin body -->
    <div id="body">
      <?php echo $inner; ?>
    </div>
    <!-- end body -->

    <!-- begin footer -->
    <div id="footer">
      <p>Powered by <a href="http://www.agavi.org/">Agavi</a>. 
      Licensed under <a href="http://www.creativecommons.org/">Creative Commons</a>.</p>
    </div>
    <!-- end footer -->
  </body>
</html>

Você perceberá que o modelo mestre usa dois ativos adicionais — uma folha de estilo CSS e uma imagem de logotipo. Localize esses arquivos na área pública do aplicativo, de forma que os cliente que estiverem conectando possam recuperá-los através de HTTP. Conforme necessário, crie os subdiretórios css/ e images/ em $WASP_ROOT/pub/ e copie os ativos necessários para esses locais (eles podem ser encontrados no archive para download).

A Lista 11 mostra as regras da folha de estilo a partir de $WASP_ROOT/pub/css/default.css, para propósitos ilustrativos:


Lista 11. A Folha de Estilo Principal do Aplicativo
            
body {
  margin: 0;
  padding: 0;
  font-family: 'Verdana' sans-serif;
}

#header {
  height: 80px;
  background: #4062A8;
}

#logo {
  float: left;
  padding-left: 50px;
  padding-top: 5px;
}

#menu {
  float: right;
  background: #4062A8;
  margin-right: 20px;
}

#menu ul {
  list-style: none;
}

#menu li {
  margin-top: 35px;
  float: left;  
  padding-right: 25px;
  padding-left: 25px;
}

#menu a, #footer a {
  color: white;
  font-weight: bold;
}

#body {
  padding-top: 20px;
  padding-left: 50px;
  min-height: 375px;
}

#footer {
  font-size: x-small;
  color: white;
  float: right;
  text-align: right;
  background: #4062A8;
  width: 400px;
  clear: both;
  margin-top: 20px;
}

Em seguida, revisite a página de índice do aplicativo e deverá ver algo semelhante à Figura 6:


Figura 6. A Página de Índice do WASP Revisada
Captura de Tela da Página de Índice do WASP Revisada

Aí! Não está com uma aparência muito melhor?



Voltar para parte superior


Fornecendo Conteúdo Estático

Até o momento, somente Ações existentes foram modificadas no aplicativo. No entanto, o script de construção do Agavi facilita muito a inclusão de novas Ações em seu aplicativo. Para ilustrar, agora, serão incluídos StaticPageAction e Visualizações correspondentes que serão responsáveis por fornecer conteúdo estático, como as páginas "Sobre Nós" e "Serviços" da empresa. As etapas abaixo refletem o processo padrão seguido para incluir qualquer nova funcionalidade em um aplicativo Agavi.

Etapa 1: Criar Classes Marcadoras

Volte a seu shell e chame o script de construção do Agavi, conforme abaixo:

shell> agavi action-wizard

Configure uma nova Ação denominada StaticContentAction e vincule-a a duas Visualizações, StaticContentErrorView e StaticContentSuccessView, fornecendo os valores a seguir quando solicitado:

Module name: Default
Action name: StaticContent
Space-separated list of views to create for StaticContent [Success]: Error Success

O Agavi agora funcionará gerando os arquivos de classe necessários e colocando-os nos locais corretos.

Etapa 2: Definir Rotas

Quando o processo for concluído, revisite o arquivo $WASP_ROOT/app/routing.xml e inclua uma nova rota base para conteúdo estático que faz referência à sua nova Ação que acaba de ser cunhada, como na Lista 12:


Lista 12. A Definição de Rota de Default/StaticContent
            
<?xml version="1.0" encoding="UTF-8"?>
<ae:configurations
 xmlns:ae="http://agavi.org/agavi/config/global/envelope/1.0"
 xmlns="http://agavi.org/agavi/config/parts/routing/1.0">
  <ae:configuration>
    <routes>
       ...
      <!-- action for static pages "/content/$page" -->
      <route name="content" pattern="^/content/(page:[\w-]+)$" module="Default"
       action="StaticContent" />

    </routes>
  </ae:configuration>
</ae:configurations>

Essa rota é um pouco mais complexa do que aquelas que você viu antes. Primeiro, inclui um atributo "name"; isso fornece um rótulo que pode ser usado ao gerar manualmente a rota (você verá isso em ação um pouco mais adiante). Segundo, a expressão regular corresponderá URLs em qualquer um dos formatos a seguir:

/content/hello
/content/HelloWorld
/content/hello-world
/content/Hello-World-123

Deve estar claro que o último segmento de todos os exemplos acima é uma variável. É necessário informar o subsistema de roteamento do Agavi sobre isso, colocando esse segmento da definição de rota entre parênteses. O uso de parênteses cria o que o manual do Agavi chama de um grupo de captura. Os grupos de captura podem ser usados para converter automaticamente determinados segmentos da URL em variáveis do Agavi, o que, eventualmente, será validado, colocado em um objeto AgaviRequestDataHolder e acessado em uma Ação ou Visualização.

Etapa 3: Definir Regras de Validação

Definir uma rota com grupos de captura é apenas parte do quebra-cabeça, no entanto. Para que as variáveis da rota transformem-na em uma Ação ou Visualização, elas devem passar pelo filtro de validação de entrada extremamente rigoroso do Agavi. Por padrão, esse filtro é configurado para o nível mais rígido; isso significa que qualquer variável GET ou POST sobre a qual não tem conhecimento (ou que não seja aprovada nos testes de validação específicos) serão eliminadas automaticamente e nunca chegarão ao aplicativo. Essa política de higienização de entrada extremamente rígida faz parte do que torna um aplicativo Agavi tão seguro.

O Agavi é fornecido com uma ampla gama de validadores de entrada integrados, que podem ser configurados através de um arquivo XML simples. Um validador de entrada compartilha o nome da Ação à qual pertence e pode ser localizado no diretório validate/ do módulo. Para ver o validador StaticContentAction, edite o arquivo em $WASP_ROOT/app/modules/Default/validate/StaticContent.xml e preencha-o com o XML da Lista 13:


Lista 13. A Definição da Ação Default/StaticContent
            
<?xml version="1.0" encoding="UTF-8"?>
<ae:configurations
  xmlns="http://agavi.org/agavi/config/parts/validators/1.0"
  xmlns:ae="http://agavi.org/agavi/config/global/envelope/1.0"
  parent="%core.module_dir%/Default/config/validators.xml"
>
  <ae:configuration>

    <validators method="read">
      <validator class="regex">
        <arguments>
          <argument>page</argument>
        </arguments>
        <ae:parameters>
          <ae:parameter name="pattern">#^[\w-]+$#</ae:parameter>
          <ae:parameter name="match">true</ae:parameter>
        </ae:parameters>
      </validator>
    </validators>

  </ae:configuration>
</ae:configurations>

De forma muito simples, esse bloco de XML configura um AgaviRegexValidator para a variável page e verifica se corresponde à expressão regular especificada na definição do validador.

Como resultado das etapas 2 e 3, quando passada a URL /content/hello, os subsistemas de roteamento e validação do Agavi verificam a URL, inicializam uma variável AgaviRequestDataHolder denominada page e designam a ela o valor hello.

Etapa 4: Gravar Código da Ação

De qualquer forma, por que deseja capturar esse segmento específico da URL? É uma boa pergunta e a razão ficará clara em breve. Até lá, no entanto, siga adiante e configure StaticContentAction, que deve ser localizado em $WASP_ROOT/app/modules/Default/actions/StaticContentAction.class.php. Edite esse arquivo para que contenha o código da Lista 14:


Lista 14. A Definição da Visualização Default/StaticContent
            
<?php
class Default_StaticContentAction extends WASPDefaultBaseAction
{
  public function getDefaultViewName()
  {
    return 'Success';
  }

  public function executeRead(AgaviRequestDataHolder $rd)
  {
    return 'Success';
  }
}
?>

Conforme mencionado anteriormente, o Agavi requer que você especifique explicitamente a Visualização usada para tratar diferentes tipos de pedidos. Por exemplo, um método executeRead() especifica como tratar os pedidos GET, enquanto que um método executeWrite() especifica como tratar de pedidos POST. Nesse caso, como StaticContentAction irá tratar somente de pedidos GET, deve conter um método executeRead() e esse método deve fazer referência a StaticContentSuccessView.

Vale a pena observar que a Visualização Success é chamada somente se os testes de validação de entrada mencionados anteriormente forem bem-sucedidos. Se a validação de entrada falhar, o comportamento padrão do Agavi é gerar a Visualização Error.

Etapa 5: Gravar Código da Visualização

Agora vamos entrar no grosso desta seção: configurar StaticContentSuccessView para fornecer páginas estáticas. Abra o arquivo de visualização em $WASP_ROOT/app/modules/Default/views/StaticContentSuccessView.class.php e preencha-o com o código da Lista 15:


Lista 15. A Definição da Visualização Default/StaticContentSuccess
            
<?php
class Default_StaticContentSuccessView extends WASPDefaultBaseView
{
  public function executeHtml(AgaviRequestDataHolder $rd)
  {
    $this->setupHtml($rd);
    $words = explode('-', $rd->getParameter('page'));
    array_walk($words, create_function('&$i', '$i = ucfirst(strtolower($i));'));
    $tmpl = 'StaticContent' . implode($words);
    if (file_exists(
     dirname($this->getLayer('content')->getResourceStreamIdentifier())
     . "/$tmpl.php")) {
      $this->getLayer('content')->setTemplate($tmpl);
    } else {
      return $this->createForwardContainer(
       AgaviConfig::get('actions.error404_module'),
       AgaviConfig::get('actions.error404_action'));
    }
  }
}
?>

Isso pode parecer complicado, mas na verdade não é. Fundamentalmente, essa Visualização tenta corresponder ao pedido da URL para um modelo de página estática e renderizá-lo, se disponível. Primeiro, o método executeHtml() da Visualização recupera a variável page que foi configurada pelo subsistema de roteamento algumas etapas atrás, usando, do objeto AgaviRequestDataHolder , o método getParameter() . Em seguida, reformata esse valor de acordo com uma convenção de nomenclatura de arquivo predefinida e tenta localizar e renderizar um arquivo de modelo com o mesmo nome.

Por exemplo, se a URL solicitada pelo cliente era /content/hello-world, a chamada a $rd->getParameter('page') retorna o valor hello-world e a Visualização, portanto, procura um modelo denominado $WASP_ROOT/StaticContentHelloWorld.php. Se o arquivo de modelo existe, a Visualização envia-o para o cliente; caso contrário, encaminha automaticamente para a visualização Error404 padrão do Agavi, que exibe um erro "Página não localizada" para o cliente.

Também é uma boa ideia no momento configurar StaticContentErrorView, em $WASP_ROOT/app/modules/Default/views/StaticContentErrorView.class.php, para especificar o comportamento quando a validação de entrada falha (Lista 16).


Lista 16. A Definição da Visualização Default/StaticContentError
            
<?php
class Default_StaticContentErrorView extends WASPDefaultBaseView
{
  public function executeHtml(AgaviRequestDataHolder $rd)
  {
    return $this->createForwardContainer(
     AgaviConfig::get('actions.error404_module'),
     AgaviConfig::get('actions.error404_action'));
  }
}
?>

Isso é bem simples — apenas encaminhe para a ação padrão Error404 novamente.

Agora, tudo que resta é realmente definir as páginas estáticas. Dê uma olhada na Figura 6 (ou no modelo na Lista 10. Encontrará dois itens no menu principal que podem ser, de forma razoável, classificados como conteúdo estático —Sobre Nós e Outros Serviços. Crie dois novos arquivos de modelo em $WASP_ROOT/app/modules/Default/templates/StaticContentAboutUs.php e $WASP_ROOT/app/modules/Default/templates/StaticContentOtherServices.php e preencha-os com algum conteúdo estático, como da Lista 17.


Lista 17. O Modelo Default/StaticContentAboutUs
            
We've been dealing with used sports cars since 1996, and have built up an
 enviable reputation for honesty, transparency, and fairness in all our dealings.
 Rest assured, when you buy a used car from us, you're not just buying an automobile...
 you're buying a solid guarantee of service and quality!
<p/>
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor
 incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud
 exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure
 dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
 Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt
 mollit anim id est laborum.
<p/>
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor
 incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis
 nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
 Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu
 fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
 culpa qui officia deserunt mollit anim id est laborum.

Em seguida, abra seu navegador da Web e visite http://wasp.localhost/content/about-us ou http://wasp.localhost/content/other-services e deverá ver suas páginas estáticas (Figura 7).


Figura 7. Uma Página Estática
Captura de tela de uma pagina estatica de amostra, sobre nós

Etapa 6: Ligar Coisas

Há um item menor que ainda resta: ligar os itens do menu principal às novas páginas de conteúdo estático criadas. Abra o arquivo de modelo mestre e use o gerador de rota do Agavi para gerar rotas automaticamente para essas páginas, como na Lista 18.


Lista 18. O Modelo Mestre do Aplicativo Atualizado
            
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
...
      <div id="menu">
        <ul>
          <li><a href="<?php echo $ro->gen('index'); ?>">
           Home</a></li>
          <li><a href="#">For Sale</a></li>
          <li><a href="<?php echo $ro->gen('content',
           array('page' => 'other-services')); ?>">Other Services</a>
           </li>
          <li><a href="<?php echo $ro->gen('content',
           array('page' => 'about-us')); ?>">About Us</a></li>
          <li><a href="#">Contact Us</a></li>
        </ul>
      </div>
...
</html>

Gerar um rota manualmente em Agavi é a simplicidade em si. Em sua Visualização ou modelo, use o objeto AgaviRouting, representado por $ro para gerar a rota, chamando seu método gen() com o nome da rota (lembra-se daquele atributo name configurado ao definir a rota?) . Os parâmetros de rota podem ser especificados em uma array e passados ao método gen() como um segundo argumento.

Salve suas mudanças no modelo mestre e revisite a página de índice de seu aplicativo. Os links do menu principal para as páginas estáticas agora devem estar ativos.

Vale a pena apontar, para os puristas entre vocês, que há na verdade uma maneira mais fácil de apresentar páginas estáticas em um aplicativo Agavi: é possível simplesmente colocá-las no diretório pub/ do aplicativo como arquivos HTML e fazer o link delas manualmente. No entanto, essas páginas não podem usar o modelo mestre do Agavi e precisam de sua própria marcação de layout, que cria problemas de atualização em data posterior; além disso, elas não se beneficiam do armazenamento em cache e dos mecanismos de segurança do Agavi.



Voltar para parte superior


Conclusão

Ufa! Isso é tudo para este primeiro artigo. Eu o apresentei ao Agavi, expliquei seus recursos exclusivos e o guiei pelo processo de instalação e configuração da estrutura em sua máquina de desenvolvimento. Também expliquei o básico sobre como o Agavi trata dos pedidos da Web e como usar o script de construção do Agavi para configurar novos projetos e modelos de arquivos de classes rapidamente. Por fim, introduzi o aplicativo WASP de exemplo, que servirá como um aplicativo de teste ao longo desta série.

Seu aplicativo de exemplo ainda está em seus estágios iniciais: sabe como fornecer a página de índice e algum conteúdo estático e possui um modelo mestre decente pronto para desenvolvimento adicional. Implementar até mesmo essa funcionalidade muito básica deve fornecer algum insight para os componentes mais importantes de um aplicativo Agavi —rotas, ações, visualizações, validadores, modelos e módulos — e configurar a base para as funções mais complexas que serão abordadas na segunda parte desta série de artigos.

É possível fazer download do archive de código para o aplicativo WASP com as funções discutidas até o momento neste artigo. Recomendo que o obtenha, comece a brincar com ele e, talvez, tente incluir novas coisas no mesmo. Garanto que não quebrará nada e, com certeza, agregará a seu aprendizado. Até a próxima vez...boas experiências!




Voltar para parte superior


Download

DescriçãoNomeTamanhoMétodo de download
Archive of the WASP applicationwasp-01.zip3075KBHTTP
Informações sobre métodos de download


Recursos

Aprender

Obter produtos e tecnologias

Discutir


Sobre o autor

Photo of Vikram Vaswani

Vikram Vaswani é o fundador e Presidente da Melonfire, uma empresa de serviços de consultoria com conhecimento especial sobre ferramentas e tecnologias de software livre. Ele também é o autor dos livros PHP Programming Solutions e PHP: A Beginners Guide.




Avalie esta página


Reserve um instante para completar este formulário para nos ajudar a servi-lo melhor.



 


 


Não
são úteis
Extremamente
úteis
 






Voltar para parte superior


IBM, o logotipo da IBM, ibm.com, DB2, developerWorks, Lotus, Rational, Tivoli e WebSphere são marcas ou marcas registradas da International Business Machines Corporation nos Estados Unidos e/ou em outros países. Esses e outros termos de marcas registradas da IBM são marcados em sua primeira ocorrência nessas informações com o símbolo apropriado (® ou ™), indicando marcas registradas nos EUA ou de direito consuetudinário de propriedade da IBM no momento em que essas informações foram publicadas. Tais marcas registradas também podem ser marcas registradas ou de direito consuetudinário em outros países. Consulte a lista atual de marcas registradas da IBM. Adobe, o logotipo Adobe, PostScript e o logotipo PostScript são marcas ou marcas registradas da Adobe Systems Incorporated nos Estados Unidos e/ou em outros países. Microsoft, Windows e o logotipo do Windows são marcas registradas da Microsoft Corporation nos Estados Unidos e/ou em outros países. Linux é uma marca registrada da Linus Torvalds nos Estados Unidos e/ou em outros países. Outros nomes de empresas, produtos ou serviços podem ser marcas registradas ou marcas de serviço de terceiros. Outros nomes de empresas, produtos e serviços podem ser marcas registradas ou marcas de serviço de terceiros.