O Composer é uma ferramenta essencial para o desenvolvimento PHP moderno, permitindo gerenciar dependências de forma eficiente. Nesta aula, exploraremos recursos avançados que vão além do básico, como scripts personalizados, versionamento semântico, dependências apenas para desenvolvimento e criação de pacotes próprios. Esses conceitos são fundamentais para projetos profissionais e colaborativos.

Dominar esses tópicos permitirá que você automatize tarefas repetitivas, mantenha compatibilidade entre versões, otimize o ambiente de desenvolvimento e compartilhe seu código de forma padronizada. Vamos mergulhar em cada um deles com exemplos práticos.

Scripts

O Composer permite definir scripts que são executados em eventos específicos do ciclo de vida do Composer, como após a instalação ou atualização de pacotes. Isso é útil para automatizar tarefas como limpeza de cache, geração de documentação ou migrações de banco de dados.

Os scripts são definidos na seção scripts do composer.json. Eles podem ser comandos do sistema, executáveis PHP ou até mesmo chamadas a outras ferramentas. Exemplo:

{
    "scripts": {
        "post-install-cmd": [
            "@php -r 'echo \"Instalação concluída!\";'"
        ],
        "post-update-cmd": [
            "@composer dump-autoload"
        ],
        "test": [
            "phpunit"
        ]
    }
}

Para executar um script manualmente, use composer run-script test. Eventos comuns incluem pre-install-cmd, post-install-cmd, pre-update-cmd, post-update-cmd, pre-package-install, entre outros. Você também pode criar scripts personalizados sem vinculá-los a eventos.

Versionamento (semver)

O versionamento semântico (SemVer) é uma convenção amplamente adotada para gerenciar versões de pacotes. Ele segue o formato MAJOR.MINOR.PATCH: incrementos MAJOR indicam mudanças incompatíveis com versões anteriores, MINOR adicionam funcionalidades de forma compatível, e PATCH corrigem bugs de forma compatível.

No Composer, você especifica restrições de versão no require usando operadores como ^, ~, >=, etc. Por exemplo, ^1.2.3 permite qualquer versão de 1.2.3 até 2.0.0 (exclusive), enquanto ~1.2.3 permite de 1.2.3 até 1.3.0 (exclusive). É importante entender essas regras para evitar quebra de compatibilidade. O Composer respeita o SemVer ao resolver dependências, priorizando a estabilidade.

require-dev

A seção require-dev do composer.json lista dependências necessárias apenas para desenvolvimento, como ferramentas de teste, depuração ou documentação. Essas dependências não são instaladas em produção se você usar a flag --no-dev no comando composer install.

Exemplo de composer.json com require-dev:

{
    "require": {
        "monolog/monolog": "^2.0"
    },
    "require-dev": {
        "phpunit/phpunit": "^9.5",
        "mockery/mockery": "^1.4"
    }
}

Isso mantém o ambiente de produção leve e seguro. Lembre-se de sempre usar composer install --no-dev ao implantar em produção. Além disso, você pode definir scripts específicos para desenvolvimento, como executar testes automaticamente.

Pacotes próprios

Criar seus próprios pacotes e disponibilizá-los via Composer é uma prática que promove reuso e organização. Para isso, você precisa estruturar seu projeto como um pacote, com um composer.json adequado, e publicá-lo no Packagist (repositório público) ou em um repositório privado.

O composer.json de um pacote deve conter pelo menos os campos name (formato vendor/package), description, type (geralmente "library") e autoload (PSR-4 ou PSR-0). Exemplo:

{
    "name": "meu-vendor/meu-pacote",
    "description": "Um pacote exemplo",
    "type": "library",
    "autoload": {
        "psr-4": {
            "MeuVendor\\MeuPacote\\": "src/"
        }
    },
    "require": {
        "php": ">=7.4"
    }
}

Após criar o repositório no GitHub, você pode configurar o Packagist para sincronizar automaticamente. Para usar o pacote em outro projeto, basta adicionar "meu-vendor/meu-pacote": "^1.0" no require. Para pacotes privados, você pode usar o Satis ou Toran Proxy, ou referenciar diretamente o repositório VCS no composer.json do projeto consumidor.

Boas práticas

Sempre defina scripts para tarefas repetitivas, como testes e deploy. Use versionamento semântico para comunicar mudanças claramente. Separe dependências de desenvolvimento com require-dev para otimizar produção. Ao criar pacotes, siga PSR-4 para autoloading e documente bem o uso. Considere usar ferramentas como PHPStan ou Psalm para qualidade de código.

Referências

Exercícios

  1. Crie um script no Composer que execute o comando php -l (lint) em todos os arquivos PHP do diretório src/ após a instalação. Escreva o trecho do composer.json correspondente.

    ✓ Resposta:
    {
        "scripts": {
            "post-install-cmd": [
                "find src/ -name '*.php' -exec php -l {} \\;"
            ]
        }
    }
  2. Explique a diferença entre as restrições de versão ^1.2.3 e ~1.2.3 no Composer. Dê um exemplo de qual versão seria permitida por cada uma a partir da versão 1.2.3.

    ✓ Resposta: ^1.2.3 permite qualquer versão de 1.2.3 até 2.0.0 (exclusive), ou seja, versões 1.x.x. Já ~1.2.3 permite de 1.2.3 até 1.3.0 (exclusive), apenas versões 1.2.x. Exemplo: ^1.2.3 permite 1.9.9, mas ~1.2.3 não.
  3. Adicione uma dependência de desenvolvimento (require-dev) para o pacote phpunit/phpunit na versão ^9.5 no composer.json abaixo. Reescreva o JSON completo.
    {
        "require": {
            "monolog/monolog": "^2.0"
        }
    }

    ✓ Resposta:
    {
        "require": {
            "monolog/monolog": "^2.0"
        },
        "require-dev": {
            "phpunit/phpunit": "^9.5"
        }
    }
  4. Crie um composer.json mínimo para um pacote próprio chamado joao/utils que segue PSR-4 com namespace Joao\Utils apontando para a pasta src. Inclua o requisito de PHP >= 8.0.

    ✓ Resposta:
    {
        "name": "joao/utils",
        "description": "Pacote de utilitários do João",
        "type": "library",
        "autoload": {
            "psr-4": {
                "Joao\\Utils\\": "src/"
            }
        },
        "require": {
            "php": ">=8.0"
        }
    }
  5. Qual comando do Composer você usaria para instalar apenas as dependências de produção (ignorando require-dev)? Escreva o comando completo.

    ✓ Resposta: composer install --no-dev