Composer – Gerencie Dependências no PHP Facilmente

Logotipo do ComposerTodo projeto possui dependências. Isso é completamente normal e existe em qualquer linguagem de programação que você possa usar para criar seu projeto, seja PHP, Javascript, Ruby ou Go. O ponto é que eu vejo muitos projetos por aí que não usam sequer um gerenciador para ajudar a manejar toda a bagunça que o uso de muitas dependências podem causar, especialmente porque é comum uma dependência ter como dependência outra dependência (plugins do jQuery são um exemplo, assim como Underscore e Backbone), e, no server-side, isso só se torna ainda mais importante: Já imaginou o quão grave seria um problema com alguma dependência importante no seu projeto e o quão louco seria atualizá-la quando vários outras dependências do seu projeto a usam também? Pois então, isso é bem comum quando se usa frameworks..E por isso que hoje vou falar um pouco sobre o Composer, um gerenciador de dependências esperto para PHP que sabe como administrar tudo da melhor forma possível.

O Composer, assim como o Bower, é um..gerenciador de dependências. Diferente do Bower, entretanto, o Composer gerencia dependências em PHP, servindo portanto apenas para atender as dependências no server-side. Com esse foco bem especificado, o Composer fornece recursos extras para atender melhor esse público, como:

  • Criação de auto-loader com base nas dependências;
  • Otimização do auto-loader para carregamento mais fácil das dependências;
  • Opção para colocar as classes do seu projeto no auto-loader automaticamente conforme um determinado padrão;
  • Configurar “binários” relacionados tanto às dependências quanto ao seu projeto;
  • Executar determinadas operações (callbacks) após o término da instalação;
  • Criação de arquivo de lock para garantir a instalação de dependências nas versões corretas;
  • Entre outros recursos;

Como você pode ver, todos esses recursos mencionados acima e que o Composer possui não estão presentes no Bower. Por quê? Porque..em sua maioria, não faz sentido. Como, por exemplo, seria possível fazer um auto-loader para CSS e Javascript, sendo que essas linguagens não fornecem o mínimo suporte para isso? Bom…parece difícil.

Bom, voltando ao assunto, dentre os recursos que eu particularmente mais curto no Composer estão os recursos voltados ao auto-loader, que é algo um pouco chatinho de configurar, e também a criação do arquivo de lock, que embora pareça ser um pouco desnecessário quando se tem todas as versões desejadas listadas no arquivo de configuração, pode ajudar a resolver algumas tretas difíceis de prever. Esses dois casos eu explico o por que esses recursos são interessantes abaixo

Suporte a criação de auto-loader

No PHP, antigamente, para carregar um outro arquivo – contendo funções, classes, ou seja lá o que for – você tinha duas opções:

  • Usar require(“<arquivo>”) ou..
  • Usar include(“<arquivo>”)

O problema é que isso proporcionava certas dores de cabeça em alguns casos, pois:

  • Os nomes dos arquivos podiam ser não padronizados;
  • Por não ser padronizado, sempre que você desejava mudar o nome do arquivo tinha que sair mudando todas as chamadas ao require(“<arquivo>”) ou include(“<arquivo>”);
  • Para carregar dependências, você tinha que carregá-las do mesmo jeito: usando require(“<arquivo>”); ou include(“<arquivo>“);
  • Se um arquivo tinha muitas dependências, consequentemente haveria muitos require(“<arquivo>”); ou include(“<arquivo>”); no topo do arquivo;

Pode parecer idiota, mas esse conjunto de problemas por si só já complicava bastante a vida do programador PHP. E é por isso que, algum tempo atrás, o PHP ganhou suporte aos autoloaders. Mas, o quê são auto-loaders? Simples: São funções responsáveis por carregar CLASSES ou INTERFACES quando estas são requisitadas e ainda não estão disponíveis no ambiente de execução.

Ou seja, suponha que você possua uma classe A definida num arquivo chamado A.php, e que você queira instanciá-la a partir de um arquivo index.php. Usando require(“<arquivo>”) ou include(“<arquivo>”), você simplesmente faria algo como require(‘A.php’); no topo do arquivo e depois chamaria new A();. Mas, usando auto-loaders, bastaria definir o auto-loader (ou usar require(“<arquivo”); para inclui-lo a partir de outro arquivo) e depois simplesmente usar new A(); para que o PHP automagicamente detecte que a classe A ainda não foi carregada e chame o auto-loader para que ele possa carregar tal classe de forma automática.

Mas…se você precisou incluir o auto-loader, qual a vantagem dele então? A vantagem é que você só faz isso uma única vez, e então após ele estar carregado e bem definido você pode carregar quantas classes quiser, de forma bem prático e fácil, sacou? 🙂

Agora, vamos a explicação relacionada ao arquivo de lock..

Criação do arquivo de Lock

A criação do arquivo de lock, pelo Composer, é um dos recursos que eu mais gosto e que particularmente acho que devia ter em todo gerenciador de pacotes. Imagine você que você inclui uma dependência, e essa dependência possui outras inúmeras dependências. Oras, não faz sentido incluir as dependências das dependências do seu projeto no seu arquivo de configuração, certo? Quem diria ainda incluir as dependências das dependências das dependências do seu projeto..

Pois bem, por isso que alguns gerenciadores de dependências criam, por padrão, um arquivo de lock: Esse arquivo salva, além das versões fixas das dependências do seu projeto, as versões fixas das dependências das dependências, e assim recursivamente.

Mas, o que seria uma versão fixa? Simples! Seria o número exato (1.3.1, por exemplo), as vezes acompanhado do hash de commit que originou aquela versão. Salvando essa versão fixa, há uma garantia maior de que as dependências que você instalará ao executar o comando de instalação no gerenciador de dependências vão ser exatamente as mesmas. Mesmo que uma dependência de alguma dependência do seu projeto tenha uma atualização no meio tempo entre uma instalação e outra.

Mas que tipo de problemas isso pode evitar? Simples! Pode evitar problemas com dependências que não seguem a risca o versionamento semântico (que abordei neste post), e acabam por introduzir quebras mesmo em sub-sub-versões (também chamada de minor versions, ou um número representando x na versão “1.3.x”). No PHP, eu não me lembro de cór projetos que já cometeram esse erro, mas no Javascript (que, só para lembrar, o Composer não gerencia), dois projetos que já fizeram isso foram o BackboneJS e o UnderscoreJS.

Por causa disso, a criação de um arquivo de lock acaba por ser sim importante, e, embora complique um pouco a administração de dependências, ajuda a resolver muitos problemas e garante que bugs não apareçam no seu código devido a sub-sub-dependências mal administradas.

Tá mas..qual a diferença entre o Composer e o PEAR/PECL?

Bom, a diferença do Composer para o PECL é que o Composer não…gerencia extensões nativas do PHP. Ou seja, o Composer manja de gerenciar dependências em PHP puro. Ponto.

Quanto ao PEAR..A diferença está basicamente nos recursos extras que o Composer provê, como criação de arquivo de lock, criação e otimização de auto-loader e possibilidade de configuração de execução de tarefas após processos de instalação/atualização de dependências. Mas, há um plus aqui: Enquanto o PEAR normalmente opera de forma global (você pode usar o PEAR de forma “compartilhada” seguindo alguns passos extras,  mas nunca localmente por projetos), enquanto o Composer trabalha mais..localmente, ou seja, cada projeto PHP contém suas próprias dependências, e não há dependências globais (você não consegue por exemplo instalar uma dependência de forma global uma única vez para todos os projetos, mas você consegue instalar projetos independentemente de forma global, como aqueles que instalam binários no seu PATH, por exemplo).

Agora, uma curiosidade: É possível instalar pacotes PEAR com o Composer. Mas, como esse post bem explica, isso não faz sentido pois algum tempo atrás o PEAR perdeu o suporte da maior parte dos mais populares projetos PHP.

Tutorial Básico de Uso

Com alguns dos recursos do Composer listados e explicados, vamos agora partir para um tutorial básico de uso, para aprender a usar alguns dos recursos básicos do Composer de forma rápida e simples. Veja:

1) Primeiramente, precisamos instalar o PHP – necessário para que o Composer execute – e o Composer. Para isso, há duas opções:

1.1) Instalar o PHP e o Composer manualmente;

1.2) Usar o Vagrant para criar uma máquina virtual já com PHP e Composer instalados usando o Vagrantfile que eu criei. Durante o tutorial, será necessário entrar na máquina virtual usando “vagrant ssh“, para que o Composer possa ser localizado como instalado;

2) Depois de ter o PHP e o Composer instalados, está na hora de brincarmos um pouco com a ferramenta. Para isso, vamos criar um novo projeto, acessando a pasta “projetos“, executando o comando “mkdir tutorial-composer” para criar uma pasta com o nome tutorial-composer e, dentro dela, rodando o comando “composer init” para que o Composer crie um composer.json básico para você (aperte Enter para todas as perguntas feitas, e preencha apenas a pergunta sobre o autor do projeto, com uma resposta no formato “nome <[email protected]>“). Veja:

3) Depois de criar o projeto, experimente executar o comando “composer require silex/silex:~1.3″. Esse comando vai criar uma pasta vendor e vai instalar o Silex (falarei dele no futuro) e suas dependências automaticamente. Veja:

4) Instalado o Silex, resta apenas testá-lo, certo? Para fazer isso, na pasta tutorial-composer, crie uma nova pasta chamada web e, dentro dessa pasta, crie um arquivo com o nome index.php e com o seguinte conteúdo:

5) Agora, dentro dessa pasta web, execute o servidor web embutido do PHP na mesma pasta no qual foi criada o arquivo index.php usando php -S 0.0.0.0 8080 -t .” e acesse o endereço http://192.168.33.10:8080/ – Se tudo der certo, você deve ver a página abaixo:

Olá mundo com informações extras :)

Note que, na página impressa, logo abaixo o “Hello world“, você verá um array de arquivos PHP impressos usando print_r. Esse array contem a diferença entre os arquivos carregados antes da instanciação da classe Silex\Application(); e após o carregamento de tudo (no momento no qual o router detectou qual função executar para aquela determinada rota).

Note também que, neste array, todos os arquivos estão presentes dentro da pasta vendor, criada automaticamente pelo Composer. Isso acontece pois o Silex e suas dependências já vem configurados para mostrar ao Composer que padrão estão utilizando para a nomenclatura de classes, e assim o Composer é capaz de, automaticamente, criar um auto-loader para carregá-las apenas quando for necessário.

Uma observação importante é que isso não significa que o Composer não pode auto-carregar as classes do seu projeto. Ele pode, só não será coberto por este tutorial.

6) Dê uma olhada no arquivo composer.lock e veja que se trata de um arquivo muito maior que o arquivo composer.json. O motivo, como explicitado acima, é que o composer.lock salva as versões exatas para cada sub-dependência do Silex, recursivamente.

7) Para testar se o Composer é capaz de repetir a instalação corretamente, experimente apagar a pasta vendor e executar o comando composer install. Você verá que o Composer recriará a pasta vendor, baixando novamente as dependências (ou usando o cache local para instalação), e com isso deixará tudo em seu devido lugar (inclusive criando o auto-loader ao final da instalação). Veja:

Conclusão

Com isso, já dá para perceber que o Composer é um gerenciador de dependências para PHP bastante responsável e que com ele o gerenciamento de bibliotecas e suas versões se torna muito mais facilitado e confiável. Além disso, nesse post deu para dar uma visão geral  sobre os auto-loaders no PHP e sobre o Silex, ambos assuntos que eu vou abordar no futuro aqui no blog (assine para não perder mais nenhum post!).

Note que este post também foi uma breve introdução ao Composer. Graças ao fato de que se trata de um gerenciador de dependências mais completo, pretendo abordar outras tarefas comuns (como atualização de pacotes, por exemplo) em outros posts. 🙂

Abaixo, ficam os links relacionados ao Composer. Até a próxima!