valb-mig/teller

Scanner de docblocks @teller que mapeia regra de negocio documentada em PHP num call graph navegavel.

Maintainers

Package info

github.com/valb-mig/php.teller

pkg:composer/valb-mig/teller

Transparency log

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.0 2026-07-28 17:29 UTC

This package is auto-updated.

Last update: 2026-07-28 17:35:04 UTC


README

Scanner de docblocks @teller que mapeia regra de negócio documentada em PHP num call graph navegável — visual (cards + timeline + setas) e em texto (narrativa "Se X: chama Y — senão chama Z", pronta pra colar numa conversa com IA).

Baseado em call graph real (via nikic/php-parser), não em estrutura estática tipo UML — segue quem chama quem de verdade, incluindo branches condicionais.

Por que

Comentário de negócio junto do código sempre existiu. O que falta é uma forma de navegar essa documentação seguindo o fluxo real de execução, em vez de abrir arquivo por arquivo. O Teller lê a tag @teller (curadoria deliberada — não documenta tudo, só o que importa), monta o grafo de chamadas de verdade a partir da AST, e apresenta os dois num viewer só.

Instalação

composer require --dev valb-mig/teller

Isso instala vendor/bin/teller e deixa vendor/valb-mig/teller/public/ pronto pro viewer.

A tag @teller

Segue a convenção padrão de PHPDoc: primeira linha = título (summary), resto = descrição longa em markdown (parseado via phpdocumentor/reflection-docblock, o mesmo parser que o próprio phpDocumentor usa por baixo).

/**
 * @teller Calcula multa de atraso
 *
 * Aplica multa fixa de 2% quando o pagamento passa de 5 dias úteis do vencimento —
 * regra definida com o setor financeiro.
 *
 * - Sem atraso: valor original, sem alteração
 * - Com atraso: `valor * 1.02`
 */
public function calcularMulta(float $valor, int $diasAtraso): float
{
    ...
}

Funciona em docblock de classe/interface e de método. Também funciona solto, colado em qualquer statement de primeiro nível do corpo de um método (não desce em bloco aninhado) — útil pra documentar uma regra no meio do fluxo sem precisar extrair um método só pra isso:

public function executar(...): string
{
    /**
     * @teller Regra de atraso
     *
     * Acima de 90 dias, o boleto some pra negativação em vez de seguir o fluxo normal.
     */
    if ($diasAtraso > 90) {
        // ...
    }
}

A tag é obrigatória — sem ela, o Teller não mostra o método/comentário, de propósito: sem isso todo docblock técnico (@param, IDE helper, etc) viraria nó no grafo e a curadoria (o diferencial da ferramenta) se perde.

Quando usar (e quando não usar)

Tagueia quando pelo menos um destes for verdade:

  • Regra de negócio de verdade — decisão que veio de discussão de produto/jurídico/ comercial, não só "como" o código funciona tecnicamente.
  • Sintaxe complexa demais pra intenção ficar óbvia — quem ler o código não vai entender o porquê só olhando a implementação.
  • Regra extensa — vários passos/condições que só fazem sentido juntos, difícil de guardar na cabeça lendo linha por linha.
  • Nomenclatura não clara — nome de variável/método/classe não deixa o significado óbvio (termo específico do domínio, abreviação, nome legado).

Não tagueia: getter/setter, CRUD simples, método técnico óbvio (formatação, cast, log), qualquer coisa auto-explicativa pelo nome.

Uso

"Arquivo" é qualquer classe — usecase, service, repository, o que for. Nenhum tratamento especial por tipo, qualquer classe serve de ponto de partida.

# a partir de um arquivo (busca por nome, segue classes referenciadas via PSR-4)
vendor/bin/teller scan --file=CalculadoraJuros

# a partir de uma classe (FQCN)
vendor/bin/teller scan --class=App\\Services\\CalculadoraJuros

# codebase inteira sob um path (recomendado pra usar o command palette do viewer)
vendor/bin/teller scan --all --path=app

# limpar o banco local (storage/data.sqlite, nunca precisa ser commitado)
vendor/bin/teller clear

# exportar relatório HTML standalone do subgrafo de um arquivo
vendor/bin/teller export --file=CalculadoraJuros --out=relatorio.html

Por padrão a raiz do projeto escaneado é o diretório de onde você roda o comando — use --project-root=/caminho se precisar rodar de outro lugar.

Viewer

O viewer é estático — sem API, o browser lê o .sqlite direto via sql.js (WebAssembly). Qualquer servidor HTTP simples serve:

vendor/bin/teller scan --all --path=app
php -S localhost:8090 -t vendor/valb-mig/teller/public

Ou via Docker, se preferir não instalar PHP local pra isso:

docker run --rm -p 8090:8090 \
  -v "$(pwd)/vendor/valb-mig/teller/public:/var/www/html:ro" \
  -v "$(pwd)/vendor/valb-mig/teller/storage/data.sqlite:/var/www/html/data.sqlite:ro" \
  php:8.3-cli-alpine php -S 0.0.0.0:8090 -t /var/www/html

Abre em http://localhost:8090. Depois de rodar scan de novo (ou clear + scan), recarregue a página — o browser relê o arquivo a cada load.

Tela limpa, sem toolbar fixa: Ctrl/Cmd + K abre o command palette pra buscar arquivo/classe por nome, título @teller ou FQCN, ranqueado por quantas referências @teller tem na árvore de chamada — escolher um já carrega o trace (BFS client-side, sem bater no banco de novo).

Cada arquivo vira um card (nome do arquivo + título/descrição da classe — clicar no header abre no VSCode). Dentro, uma timeline vertical lista métodos e comentários soltos na ordem em que aparecem no arquivo: métodos mágicos (__construct, __invoke) em verde, métodos comuns em cinza, comentário solto em triângulo amarelo. Documentados aparecem destacados (título+descrição, cyan), sem tag aparecem apagados (só o nome). Cada item tem dropdown pra colapsar e abre um modal com mais detalhe (namespace, linha, fqcn, botão abrir no VSCode) ao clicar.

Cards ficam em colunas por profundidade (BFS a partir do arquivo escolhido — quem ele chama fica na coluna seguinte), com setas conectando o método/comentário exato que chama ao método exato chamado do outro lado, numeradas na ordem real do código-fonte. Canto inferior direito: zoom in/out/fit + exportar PNG (captura tudo via html2canvas).

Chamadas alternativas (if/elseif/else no código) convergem num losango de decisão entre origem e destinos — a origem aponta só pro losango (rótulo = número do passo, ex. "4"), o losango se abre pra cada braço (rótulo = letra, "a"/"b"). Hover na origem acende todos os caminhos; hover num destino acende só o caminho dele.

Sidebar esquerda (clique no no canto pra abrir/fechar) narra o trace inteiro em texto, na mesma ordem/numeração das setas, traduzindo estrutura de controle pra português ("Se X: chama Y — senão chama Z"). Botão Copiar pro Claude copia essa narrativa pra área de transferência — cola numa conversa com IA pra pedir revisão, dúvida sobre a regra, etc.

Limitações da v1

  • Chamada via dependência injetada resolve pro tipo declarado (constructor promoted property ou propriedade tipada), não pelo container real — se a interface tiver várias implementações, o grafo não sabe qual roda em runtime.
  • Chamadas a parent:: não são seguidas.
  • Comentário @teller solto só é achado em statements de primeiro nível do corpo do método — não desce em bloco aninhado (se dentro de outro if, por exemplo).
  • Narrativa da sidebar só entende if/elseif/elsetry/catch, switch, loop não viram texto especial (aparecem como chamada linear normal).
  • "Abrir no código" usa o esquema vscode://file/ — preso ao VSCode.
  • Só resolve classes dentro dos diretórios PSR-4 do próprio projeto; chamadas pra vendor/framework não viram edge.

Licença

MIT — veja LICENSE.