Blog

Composer - gerencie as dependências de seu projeto como um maestro

Eriksen Costa03.01.2012 - 10:30(upd: 25.07.2013 - 16:44)

Quando o Symfony2 foi lançado, escrevi um post onde dizia que o framework estava liderando a inovação no mundo PHP. Foi exatamente assim que iniciei a minha palestra no PHP Conference Brasil no final do ano passado e não é apenas uma questão de entusiasmo. A comunidade Symfony tem criado novas e interessantes bibliotecas e ferramentas e isso tem tido um impacto considerável na formação de um ecossistema na comunidade PHP. E uma dessas ferramentas, apesar de ainda muito recente, resolve um dos grandes problemas existentes em projetos PHP: o gerenciamento das dependências.

O Composer é esta ferramenta. Trata-se de um gerenciador de pacotes para bibliotecas PHP, disponível como utilitário de linha de comando.

Guia rápido de 1 minuto

Talvez você se questione e/ou sofre antes do tempo: “Mais uma ferramenta para aprender? Não tenho paciência para isso!“. Mas calma, aprender o básico de como usar o Composer não demora 1 minuto. E você verá que vale a pena!

Você precisa do PHP 5.3.0 para executar o Composer.

O Composer é distribuído como um arquivo Phar. Digamos que você esteja interessado em criar um webscrapper usando algumas bibliotecas PHP 5.3: Buzz, Symfony DomCrawler e Symfony CssSelector. Vamos instalar o Composer em nosso diretório de projeto:

Crie o arquivo composer.json com o seguinte conteúdo:

Execute o utilitário Composer:

Após o download das dependências, você pode começar a usar as classes das bibliotecas no seu projeto. O Composer gera um autoloader automaticamente, basta incluir o mesmo no seu código:

Agora você tem todas as dependências para fazer o seu webscrapping em PHP:

De onde veio o código?

Você deve estar se perguntando de onde veio o código. O Composer é apenas um utilitário que faz o download de um pacote através de um repositório de pacotes chamado Packagist. Desenvolvedores submetem o endereço do repositório público de suas bibliotecas para o Packagist que então monitora o repositório e cria novos pacotes quando uma nova tag é criada no mesmo.

Todas as informações ficam disponíveis na web através do site Packagist. Cada biblioteca de nosso webscrapper tem uma página no Packagist (veja: Buzz, Symfony CssSelector e Symfony DomCrawler) com informações úteis como autor, licença, link para o repositório público, versões disponíveis, dependências, entre outras. Essas informações são extraídas do arquivo composer.json.

Para publicar um pacote Composer no Packagist, é necessário criar um arquivo composer.json no diretório raiz do projeto. Esse arquivo precisa conter, além de quaisquer dependências, informações úteis como as já citadas anteriormente. Consulte a documentação do Packagist para mais informações caso esteja interessado em disponibilizar algum código de sua autoria como pacote Composer.

É possível visualizar essas informações através do Composer, com o comando show:

Note o atributo “psr-0” nas informações do pacote Symfony Locale. Isso significa que o pacote tem conformidade com o padrão PSR-0. O Composer adiciona automaticamente pacotes PSR-0 no autoloader.

Você também pode pesquisar pelos pacotes disponíveis no Packagist através do Composer com o comando search:

Como incorporar pacotes não disponíveis no Packagist

O Packagist, assim como o Composer, é muito recente e com isso talvez uma biblioteca desejada por você não esteja publicada como um pacote Packagist. Mas isso não é um problema: você pode adicionar repositórios de pacotes adicionais no seu arquivo composer.json, uma referência para o repositório da biblioteca desejada ou até mesmo uma referência para um pacote PEAR!

Um repositório de pacote é um repositório Composer. O Packagist é um repositório Composer e você pode usar o código-fonte do mesmo (está disponível no GitHub) para subir o seu próprio repositório Composer. Um repositório Composer pode ser configurado da seguinte forma no composer.json:

Você não precisa, no entanto, manter um site como o Packagist para distribuir seus pacotes. Caso o repositório de sua biblioteca seja o Git, você pode adicioná-lo como repositório de pacote (para isso deve existir um arquivo composer.json no diretório raiz do pacote):

Ok, mas e se você não tiver acesso ao repositório para criar o composer.json? Não tem problema, você pode especificar a informação do pacote diretamente no composer.json de seu projeto:

Note especificamos aqui duas fontes para o pacote: dist e source. Por padrão, a fonte dist sempre é usada. source é usada quando você usa a opção —dev junto com o comando install ou update do composer.phar. Caso especifique os dois, tenha certeza que ambas as fontes possuem a mesma versão do código, evitando assim surpresas causadas por código diferente entre as fontes (quebra da API, correções de bug, entre outras).

Por último e não menos importante, é possível também usar pacotes PEAR:

E se a biblioteca ao qual você depende não estiver em conformidade com o PSR-0 (ex: bibliotecas PHP 5.2 seguindo as conversões de nomeação PEAR)? Como alternativa você pode usar o componente Symfony ClassLoader e criar você mesmo o arquivo de autoloading. Adicione no seu composer.json:

Crie um arquivo autoload.php na raiz do seu projeto com o seguinte código:

Não é tão ágil quanto usar o autoloader gerado pelo Composer mas ainda assim é muito simples de configurar.

Legal! Mas qual era o problema mesmo?

O problema ao qual o Composer se propõe a resolver é o de reuso de código. Ao tornar a incorporação de dependências um processo mais simples, os autores de bibliotecas ganham agilidade e praticidade para reusar código de outras bibliotecas ao invés de reinventar a roda. Desenvolvedores clientes (que usam as referidas bibliotecas) também ganham com a praticidade já que fica fácil explicitar as dependências de um projeto no arquivo composer.json, que é muito mais simples do que configurar atributos de VCS como o SVN externals e Git submodules.

E comparado com o PEAR, o Composer tem uma filosofia bastante diferente: o Composer gerencia pacotes no diretório do projeto enquanto o PEAR gerencia pacotes no nível do sistema (semelhante ao aptitude, yum, entre outros). O problema de lidar com dependências dessa forma é quando você tem muitos projetos executando em uma máquina com os diferentes projetos dependendo de diferentes versões de uma mesma biblioteca. Não há uma forma fácil de instalar as diferentes versões das dependências e caso não haja um procedimento de empacotamento dessas dependências com o build (se houver) do projeto, fica fácil esquecer a instalação de uma dependência.

Como as dependências em um projeto usando o Composer ficam explícitas, existe também o benefício de visualizá-las mais rapidamente. Você nem mesmo precisa versionar as bibliotecas instaladas pelo Composer no diretório vendor/ (o que reduz o tamanho total do repositório de seu projeto): qualquer pessoa que fizer uma cópia do repositório do projeto e executar o comando composer.phar update irá fazer o download das dependências como especificado nos arquivos composer.json ou composer.lock. Dessa forma se evita surpresas como algum outro desenvolvedor ter problemas porque instalou uma dependência em uma versão diferente, seja mais nova ou mais velha, com diferenças no comportamento na API ou bugs.

Quanto a especificação de versão de uma dependência, eu tenho a tendência de ser conservador, preferindo especificar uma dependência exata para ter previsibilidade na implantação do código nos ambientes de produção/stagging/testing. Se você usar uma dependência usando um operador de desigualdade (>, >=, <, <=), o Composer irá escolher a maior versão disponível (se for “>X.Y.Z”, por exemplo, você poderá pegar uma versão baseada na árvore principal de desenvolvimento, com código instável - claro, dependendo de como o projeto organiza seu repositório).

Isso é potencialmente mais perigoso quando não se versiona o diretório com o código-fonte das dependências (vendor/ nesse caso) já que seu processo de implantação irá requerer a geração de um pacote (build) com a execução do comando composer.phar install. Procure basear a sua especificação de dependência levando em conta a maturidade do processo de desenvolvimento do pacote que irá adotar (os componentes do Symfony, por exemplo, possuem o compromisso de não quebrar a compatibilidade de API em métodos públicos marcados com a tag @api, sendo mais seguros para definir com um operador de desigualdade), seja mais específico caso não encontrar compromissos com compatibilidade de API ou um processo de release previsível.

Conclusões

O Composer, como bem definiu Henri Bergius, resolve o problema do compartilhamento de código no mundo PHP. O utilitário é simples e, somente no Packagist, temos 227 pacotes disponíveis. É um número bastante expressivo já que todos os pacotes são PHP 5.3+, para uma referência, o PEAR (1 e 2) contém ao todo 592 pacotes sendo 192 PHP 5+.

Diante desses números, é evidente que o Composer já causou seu impacto na comunidade PHP. Está promovendo reuso de código e está tornando simples o que outrora era um processo falho (instalação no nível do sistema via PEAR) ou desajeitado (SVN externals, Git submodules). A ferramenta, como já frisada, é nova e ainda tem um longo caminho para melhorias como adicionar suporte para autoloading de código não PSR-0 (como bibliotecas PEAR e classes do Zend Framework 1.X, p.e.) mas ainda assim é a melhor ferramenta disponível. Use-a!

Referências

Esse post foi fortemente baseado nos seguintes posts: