assertTrue($tdd);

Olá! Esse é um artigo irmão da palestra assertTrue($tdd); ministrada no COLAPHP, a trilha de PHP do Latinoware 2011. Você pode ver os slides da palestra aqui também. Caso tenha assistido, por favor avalie-a no joind.in.


Apesar de ser tratada como uma coisa nova e frequentemente encarada como algo diferente, o Desenvolvimento Orientado a Testes não é algo inventado, que surgiu da cabeça de um desenvolvedor paranóico que codava enquanto ouvia Janis Joplin pelado montado numa cabra de estimação.

Nós, como desenvolvedores, jamais deixamos de testar um software. Mas nosso instinto natural nos faz testar software como completos idiotas! Somos confiantes de que temos o funcionamento do código completo na nossa cabeça, e essa arrogância não-intencional é a maior causadora de bugs de todos os tempos por um único motivo: não conseguimos prever todos os problemas. É impossível. Ler mais »

Autoloaders Universais

Esse artigo é irmão da palestra Autoloaders Universais realizada na trilha de PHP do TDC 2011. Veja os slides e avalie a palestra no joind.in.

No PHP, autoloading é um mecanismo para resolver automaticamente as dependências de classes no código. Isso significa que você não precisa incluir instruções require ou include manualmente para os arquivos de classe.

O mecanismo funciona baseado em um evento. Sempre que uma classe não é encontrada no sistema, o PHP chama, se houverem, as funções registradas para o tratamento do autoloading. Existem duas formas de declará-las:

Pelo nome da classe, você pode escrever uma rotina que inclua automaticamente um arquivo que a contém. É comum em vários projetos ver estruturas de pastas que refletem os nomes das classes. No Zend Framework, por exemplo, todas as classes seguem o mesmo padrão:

  • Zend_Validate_Email: Zend/Validate/Email.php
  • Zend_Filter_Int: Zend/Filter/Int.php
  • Zend_Service_Twitter: Zend/Service/Twitter.php
  • Zend_Version: Zend/Version.php

O primeiro projeto a seguir esse padrão provavelmente foi o PEAR. Hoje, muitos outros projetos e frameworks seguiram a mesma convenção. Isso inclui o Zend Framework, Solar Framework, Doctrine ORM, Symfony 2.0, Twig Template Engine e muitos outros. Esses muitos projetos se reuniram e até mesmo criaram uma especificação que define uma maneira padrão de carregar classes, compatível com namespaces e reutilizável, o que significa que o autoloader do Zend poderá carregar arquivos do Symfony, por exemplo.

Essa especificação é a PSR-0, que tem até mesmo uma implementação em PHP e uma implementação em C nativa que pode ser incluída no PHP diretamente. Eu mesmo já escrevi uma implementação simplificada em PHP que uso no projeto Respect.

Na prática, a coisa é simples. Vamos ver um exemplo de como três classes simples, sem namespaces, podem ser auto-carregadas:

//Arquivo /project/public/index.php

include '../config.php';

$departamento = new Departamento;
$funcionario = new Funcionario;
$funcionario->id = 1;
$funcionario->nome = 'Alexandre';
$departamento->adicionarFuncionario($funcionario);

var_dump($departamento);
//Arquivo /project/config.php

$diretorio_classes = '/project/library/';

set_include_path($diretorio_classes . PATH_SEPARATOR . get_include_path());

function __autoload($nomeDaClasse)
{
    require_once "{$nomeDaClasse}.php";
}
//Arquivo: /project/library/Departamento.php

class Departamento
{
    protected $funcionarios = array();
    protected $equipamentos = array();

    public function adicionarFuncionario(Funcionario $funcionario)
    {
        $this->funcionarios[$funcionario->id] = $funcionario;
    }
    public function adicionarEquipamento(Equipamento $equipamento)
    {
        $this->funcionarios[$equipamento->id] = $equipamento;
    }
}
//Arquivo: /project/library/Funcionario.php

class Funcionario
{
    public $id, $nome;
}
//Arquivo: /project/library/Equipamento.php

class Equipamento
{
    public $id, $marca, $modelo, $tipo;
}

A declaração da classe Departamento naturalmente requer que as classes Funcionario e Equipamento estejam previamente declaradas, mas você pode ver que não existe nenhuma referência pro arquivo na classe. No arquivo config.php há uma implementação bem simples de autoloading, mas recomendo que você explore os links ali acima para ver exemplos reais com classes mais complexas e namespaces.

Microframeworks em PHP

Esse artigo é irmão da palestra Microframeworks em PHP realizada na trilha de PHP do TDC 2011. Veja os slides e avalie a palestra no joind.in.

Frameworks são um tipo específico de biblioteca de software que fornece não somente funcionalidade, mas uma estrutura padrão na qual você pode construir por cima.

Normalmente, frameworks completos são organizados em camadas. No clássico esquema MVC, cada letra representa uma camada: o modelo, a visão e o controlador, cada qual responsável por uma coisa no sistema.

Microframeworks normalmente fornecem a estrutura para uma única camada na aplicação, e deixam o espaço para as outras camadas serem modeladas como você quiser. É comum falarmos sobre microframeworks de forma genérica para os microframeworks que tratam da camada de controle da aplicação, ou seja, que recebem as requisições, processam as rotas e direcionam o código para a rotina de processamento correta.

No PHP, os microframeworks tornaram-se mais populares após a versão 5.3, que permite que declaremos funções anônimas para o tratamento das requisições:

require_once 'SplClassLoader.php';

$r = new Respect\Rest\Router;
$r->get('/oi/*', function($nome='visitante') {
 return "Olá $nome";
});

As rotas em microframeworks são normalmente representadas por um método HTTP (get, post, put e outros) e um padrão de URL. No exemplo acima, utilizando o Respect\Rest é o método get em qualquer url no formato http://exemplo.com/oi/algumacoisa.

Existem, obviamente, algumas diferenças entre os microframeworks em PHP:

O Silex é um microframework baseado no Symfony 2.0, distribuído em um único arquivo phar. Ele fornece alguns módulos pré-escritos e uma documentação sólida. Talvez seu melhor recurso seja a possibilidade de reutilizar aplicações, o que permite que você construa sistemas modulares constituídos de uma ou mais aplicações distintas.

Um outro projeto interessante é o Slim, que traz conceitos interessantes como os middlewares, que são funções que você pode associar à rotas individuais, o que melhora a taxa de reúso de código no sistema. Suporte nativo a views e loggers também tornam o desenvolvimento mais simples.

Por último, um projeto no qual tenho me dedicado bastante é o Respect\Rest que usei como exemplo lá em cima. Além do básico dos microframeworks, ele traz alguns recursos interessantes que auxiliam projetos que começam pequenos a crescer sem perder o controle do código, como por exemplo associação de classes inteiras a grupos de rotas:

use Respect\Rest\Routable;

class MeuArtigo implements Routable {
    public function get($id) { }
    public function delete($id) { }
    public function put($id) { }
}

$r->any('/artigo/*', 'MeuArtigo');

Além de eventos antes e depois das rotas, é possível associar rotinas complexas pré-configuradas no próprio microframework. É possível configurar rotas para tratar formatos de resposta automaticamente. No exemplo abaixo é definida uma rota que aceita requisições em HTML, XML e JSON, e trata os resultados automaticamente:

$r->get('/users/*', function($username) use ($db) {
    return $db->users(array('username'=>$username'))->fetch();
})->accept(array(
    'text/html' => 'print_r',
    '.xml' => function ($user) {
        return "<user><id>{$user->id}</id><name>{$user->name}</name></user>";
    },
    '.json' => 'json_encode'
));

Observação: a parte do banco de dados na linha com $db é ilustrativa, não faz parte do projeto do microframework, mas há um outro projeto aqui que faz isso.

Há muito potencial para explorar com o conceito de microframeworks e as técnicas que podem ser aplicadas ao seu desenvolvimento, mas já é possível notar que eles podem ser produtivos e seu código reutilizável.

Ambiente de Trabalho

Tá rolando esse meme bacana sobre os vários ambientes diferentes de trabalho que desenvolvedores PHP usam. O Minetto e o Dohms me convidaram e vou escrever um pouco sobre as 7 ferramentas que mais uso pra trabalhar! =D Vamos lá, sem ordem específica:

NetBeans

A equipe do NetBeans conseguiu criar uma excelente IDE para PHP com instalador de poucos MB. Se você pegar leve nos plugins e projetos abertos simultaneamente, o consumo de memória é aceitável e ela fica rapidinha. Na versão mais recente já veio o suporte nativo ao git pra completar a jogada.

Acima está a configuração de painéis que eu uso no NetBeans, que é diferente da padrão que vem com a IDE. Cada coisinha aí tem um motivo pra estar nessa posição, vamos lá:

  • Ocultei a barra de ferramentas principal. Eu realmente não preciso de botões de copiar, colar, recortar, etc.
  • À esquerda está o principal: o editor de código. Sigo o padrão PEAR de código, e configuro o NetBeans pra formatar o código de acordo com ele automaticamente. Com isso, meu código fica sempre com linhas de no máximo 85 caracteres, e não preciso de uma área de edição muito larga.
  • Com linhas de 85 caracteres, sobra bastante espaço do lado direito pra outros painéis. Não coloquei o código no centro pra que sobrasse bastante largura pro painel de output lá no canto direito inferior. Como desenvolvo basicamente coisas server-side, dependo bastante da saída do código e tô sempre de olho naquele espaço.
  • Logo acima aquela barrinha verde me diz qual a porcentagem de testes uniários que passaram. No NetBeans basta apertar Alt+F6 pra rodar os testes do projeto atual, sem nem precisar sair da IDE.
  • Os painéis com a estrutura de pastas do projeto e navegador de objetos do arquivo atual não são novidade, lá em cima.

Ubuntu

Por mais que o NetBeans me dê liberdade pra trabalhar com qualquer sistema operacional, o que obtive melhores resultados é o Ubuntu. Ele simplesmente fica fora do caminho entre eu e o código. Se minha máquina explodir agora, posso subir um Ubuntu com PHP, MySQL, MongoDB, NetBeans e tudo mais em questão de minutos em outra máquina qualquer, tudo graças aos milhares de pacotes prontinhos que o apt-get nos oferece.

Presente em versões pra Netbook, Desktop, Servidores e Cloud o Ubuntu tem uma presença sólida e consistente. Posso desenvolver protótipos na minha máquina local rodando Ubuntu Desktop e sei que as chances dela funcionar em produção com o Ubuntu Cloud são mais altas do que se fosse em outro tipo de ambiente com configurações diferentes.

php.net

O melhor portal de documentação que já vi. Simples, direto, acessível, rico, bem traduzido. Difícil ficar sem ele.

Guake

O Guake é um terminal pro GNOME similar ao console do jogo Quake. Funciona de maneira simples: Você aperta F12 e ele desce do topo da tela.  Dá pra criar várias abas, configurar as cores e tudo mais. Obrigatório pra quem mexe compulsivamente com o terminal.

Git e GitHub

Controle de versão é necessário. Controle de versão distribuído é desejável. Controle de versão distribuído e social é viciante.

Embora existam concorrentes como o BitBucket, o GitHub conseguiu atrair uma grande quantidade de usuários pra sua plataforma de código social. E quando falamos de redes sociais, a mais bem sucedida é sempre a mais amplamente adotada. O processo de engajar outros desenvolvedores no seu projeto é muito mais simples com sistemas como o GitHub.

Chromium

O Chromium é o projeto open source por trás do Google Chrome. Em pouco tempo esse navegador me conquistou como usuário pelos detalhes, coisas simples como o auto-completar bem polido da barra de endereços e a sincronização transparente entre computadores. Um recurso pioneiro desse navegador que eu uso desde o surgimento são as janelas anônimas, principalmente para iniciar sessões limpas livres de cookies pra testes de navegação em determinados sistemas. Poupa o trabalho de limpar histórico, cache e tudo mais.

Como desenvolvedor achei as ferramentas de inspeção e monitoramento de requisições HTTP excelentes. Ao contrário do Firebug, não tornam a página toda uma porcaria lenta e não sugam toda a memória do sistema.

PEAR

A maneira mais simples de obter e atualizar bibliotecas PHP. Se um determinado software é instalável via PEAR, você deveria escolher esse formato. Se você distribui software, você deveria suportar esse formato. É o apt-get dos PHPeiros.


Continuando a brincadeira, convido os seguintes programadores pra escrever 7 itens sobre seu ambiente de trabalho e indicar de 3 a 5 outros programadores pra participar:

Carlos Ferrari

Jayson Santos

G0nc1n

Validação de dados em PHP

Validar dados é um trabalho ingrato. Após buscar diversos componentes, resolvi escrever minha própria biblioteca, para PHP 5.3 apenas, totalmente open source. Pra ilustrar meus motivos e alguns aspectos dela, vou criar um problema de validação rotineiro, inspirado no objeto de usuário da API do Twitter:

  • Deve ser um objeto.
  • Esse objeto deve possuir obrigatoriamente um atributo “screen_name” ou um atributo “id”.
  • Esse objeto deve possuir obrigatoriamente os atributos “created_at” e “name”.
  • O objeto pode, opcionalmente, ter os atributos “location” e “description”.
  • O atributo “screen_name”, se presente, deve ter de 1 a 15 caracteres alfanuméricos, incluindo o “_”.
  • O atributo “id”, se presente, deve ser um inteiro.
  • O atributo “created_at” deve ser uma data válida entre 2002 e hoje.
  • O atributos “location”, “description” e “name”, se presentes, devem ser cada um uma string de 1 a 160 caracteres.

Uau. Coisa pra caramba, né? Preparei alguns exemplos de validação que você pode ver abaixo só pra ter uma idéia do volume de código:

O Zend_Validate pode até trazer vantagens na tradução e na configuração de validadores, mas não facilita o trabalho principal: validar. As dificuldades de validar regras compostas ainda estão ali, assim como em muitos componentes de validação como o Inspekt e o validador do Symfony. Todos eles foram arquitetados pra validar um único valor, não uma sequência completa de regras interligadas.

Esse código cheio de condicionais tem sérios problemas de clareza (é difícil determinar que apenas um dos atributos “id” ou “screen_name” precisa estar presente, por exemplo). Isso leva a dores de cabeça latejantes na hora da manutenção.

Pensando nisso, começei a arquitetar minha biblioteca com alguns princípios básicos em mente:

  • Tratamento de regras opcionais.
  • Suporte transparente para regras compostas.
  • Validação de atributos e chaves dentro de objetos e arrays.
  • Coleta e agrupamento de várias mensagens de erro diferentes.
  • Simples de utilizar, simples de instanciar.
  • Baixo acoplamento e fácil reuso.
  • Coberta por testes unitários.

Essa biblioteca é o Respect\Validation e está em fase alpha de desenvolvimento, com previsão de entrar em beta pros próximos meses. Validar as regras do começo do post com ela é algo assim:

use Respect\Validation\Validator as v;
use Respect\Validation\Exceptions\InvalidException;

$validator =
    v::object()
    ->oneOf(
        v::hasAttribute('screen_name', v::alnum('_')->noWhitespace()->stringLength(1,15)),
        v::hasAttribute('id', v::numeric())
    )
    ->hasAttribute('created_at', v::date())
    ->hasAttribute('name', $v160 = v::stringLength(1,160))
    ->hasOptionalAttribute('location', $v160)
    ->hasOptionalAttribute('description', $v160);
try {
    $validator->assert($target);
} catch (InvalidException $e) {
    echo $e->message();
}

Opa! Bem melhor. Esse é o tipo de código que você quer encontrar quando vai fazer uma manutenção:

  • Cada atributo só aparece uma única vez na rotina. As regras estão centralizadas.
  • O código deixou de ser condicional e passou a ser declarativo. Graças aos validadores compostos.
  • O objeto-alvo ($target) da validação só é utilizado uma única vez, após a definição do validador.
  • A mensagem de erro disparada na exceção será uma lista de erros, similar a mensagem abaixo:
“foobar” is not an object
“foobar” does not have the attribute “screen_name”
“foobar” does not have the attribute “id”
“foobar” does not have the attribute “created_at”
“foobar” does not have the attribute “name”

Você pode reaproveitar esse $validator para validar quantos objetos nessa especificação você quiser sem ter que reconstruir a regra. Cada partezinha do validador é reutilizável, como você pode notar no $v160, que eu declarei em um ponto e apliquei em dois outros lugares.

Há também pontos de integração com o Zend Validate e o Symfony, que eu mencionei lá em cima. Com eles você consegue usar a engine do Respect com as excelentes regras dessas bibliotecas. Por exemplo, a regra a seguir valida se a variável $email é nula ou um email válido:

$validEmail = v::oneOf(
    v::zend('emailAddress'),
    v::nullValue()
)->assert($email);

Como todo projeto novo, o que mais falta na biblioteca é documentação :D  Enquanto eu preparo ela, você pode ver a lista de regras de validação implementadas aqui, cada uma delas é um objeto reutilizável de baixo acoplamento que você pode utilizar diretamente, se quiser.

Como Encontrar Bons Desenvolvedores PHP

Há aproximadamente um mês o Cal Evans publicou em seu blog um email que ele redigiu para pessoas que lhe perguntam a melhor maneira de encontrar bons desenvolvedores PHP. Com a devida permissão, traduzi seu excelente texto para português abaixo. Logo após o texto, coloquei comentários meus que acho pertinentes para o cenário brasileiro.

Essa é uma pergunta muito interessante. Fico feliz em compartilhar meu conhecimento. Deixe-me começar com um pouco de esclarecimento. Um dos meus trabalhos paralelos é encontrar e testar desenvolvedores PHP. Tentarei fazer com que essa resposta não aja a favor dos meus interesses. :)

  1. phpdeveloper.org

    Chris Cornutt mantém esse site e ele publicará suas vagas gratuitamente. Esse é um site bem respeitado na comunidade PHP e é utilizado por muitos outros sites, portanto sua vaga será vista por muitos olhos.

  2. PUGs

    Encontre seu Grupo de Usuários PHP local. (Eu sempre me surpreendo com pessoas que não fazem isso.) Primeiro, descubra sobre as regras para publicar vagas em sua lista de emails. É melhor perguntar antes do que ser banido depois. A maioria dos grupos não se incomoda com a publicação de vagas, desde que não seja frequente. Aqueles que estiverem em cima do muro podem ser inclinados [a aceitar vagas] oferecendo-lhes pizza/cerveja em uma próxima reunião. (Faça as contas aqui, grandes sites de empregos cobram de $400 a $500 [dólares] para publicar uma vaga que atinge o mundo inteiro. $75 [dólares] de pizza e cerveja levarão sua mensagem para as 20 ou 30 pessoas com as quais você realmente quer falar.) Outra dica, se ofereça para hospedar as reuniões do grupo em algum mês. Essa é uma grande oportunidade para ter cerca de 20 desenvolvedores em seu escritório para beber uma cerveja contigo, conversar sobre trabalho e talvez até lhes dar seu endereço de email para “discutir oportunidades” com eles.

  3. Comunidade

    Essa é uma jogada um pouco demorada mas trará resultados. Envolva-se na comunidade. Se você já é envolvido, ótimo, mantenha; senão, começe agora. Como disse o grande vendedor Harvey Mackay: “Cave bem seu poço antes de ficar com sede“. Passe um tempo no irc, procure pessoas que estão procurando emprego, descubra quem é quem e mantenha uma lista de pessoas que você deseja contratar. O mesmo se aplica a listas de email. A maioria dos grupos de usuários possuem listas de email. Inscreva-se, observe, responda perguntas quando puder, publique quando apropriado e tome notas em quem está respondendo perguntas e quem as está perguntando. (As duas coisas são muito boas, a propósito.) Em sua lista de quem é quem, marque as estrelas e os recém-chegados. Quando você precisar de um talento rápido que possa trabalhar imediatamente, você terá sua lista de estrelas. Quando você tiver uma vaga aberta que não é urgente, procure na sua lista de recém-chegados.

    Uma palavra de aviso. Eu já vi empresas contratarem da comunidade e não perceber a responsabilidade que estão assumindo. Membros da comunidade são ótimos funcionários porque eles aprenderam a ajudar e onde procurar ajuda. Você precisa deixá-los continuar com essa prática. Isso significa que você receberá vários pedidos por ano de cada membro para participar de conferências; provavelmente até palestrar nelas. Você tem que estar preparado para permitir e até mesmo encorajar esse comportamento. É uma parte do que você está comprando.

  4. Considere trabalho a distância

    [Novamente, um pouco de esclarecimento, Eu falo bastante sobre trabalho a distância e ajudo empresas a descobrirem como definir programas de trabalho a distância que funcionam.]

    Se você não consegue encontrar um talento ou um talento dentro da sua faixa de preços, considere contratar alguém de outra região. Eu recebi um email de um cara ontem que estava procurando por empregos a distância. Esse cara é um peso-pesado em relação a código mas não pode mover-se por motivos de família. Se não fosse pelas restrições de localidade qualquer time estaria feliz em tê-lo mas muitas empresas o ignoram (e outras adoram-no) por causa da falta de visão de “se eu não posso vê-lo, como posso saber se está trabalhando?” Trabalho a distância multiplica seu pool de possíveis talentos em 100 vezes.

    Trabalho a distância também acaba com a infundada prática de bater ponto no serviço.

    É claro, trabalho a distância não é para todo mundo mas eu gerenciei times inteiros nos quais dois membros estavam a pelo menos 150 milhas um do outro e com o programa certo e as pessoas certas, funciona muito bem. Você contrata o talento que precisa para levar seu projeto pra rua. Os desenvolvedores ganham a liberdade para trabalhar onde quiserem… contanto que trabalhem.

  5. Compareça a Conferências

    Essa é a razão pela qual a maioria das empresas recebe eventos e conferências. Elas estão procurando a mesma coisa que você, acesso a talentos que puderem contratar. De qualquer forma você não tem que ser um patrocinador para procurar talentos em uma. Inscreva-se, compareça e fique por lá. Geralmente existe um lugar para os que não quiserem assistir uma determinada sessão ficarem. Fique por lá com uma camiseta escrito “ESTOU CONTRATANDO DESENVOLVEDORES PHP” nas costas. (e tenha uma boa pilha de cartões de visita, um bloco de notas e uma caneta em mãos.)
    Atualmente, ZendCon, CodeWorks e php|tek são as conferências nas quais estou pessoalmente envolvido, estamos felizes em recebê-lo em qualquer uma delas. Se você está incerto em qual é melhor pra você, entre em contato com o seu grupo de usuários local e pergunte a eles.
    Ah, e estou organizando o DayCamp4Developers.com, uma conferência online, e eu o encorajo a ser um patrocinador! :)
  6. Faça seus desenvolvedores escreverem sobre sua empresa/ambiente de trabalho.

    Sua empresa tem um blog? Seus desenvolvedores publicam regularmente nele? Cada desenvolvedor tem uma “história especial” sobre algo legal sobre a empresa que mudou suas vidas? Reúna essas histórias em algum blog, preferencialmente em um blog corporativo. Quando você abordar um candidato em potencial, a primeira coisa que os mais espertos farão será ir ao Google pesquisar sobre você e sua empresa. Tenha certeza que o que eles virem os animará.

  7. Faça coisas que inspirem seus desenvolvedores a falarem bem de você

    Encontre algo todo mês que você pode fazer para que seus desenvolvedores parem e pensem “Uau, eu tenho orgulho de trabalhar aqui” Um holerith não é suficiente hoje em dia. Você quer usar seus talentos para seu benefício, portanto precisa agradá-los. Jantar, dança, chocolates, jóias, você sabe como agradar um namorado(a), coloque o mesmo esforço pra atrair desenvolvedores. Nota: Não estou dizendo que precisamos voltar aos dias de recrutamento com limusines ou mesas de pebolim. De qualquer forma, se você puder dar máquinas com dois monitores, quatro núcleos e 8GB de RAM com 1TB de HD para cada desenvolvedor, esse é um ponto de venda! Diga as pessoas. Mais importante, instale máquinas assim para seus funcionários e ELES dirão as pessoas.

Resume-se a isso. Existe uma guerra de talentos acontecendo para desenvolvedores PHP. Prometer um pagamento constante não funciona mais. Desenvolvedores precisam sentir-se respeitados, poderosos e engajados. Se você quer usar seus talentos, precisa mostrá-los que você e sua empresa os respeitam, que seu projeto tem desafios técnicos interessantes para que eles superem, e que você está disposto a deixá-los resolver esses desafios da forma que eles quiserem.

Espero que isso o tenha ajudado,

=C=

  • Para encontrar os grupos de usuários brasileiros, procure nessa relação de listas de email do ProPHP.
  • A lista php-empregos aceita vagas de emprego e é lida por milhares de associados todos os dias.
  • No Brasil existem muitas conferências locais, procure nos grupos de usuários.
  • Usuários de PHP no Brasil se reunem em eventos como o FISL e o LatinoWare, compareça a esses eventos também.
  • A maior conferência nacional sobre PHP é a PHP Conference. Há sempre uma porção de ótimos talentos por lá.

Dependency Injection e Containers sem enrolação

Dependency Injection é simples, tão ridiculamente simples que pode ser considerada apenas bom senso. Dá uma olhada nesse exemplo danado de uma classe mal projetada:

<?php
$accounts = new Accounts('localhost', 'root', '', 'acc');
class Accounts
{
    protected $db;
    public function __construct($dbHost, $dbUser, $dbPass, $dbName) {
        $this->db = new PDO("mysql:host=$dbHost;dbname=$dbName", $dbUser, $dbPass);
    }
    /* ... */
}

Consegue enxergar o problema aí? É normal haverem dependências entre as classes, mas nesse caso a PDO está sendo instanciada dentro da Accounts, o que leva a uma série de problemas em potencial:

  • Imagine que você precisa configurar, opcionalmente, a PDO para trabalhar ou não com disparo de exceções.
  • Você precisa trocar o driver de mysql para sqlite.

Para resolver esses dois problemas, você poderia fazer algumas alterações:

<?php
$accounts = new Accounts('localhost', 'root', '', 'acc', 'mysql', true);
class Accounts
{
    protected $db;
    public function __construct($dbHost, $dbUser, $dbPass, $dbName, $dbDriver='mysql', $throwExceptions=false) {
        $this->db = new PDO("$dbDriver:host=$dbHost;dbname=$dbName", $dbUser, $dbPass);
        if ($throwExceptions) {
            $this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        }
    }
    /* ... */
}

Isso é ruim porque todos os requisitos para essa alteração giravam em torno da PDO, mas quem foi alterada foi a Accounts, que não tem nada a ver com a história. A PDO É OS POLÍTICO E A ACCOUNTS É O POVO BRASILEIRO SOFREDOR.

Sou totalmente a favor de uma IDE que emite um curto porém forte choque elétrico em programadores que criam construtores com muitos parâmetros, e um boost extra na voltagem para aqueles que colocam mais de um parâmetro opcional. A coisa mais melequenta que existe é ficar testando parâmetros, valores padrão, ordzZZZzzzzZ. Projete seus construtores para serem simples e o universo conspirará a favor do seu software.

Usando Dependency Injection, você escreveria o código acima assim:

<?php
$db = new PDO('mysql:host=localhost;dbname=acc', 'root', '');
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$accounts = new Accounts($db);
class Accounts
{
    protected $db;
    public function __construct(PDO $db) {
        $this->db = $db;
    }
    /* ... */
}

Containers

Agora você moveu a parte tosca do código, que é instanciar e configurar a PDO, para fora da classe Accounts, que não tem nada a ver com isso. O problema é que agora você tem que escrever três linhas pra fazer o que antes era feito com apenas uma. Downers.

Pra resolver esse probleminha, você pode gerenciar as dependências usando um Container (coloquei até uma outra dependência aí dentro, pra ilustrar):

<?php
$statistics = AccountsContainer::getAccountsStats();
abstract class AccountsContainer
{
    public static function getDb()
    {
        $db = new PDO('mysql:host=localhost;dbname=acc', 'root', '');
        $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        return $db;
    }
    public static function getAccounts()
    {
        return new Accounts(self::getDb());
    }
    public static function getAccountsStats()
    {
        return new Statistics(self::getAccounts());
    }
}

Agora três objetos distintos que dependem uns dos outros podem ser configurados independentemente e o código fica mais organizado. O container sabe quem depende de quem, você pede um objeto e ele resolve todas as dependências.

Você pode implementar containers como bem entender. O Symfony Dependency Injection Container por exemplo permite você fazer MISÉEERIAS, com o preço de ser complexo pra caralho. Você pode descrever as dependências usando um arquivo de configuração XML, YAML ou LOLCODE por exemplo.

Mas fica a dica: você só vai usar containers configuráveis e complexos em projetos gigantemente enormes. Pra coisas simples, comece simples. O que importa é o conceito.

Abraços encapsulados pra vocês.

php5.net.br – Conteúdo sobre PHP5 em Português

Bem-vindo ao php5.net.br, esse é o primeiro post aqui, e o único objetivo desse primeiro post é encher linguiça e falar sobre coisa alguma.

Sou o Alexandre e trabalho com PHP tempo suficiente pra nem lembrar há quanto tempo eu realmente trabalho com PHP. Desenvolvo para a Kingo Labs, startup brasileira que caça dados de diversas fontes e cria maneiras úteis e criativas de agregá-los, processá-los visualizá-los. Escrevo aqui, no pla.net.br, no meu blog pessoal, no PHP Rocks e já tive outros blogs sobre tecnologia.

Os assuntos relacionados ao PHP que vou tratar aqui são:

  • Projetos brasileiros em PHP orientado a objetos, que sem dúvida é algo que merece destaque. Não terei dó de elogiar nem orgulho pra criticar (e vice-versa ao contrário) as tranqueiras em nível de arquitetura e/ou/inclusive código.
  • Teoria envolvendo orientação a objetos, padrões de projeto, reusabilidade de código, boas práticas e gambiarras lindas com foco em PHP.
  • Novos recursos, recursos pouco conhecidos, recursos obscuros e recursos pagãos do PHP.

O que você não verá aqui é:

  • Exemplo de código idiota, usando classes com nomes de animais, carros e coisas que você nunca vai usar na vida.
  • Enrolação e aquelas palavrinhas difíceis pra tentar fazer o autor parecer inteligente.
  • Indiretas, se é que vocês me entendem.

Beijo no cóccix e até a próxima ;)