Servindo uma documentação PHP Doc

Artigo que mostra um exemplo de como configurar o docker compose para servir uma doc gerada por PHPDoc dinamicamente.

Introdução

O padrão de documentação PHPDoc tornou-se referência para diferentes projetos em PHP, tanto em bibliotecas, frameworks, quanto em apps. Embora a chegada de typehints cada vez mais sofisticados no PHP tenha tornado alguns tipos de documentação redundantes ou desnecessárias, manter algumas descrições importantes em alguns tipos de classes e métodos ainda me parece um meio simples e eficaz de manter código e documentação caminhando juntos. Embora seja possível criar documentações apartadas da aplicação, é sempre útil poder recorrer diretamente a uma explicação no próprio código-fonte. Além disso, a proximidade entre código-fonte e documentação é um fator positivo para que a documentação não fique desatualizada, já que revisões no código acabam naturalmente exigindo uma revisão da PHPDoc.

Neste artigo, verermos como servir uma documentação em PHPDoc na própria estrutura de desenvolvimento de um repositório.

Docker e docker compose

Um dos meios mais práticos para se manter uma estrutura para desenvolvimento local é utilizando containers com Docker. Ele encapsula a aplicação quase como se fosse um processo do S.O. host, mas dando a liberdade para que o processo se comporte como se estivesse num ambiente isolado, com suas próprias dependências, sem interferir nos demais containers à sua volta.

É comum se utilizar o docker compose para especificar o ecossistema (total ou parcial) de um sistema que envolve vários componentes. Por exemplo, especificar um serviço que encapsula a aplicação (que responde na porta 80 e/ou 443), outro para encapsular o banco de dados (usando una porta específica), e assim por diante. Porém, é possível utilizar o docker compose para especificar serviços auxiliares ao ambiente de desenvolvimento, mas que não vão necessariamente interagir com a aplicação. Neste sentido, é possível utilizá-lo para especificar um serviço que servirá a documentação PHPDoc do seu projeto em uma porta específica.

A seguir, mostro um exemplo de docker-compose.yml que expõe uma web app, mas que também expõe a documentação PHPDoc:

  name: foo
  services:
      app:
          ...

      db:
          ...

      doc:
          image: phpdoc/phpdoc
          volumes:
              - .:/data
              - /data/var
          ports:
              - 8080:8080
          profiles:
              - dev
              - doc
          entrypoint: /bin/bash -c
          command: >
              "
                  /opt/phpdoc/bin/phpdoc run --no-interaction --ansi --encoding=UTF-8 --title='Foo' --cache-folder=/data/var/cache/phpdoc --log=/data/var/logs/phpdoc --directory=/data/src --visibility=public --target=/data/var/reports/phpdoc
                  echo -e '\\nStarting to serve the phpdoc at http://localhost:8080'
                  php -S 0.0.0.0:8080 -t /data/var/reports/phpdoc/
              "

Note alguns detalhes importantes:

  • Foi criado um volume que mapeia o diretório raiz da app no dirtório /data do container do PHPDoc.
  • Foi estabelecido que os arquivos temporários (cache e log) gerados pelo PHPDoc serão armazenados em /data/var, mas esse diretório não ficará mapeado na máquina hospedeira (você pode optar por remover essa definição, caso queira acessar esses arquivos a partir da sua máquina hospedeira.
  • Foi estabelecido que a porta 8080 será reservada para servir a documentação. Você pode utilizar outra que quiser, conforme ache mais conveniente.
  • Foram adicionados tags de profile ao serviço de doc: dev e doc. Isso é útil para que esse serviço seja iniciado apenas se você chama o docker compose passando alguma das tags de profile. A escolha dessas tags foi apenas por conveniência. Podem existir outros serviços que estejam relacionados à documentação (por exemplo, servindo localmente uma documentação da API a partir de uma especificação em Swagger, Postman ou Api Blueprint). Assim como podem ter outros serviços que são relacionados ao process de desenvolvimento, e que podem compartilhar a tag dev para facilitar subí-los todos de uma vez. Veja mais sobre este assunto na documentação sobre profiles de serviços no Docker.
  • Foi ajustado o entrypoint e command da imagem do phpdoc, já que ela foi projetada para executar a construção da documentação em HTML, mas não serví-la. Portanto, após o ajuste, o container passa a rodar a criação da documentação, soltando o resultado na pasta /data/var/reports/phpdoc e, em seguida, utilizado o servidor HTTP nativo do PHP para servir o conteúdo desta pasta pela porta 8080. Embora o servidor nativo do PHP não seja recomendado para expor páginas em ambientes produtivos, ainda é útil para situações locais em que se precisa de um "servidorzinho" pra servir páginas estáticas, por exemplo. Note que a chamada do comando phpdoc pode especificar vários parâmetros e é recomendado que você veja a documentação dos parâmetros permitidos no PHPDoc, ou então salvar um arquivo de configuração do PHPDoc e mapeá-lo de forma que fique visível para o serviço/container do PHPDoc via volume.

Portanto, ao executar docker compose --profile doc up, a aplicação será iniciada juntamente com o serviço de PHPDoc, que irá gerar a doc e depois serví-la. É preciso acompanhar os logs do serviço para assegurar que tudo rolou como esperado, então bastará acessar o endereço http://localhost:8080 na sua máquina host para acessar a documentação.

Uma outra abordagem possível seria especificar 2 serviços: (1) apenas para gerar a documentação; e (2) outro para servir o conteúdo estático gerado. Neste caso, o serviço de geração da documentação será um serviço "one-off", que apenas executa e se encerra, mas irá gerar o conteúdo numa pasta que pode estar compartilhada por volume para ser servida pelo serviço servidor de HTML. Abaixo está um exemplo com esta outra abordagem:

    build-doc:
        image: phpdoc/phpdoc
        volumes:
            - .:/data:ro
            - phpdoc-volume:/data/var/reports/phpdoc:rw
        profiles:
            - dev
            - one-off
        command: "run --no-interaction --ansi --encoding=UTF-8 --title='Foo' --cache-folder=/data/var/cache/phpdoc --log=/data/var/logs/phpdoc --directory=/data/src --visibility=public --target=/data/var/reports/phpdoc"

    doc:
        image: php:8-cli-alpine
        volumes:
            - phpdoc-volume:/var/www/html:ro
        ports:
            - 8081:8081
        profiles:
            - dev
            - doc
        command: "-S 0.0.0.0:8081 -t /var/www/html"

volumes:
    phpdoc-volume:
        external: false

Neste caso, as responsabilidades ficam mais isoladas, então os comandos firam mais simples, embora aumente-se a quantidade de serviços para lidar. Porém, um benefício é que você pode querer só atualizar o conteúdo da documentação conforme ache necessário ao invés de atualizá-la sempre.

0 comentários