Nível: Intermediário Nathan A. Good, Senior Information Engineer, Freelance Developer
14/Jul/2009 Mate é uma estrutura leve orientada por eventos que possibilita a construção de interfaces com o usuário (UIs) e serviços em um padrão
Model-View-Controller (MVC). Aprenda como usar as ferramentas de desenvolvimento PHP (PDT) do Eclipse e o kit de desenvolvimento (SDK) Flex juntos para estender um aplicativo usando a estrutura Mate.
Este artigo expande a documentação existente do Mate, já que foca o uso do Eclipse PDT como a ferramenta.
O Adobe® Flash® é uma alternativa atraente para construir aplicativos ricos da Internet (RIAs).
Usando o Eclipse e o Adobe Flex SDK, é possível compilar seus projetos em aplicativos que são executados no Flash Player. O Mate é uma estrutura leve orientada por eventos que traz o poder de chamar facilmente objetos remotos usando invocadores.
Esses objetos remotos podem ser serviços gravados em PHP.
Para executar o exemplo neste artigo, é necessário ter o Eclipse PDT. O restante das coisas necessárias é coberto neste artigo.
Visão Geral de Mate e Flex SDK
O Flex SDK permite construir aplicativos Adobe Flex. Os aplicativos Flex usam arquivos de origem no formato XML e ActionScript V3.0. Usando o compilador, é possível compilar esses arquivos .mxml e os arquivos ActionScript para aplicativos Adobe Flex.
Há duas versões do Flex SDK que pode ser transferido por download:
- A versão Adobe Free que inclui os produtos Adobe que permitem compilar aplicativos e também inclui o Adobe AIR e outros componentes.
O Adobe Free SDK é licenciado com a licença do Adobe Flex SDK (consulte
Recursos).
- O SDK também é fornecido em uma versão Open Source Flex SDK licenciada sob a Mozilla Public License (MPL). Inclui um compilador e tudo que for necessário para executar o exemplo deste artigo.
No entanto, a licença MPL pode não atender suas necessidades. Verifique com seu departamento jurídico se a licença serve para você.
Além disso, a Adobe oferece um produto chamado Flex Builder que pode ser transferido por download como um plug-in do Eclipse ou como um produto integral.
Para os propósitos deste artigo, faça download do Flex SDK de software livre. Faça download do arquivo ZIP e descompacte-o em um local fácil de se lembrar; neste artigo, refiro-me a esse local como FLEX_HOME. Este artigo demonstra como configurar o Eclipse com o SDK gratuito para que você possa construir RIAs com apenas o Eclipse, PDT e o Flex SDK.
O Mate é uma estrutura que ajuda a construir aplicativos que seguem o padrão MVC.
O Mate é orientado por eventos, portanto, são criados eventos que são classes
ActionScript. Pode-se, então, mapear os eventos para a chamada de objeto remoto, deixando os detalhes do código fora de suas visualizações e modelo.
Criando as Estruturas do Projeto
Neste artigo, um aplicativo simples de entrada de hora — chamado ChronoLog — demonstra como construir uma UI Flex com serviços PHP.
O exemplo requer dois projetos na área de trabalho do Eclipse: um é um projeto PHP que é usado para os serviços e o outro é um projeto normal que inclui um arquivo Ant que é configurado como um construtor Ant.
O construtor Ant é usado para executar o compilador Flex para compilar o projeto que é usado para a UI.
Para criar o projeto PHP de serviços, selecione Arquivo > Novo > Projeto no menu Eclipse. Selecione
Projeto PHP, selecione então Criar Projeto a partir de Origem Existente e escolha um local sob a raiz do documento de seu servidor de arquivos.
Isso permite testar rapidamente os serviços PHP criados.
Para criar o projeto da UI, crie um projeto vazio usando Arquivo > Novo > Projeto e selecione
Projeto.
Após criar o projeto para a UI, crie a pasta chamada src e outra dentro dela chamada chronolog. Sob essa pasta, crie pastas chamadas events, model, views e maps. A estrutura será semelhante àquela abaixo.
Figura 1. A Estrutura do Projeto de UI
Fazendo Download e Instalando o Mate
O Mate está disponível para download como um único arquivo compilado com uma extensão .swc. Faça download do arquivo a partir do site e instale-o importando-o para a pasta libs no projeto de UI.
O resultado deve ser semelhante à Figura 2.
Figura 2. O Arquivo .swc do Mate na Pasta libs
Construindo um Serviço Simples
O serviço é gravado em PHP e possui somente duas classes: uma classe de serviço que não funciona e uma classe PHP para a qual a classe de UI TimeEntry é mapeada.
Antes de gravar classes de serviços, faça download e instale os arquivos AMFPHP necessários.
Para executar esse exemplo, simplesmente instale a pasta amfphp em seu projeto PHP, conforme mostrado abaixo.
Figura 3. A Pasta amfphp
A classe de serviço é mostrada na Lista 1.
A classe de serviço simplesmente grava os valores como um pequeno arquivo XML em um local temporário.
Em seu computador, pode ser necessário modificar esse local (em negrito) para que seja gravado com êxito.
Você deve alterá-lo para o nome de um arquivo que possa ser lido e gravado em seu servidor da Web.
Lista 1. O Arquivo amfphp/services/ChronoLogManager.php
<?php
require_once('./vo/TimeEntry.php');
/*
* A service for managing time entries.
*/
class ChronoLogManager
{
public function saveTimeEntry($entry)
{
$myFile = "/tmp/time.xml";
$fh = fopen($myFile, 'w') or die("can't open file");
$stringData = "<projects>";
$stringData .= "<entry project=\"$entry->project\" " .
"time=\"$entry->time\" user=\"$entry->username\" />";
$stringData .= "</projects>";
fwrite($fh, $stringData);
fclose($fh);
}
}
?>
|
O objeto modelo do lado do serviço, TimeEntry.php, é mostrado na Lista 2.
AMFPHP requer os objetos de valor a serem armazenados na pasta services/vo, de forma que o nome do caminho relativo do arquivo PHP da raiz do projeto PHP seja
amfphp/service/vo/TimeEntry.php. AMFPHP requer que os nomes de classes e os nomes de arquivos (menos a extensão) sejam correspondentes.
Lista 2. O Arquivo
amfphp/services/vo/TimeEntry.php
<?php
class TimeEntry
{
var $_explicitType = 'model.TimeEntry';
public $username;
public $project;
public $time;
}
?>
|
Uma vantagem de usar uma classe como um objeto de parâmetro em vez de muitos parâmetros diferentes é que se mais algum campo for incluído no campo de entrada de hora, é possível simplesmente incluir um novo campo no objeto.
Uma desvantagem é que qualquer problema encontrado durante o uso de estruturas para sistemas de mensagens provavelmente ocorrerão com tipos complexos.
Às vezes, tipos simples (cadeia de caracteres, número inteiro, booleano) são mais fáceis de usar, pois evitam problemas.
Usando um Objeto Modelo
Um objeto modelo é uma classe ActionScript que contém dados no aplicativo e mapeia para um objeto remoto (TimeEntry.php) na camada de serviços PHP. É possível usar o objeto como um argumento para um método
— ou o resultado de um método — sem construir arrays para passar dados entre a UI e os serviços.
Normalmente, ao construir UIs em uma tecnologia e serviços em outra, deve-se gravar ou encontrar uma estrutura que serialize as classes em algum formato para que ambas as tecnologias entendam.
XML é frequentemente usado, pois é geralmente fácil de localizar utilitários nas linguagens de programação modernas que leem e gravam de XML.
Neste aplicativo, AMFPHP é usado para mapear os objetos Flex para os objetos PHP remotos. Adobe Message Format
(AMF) é o formato da Adobe para serializar e desserializar objetos. Uma vantagem de usar o AMF é que permite conectar a objetos sem precisar gravar seu próprio código de serialização e desserialização entre a UI e os serviços.
Há duas estruturas para conectar Flex a PHP via AMF, incluindo um de Zend (consulte
Recursos).
O objeto modelo para TimeEntry, que contém as informações usadas para cada campo de entrada de hora, é mostrado na Lista 3. Salve-o na pasta
src/chronolog/model do projeto de UI.
Lista 3. O Arquivo src/chronolog/model/TimeEntry.as
package model
{
[Bindable]
[RemoteClass(alias='TimeEntry')]
public class TimeEntry
{
private var _username : String;
private var _project : String;
private var _time : String;
public function TimeEntry()
{
}
public function get username() : String
{
return _username;
}
public function set username(value : String) : void
{
_username = value;
}
public function get project() : String
{
return _project;
}
public function set project(value : String) : void
{
_project = value;
}
public function get time() : String
{
return _time;
}
public function set time(value : String) : void
{
_time = value;
}
}
}
|
Além do atributo RemoteObject declarado na classe, é exatamente como qualquer outra classe ActionScript que seria criada para suportar seu aplicativo.
Construindo um Evento
O Mate é uma estrutura orientada por eventos, portanto, muitos eventos são criados durante o uso de Mate. Um evento Mate é uma classe ActionScript que se estende da base flash.events.Event class. Uma constante da cadeia de caracteres identifica o evento específico e é possível ter uma ou mais constantes em cada classe de eventos.
Uso essa capacidade para agrupar eventos de acordo com seu propósito no domínio de negócios.
Por exemplo, no exemplo ChronoLog há uma classe TimeEntryEvent . Ela usa a constante
SAVE, que representa um evento que deve acionar uma função que salva a entrada de hora.
À medida que o projeto cresce, mais eventos da mesma classe podem incluir
GET ou DELETE.
Denomine seus eventos usando termos de negócios e mapeie-os para funções de negócios. Não crie uma classe de evento chamada
ButtonEvent nem inclua constantes chamadas SAVE_CLICKED. Ao fazer isso, você usa nomenclatura pobre para limitar implicitamente a reutilização de seu evento. Se os usuários mudarem de ideia e um botão for alterado para um link, isso apresenta um problema com eventos que estão amarrados muito fortemente à UI.
Um exemplo da classe ActionScript TimeEntryEvent é mostrada na Lista 4.
Além de incluir a constante SAVE , o padrão para o parâmetro bubbles no construtor é alterado para true.
Incluo um argumento para o evento como uma variável pública do tipo TimeEntry.
Lista 4. O Arquivo src/chronolog/events/TimeEntryEvent.as
package events
{
import flash.events.Event;
import model.TimeEntry;
public class TimeEntryEvent extends Event
{
public static const SAVE : String = 'TimeEntryEvent_SAVE';
public var entry : TimeEntry;
public function TimeEntryEvent(type:String, bubbles:Boolean=true,
cancelable:Boolean=false)
{
super(type, bubbles, cancelable);
}
}
}
|
Construindo uma Visualização
Além da classe ActionScript, o restante dos arquivos no projeto da UI são componentes Flex.
Esses componentes são colocados em arquivos com uma extensão .mxml. Os arquivos MXML são arquivos XML bem-formados, portanto, associo os mesmo no Eclipse ao editor XML para tirar proveito dos recursos do editor XML, como formatação e fechamento de tag.
Para criar o arquivo MXML, selecione Arquivo > Novo > Arquivo e forneça a ele o nome apropriado.
Para a UI, o arquivo de aplicativo principal (main.mxml) contém somente a referência para o
EventMap principal e para a visualização. A origem do arquivo main.xml é mostrada na Lista 5. O arquivo main.mxml é salvo na pasta src/chronolog do projeto de UI.
Lista 5. O Arquivo src/chronolog/main.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
xmlns:maps="maps.*"
xmlns:views="views.*">
<mx:ViewStack>
<views:DetailView id="detailView" />
</mx:ViewStack>
</mx:Application>
|
A visualização detailView é um componente Flex que estende o objeto Flex
Panel . Todo o conteúdo da visualização é mostrado abaixo.
Lista 6. O Arquivo src/chronolog/views/DetailView.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Panel xmlns:mx="http://www.adobe.com/2006/mxml" layout="horizontal"
width="100%"
height="100%"
xmlns:mate="http://mate.asfusion.com/">
<mx:Script>
<![CDATA[
import com.asfusion.mate.events.ResponseEvent;
import mx.controls.Alert;
import model.TimeEntry;
import events.TimeEntryEvent;
[Bindable]
public var entry : TimeEntry;
private function save() : void
{
entry = new TimeEntry();
entry.username = 'jdoe';
entry.project = projectInput.text;
entry.time = timeInput.text;
}
private function handleResult(event : ResponseEvent) : void
{
Alert.show('Success!');
}
private function handleFault(event : ResponseEvent) : void
{
Alert.show(event.fault.toString());
}
]]>
</mx:Script>
<mx:HBox>
<mx:FormItem label="Project Number">
<mx:TextInput id="projectInput" />
</mx:FormItem>
<mx:FormItem label="Time">
<mx:TextInput id="timeInput" />
</mx:FormItem>
<mx:Button label="Save" click="save()" />
</mx:HBox>
</mx:Panel>
|
A visualização contém dois campos e um botão. Os campos são o número do projeto e um valor que representa o tempo trabalhado.
Um nome de usuário, que também é usado no campo de entrada de hora, é codificado permanentemente.
À medida que o aplicativo cresce, o nome de usuário vem de dados adquiridos quando o usuário efetua login no aplicativo.
Salve a visualização como um arquivo MXML na pasta views como DetailView.mxml.
Construindo o EventMap
Um componente que estende o EventMap do Mate é usado para mapear os eventos para ações. É aí que os eventos são "conectados" a objetos remotos.
Além disso, é possível usar o EventMap
para que os eventos chamem outros eventos quando estiverem concluídos, configurem valores nos objetos locais e iniciem métodos localmente.
Especificamente, encadear eventos é poderoso. Ao encadear eventos, é possível evitar a codificação permanente de muitas interações para tornar o projeto mais resiliente para alterar as necessidades comerciais.
Por exemplo, se os usuários quiserem que a tela seja alterada para uma visualização diferente após terem salvado dados com êxito, é possível criar um
NavigateEvent que faça essa mudança. O evento TimeEntry.SAVE pode efetuar dispatch do
NavigateEvent. Dessa forma, se os usuários mudarem de ideia posteriormente, é possível simplesmente atualizar o
EventMap para refletir os novos requisitos.
Um exemplo do MainEventMap é mostrado na Lista 7. É simplesmente um arquivo .mxml criado na pasta maps.
Lista 7. O Arquivo src/chronolog/maps/MainEventMap.mxml
<?xml version="1.0" encoding="utf-8"?>
<mate:EventMap
xmlns:mate="http://mate.asfusion.com/"
xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
import events.TimeEntryEvent;
]]>
</mx:Script>
<mate:EventHandlers type="{TimeEntryEvent.SAVE}">
<mate:RemoteObjectInvoker destination="amfphp"
source="ChronoLogManager"
method="saveTimeEntry"
arguments="{event.entry}">
<mate:resultHandlers>
<mate:ServiceResponseAnnouncer type="result" />
</mate:resultHandlers>
<mate:faultHandlers>
<mate:ServiceResponseAnnouncer type="fault" />
</mate:faultHandlers>
</mate:RemoteObjectInvoker>
</mate:EventHandlers>
</mate:EventMap>
|
No EventMap, o RemoteObjectInvoker contém as informações usadas para mapear o evento do tipo TimeEntryEvent.SAVE para o método correto (saveTimeEntry) no objeto remoto
(ChronoLogManger). A entrada RemoteObjectInvoker também especifica os argumentos — o campo de entrada do evento, conforme observado por {event.entry}. O destino, amfphp, é o ID do destino especificado no arquivo services-config.xml.
Após criar o EventMap, inclua-o no aplicativo principal, conforme mostrado abaixo.
Lista 8. O MainEventMap no Arquivo main.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
layout="absolute"
xmlns:maps="maps.*"
xmlns:views="views.*">
<maps:MainEventMap id="eventMap" />
<mx:ViewStack>
<views:DetailView id="detailView" />
</mx:ViewStack>
</mx:Application>
|
Efetuando Dispatch do Evento na Visualização
Usando o Mate, é efetuado dispatch dos eventos de muitas maneiras. É possível usar o dispatcher fornecido em cada componente.
Também é possível usar o Dispatcher do Mate, que é o que é usado no aplicativo
ChronoLog. Um benefício de usar o Dispatcher é que permite configurar facilmente os parâmetros do evento para valores locais, como entrada de texto, sem precisar gravar nenhum código em ActionScript para fazer isso. Além disso, com o Dispatcher, é possível configurar os métodos executados quando o evento é concluído.
O Dispatcher é mostrado em negrito abaixo.
Lista 9. O Dispatcher Mostrado no Arquivo src/chronolog/views/DetailView.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Panel xmlns:mx="http://www.adobe.com/2006/mxml" layout="horizontal"
width="100%"
height="100%"
xmlns:mate="http://mate.asfusion.com/">
<mx:Script>
<![CDATA[
import com.asfusion.mate.events.ResponseEvent;
import mx.controls.Alert;
import model.TimeEntry;
import events.TimeEntryEvent;
[Bindable]
public var entry : TimeEntry;
private function save() : void
{
entry = new TimeEntry();
entry.username = 'jdoe';
entry.project = projectInput.text;
entry.time = timeInput.text;
saveDispatcher.generateEvent();
}
private function handleResult(event : ResponseEvent) : void
{
Alert.show('Success!');
}
private function handleFault(event : ResponseEvent) : void
{
Alert.show(event.fault.toString());
}
]]>
</mx:Script>
<mx:HBox>
<mx:FormItem label="Project Number">
<mx:TextInput id="projectInput" />
</mx:FormItem>
<mx:FormItem label="Time">
<mx:TextInput id="timeInput" />
</mx:FormItem>
<mx:Button label="Save" click="save()" />
</mx:HBox>
<mate:Dispatcher id="saveDispatcher" generator="{TimeEntryEvent}"
type="{TimeEntryEvent.SAVE}">
<mate:eventProperties>
<mate:EventProperties entry="{entry}" />
</mate:eventProperties>
<mate:ServiceResponseHandler result="handleResult(event)"
fault="handleFault(event)" />
</mate:Dispatcher>
</mx:Panel>
|
 |
Chamando o Serviço
Para chamar o serviço, é necessário configurar a classe PHP no arquivo de configuração do serviço AMFPHP em seu arquivo services-config.xml. O arquivo services-config.xml contém a URL do terminal do serviço que funciona para a estrutura de suporte AMF.
O arquivo services-config.xml de exemplo é mostrado abaixo.
Lista 10. O Arquivo src/services-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<services-config>
<services>
<service id="amfphp-flashremoting-service"
class="flex.messaging.services.RemotingService"
messageTypes="flex.messaging.messages.RemotingMessage">
<destination id="amfphp">
<channels>
<channel ref="my-amfphp" />
</channels>
<properties>
<source>*</source>
</properties>
</destination>
</service>
</services>
<channels>
<channel-definition id="my-amfphp"
class="mx.messaging.channels.AMFChannel">
<endpoint uri="http://localhost/chronolog/amfphp/gateway.php"
class="flex.messaging.endpoints.AMFEndpoint" />
</channel-definition>
</channels>
</services-config>
|
Observe que é necessário alterar a URL para o gateway usado por AMFPHP para o que funcionar para seu ambiente.
Construindo o Projeto
Quando todos os arquivos Flex são configurados, um construtor Ant usa o arquivo mxml.jar fornecido com o Flex SDK para compilar o projeto. O arquivo Ant é mostrado abaixo.
Lista 11. O Arquivo build.xml
<?xml version="1.0"?>
<project name="chronologUI" basedir="." default="build">
<property name="flex.home" value="/home/ngood/bin/flex3-sdk" />
<property name="mxmlc.jar" value="${flex.home}/lib/mxmlc.jar" />
<property name="project.home" value="${basedir}" />
<property name="project.src" value="${project.home}/src" />
<property name="build.out" value="${project.home}/bin" />
<target name="build">
<java jar="${mxmlc.jar}" fork="true" failonerror="true">
<jvmarg value="-Xms256m"/>
<jvmarg value="-Xmx256m"/>
<arg value="-compiler.source-path=${project.src}" />
<arg value="+flexlib=${flex.home}/frameworks" />
<arg value="-compiler.library-path+=${project.home}/libs/Mate_08_8_1.swc" />
<arg value="-file-specs=${project.src}/chronolog/main.mxml" />
<arg value="-locale=en_US" />
<arg value="-services=${project.src}/services-config.xml" />
<arg value="-compiler.strict=true" />
<arg value="-warnings=false" />
<arg value="-output=${build.out}/chronolog.swf" />
</java>
</target>
</project>
|
Após o arquivo build.xml ser incluído no projeto, inclua um Construtor Ant selecionando o projeto chronologUI no Explorador de Projetos e selecionando Projeto > Propriedades. Clique em Novo na janela Propriedades.
Selecione Construtor Ant na lista. Na próxima tela (consulte a Figura 4), clique em Navegar pela Área de Trabalho
para localizar e selecionar o arquivo build.xml. A configuração do construtor usa automaticamente o destino padrão, para que você possa clicar em
Concluir para fechar o navegador. Para obter uma explicação mais completa da criação de seus construtores para projetos, consulte
Recursos.
Figura 4. Criando o Construtor Ant
Agora que o construtor foi incluído no projeto, será executado ao selecionar Projeto > Construir
no menu no Eclipse.
Como esse exemplo usa AMFPHP, o diretório AMF deve estar lá para a estrutura AMFPHP de suporte.
Informações adicionais sobre como configurar e instalar AMFPHP estão em Recursos.
Executando o Exemplo
Após construir o projeto de UI, coloque o arquivo chronolog.swf sob a raiz do documento do navegador da Web e navegue até ele em seu navegador.
Deve ter a aparência do exemplo abaixo.
Figura 5. O Aplicativo Concluído em Execução
Ao incluir dados e clicar em Salvar, a camada de serviços grava os dados em um pequeno arquivo XML no local especificado no serviço.
Resumo
Usando Flex SDK, Mate e PHP, é possível construir aplicativos da Web ricos. Com alguns ajustes em sua configuração do projeto Eclipse, é possível usar o
Flex SDK para construir aplicativos Flex no Eclipse com o uso do Flex Builder, apesar de os recursos de edição e depuração da UI do WYSIWYG do Flex Builder tornarem a construção de aplicativos Flex muito mais fácil.
Usando o Eclipse PDT e o AMFPHP, é possível construir serviços que podem ser consumidos por seus aplicativos Flex.
Ficando remoto — usando AMF — permite gravar eventos rapidamente que chamam métodos em objetos remotos a partir do Flex.
Recursos Aprender
-
Leia sobre MPL sob o qual o Flex SDK gratuito está licenciado.
-
"Flex e PHP: Ficando Remoto com AMFPHP" ensina como instalar e usar o AMFPHP.
-
Assista este tutorial em vídeo sobre Flex e Mate para aprender mais sobre Mate e PHP.
-
Verifique BlazeDS, um projeto para ficar remoto de AMF para serviços Java™ .
-
Aprenda mais sobre Zend AMF.
-
PHP.net é o recurso central para desenvolvedores de PHP.
- Verifique a "Lista de Leitura Recomendada sobre PHP."
- Procure todo o
conteúdo de PHP
no developerWorks.
- Siga o
developerWorks no Twitter.
- Expanda suas qualificações em PHP verificando, no IBM developerWorks, os
recursos de projeto PHP.
- Para ouvir entrevistas e discussões interessantes para desenvolvedores de software, verifique os
podcasts do developerWorks.
- Usando um banco de dados com PHP? Verifique o
Zend Core para
IBM, um ambiente de desenvolvimento e produção de PHP transparente pronto para utilização pelo cliente que suporta o IBM DB2 V9.
- Permaneça atualizado com
eventos técnicos e webcasts do developerWorks.
- Verifique as futuras conferências, feiras, webcasts e outros
Eventos
em todo o mundo que são de interesse para desenvolvedores de software livre da IBM.
- Visite, no developerWorks, a
Zona de Software Livre
para obter informações extensivas de instruções, ferramentas e atualizações de projetos para ajudá-lo a desenvolver com tecnologias de software livre e usá-las com produtos IBM.
- Assista e aprenda sobre tecnologias da IBM e de software livre e funções de produtos gratuitamente com as
demos on demand do developerWorks.
Obter produtos e tecnologias
Discutir
Sobre o autor  | 
|  | Nathan A. Good vive na área de Twin Cities em Minnesota. Profissionalmente, ele realiza desenvolvimento de software, arquitetura de software e administração de sistemas.
Quando não está gravando software, ele gosta de construir PCs e servidores, ler sobre e trabalhar com novas tecnologias e tentar convencer seus amigos de migrar para software livre. Ele escreveu e foi co-autor de muitos livros e artigos, incluindo Professional Red Hat Enterprise Linux 3, Regular Expression Recipes: A Problem-Solution Approach e Foundations of PEAR: Rapid PHP Development. |
Avalie esta página
|