PHPGraph MCP
🐘 🏠 🍎 🪟 🐧 - Grafo de conhecimento de qualquer base de código PHP construído por análise estática (sem LLM, sem rede): chamadas, rotas, barramentos de mensagens, camadas DDD e contratos de microsserviços, com níveis de confiança, além de análise de impacto e esboços de funcionalidades para agentes de codificação.
Documentação
phpgraph
Um grafo de conhecimento do seu código PHP, para agentes de IA de codificação.
Análise estática servida via MCP para Sherpa, Claude Code, Codex, Cursor e qualquer cliente MCP.
Sem LLM, sem rede, sem embeddings: fatos determinísticos, cada um com um nível de confiança.
English · Français
Por que phpgraph?
Um agente de IA inserido em um projeto PHP desconhecido lê arquivos um a um e procura por nomes. Ele perde o que mais importa em um código real: quem chama o quê, qual handler recebe uma mensagem, qual controller atende uma rota, o que quebra se um método mudar e qual camada depende de qual.
O phpgraph analisa o projeto inteiro uma vez (com nikic/php-parser) e responde a essas perguntas a partir de um grafo:
- Fatos determinísticos, não suposições. Cada relação é
EXTRACTED(lida no código),INFERRED(resolvida a partir de tipos declarados) ouAMBIGUOUS(uma suposição, rotulada como tal). - Ele diz o que não sabe. Chamadas não resolvidas,
vendor/ausentes, mensagens sem handlers: as lacunas são relatadas, para que o agente saiba quando ler o código ele mesmo. - Feito para DDD e arquitetura hexagonal, depois microsserviços, depois PHP em geral: Symfony, Laravel, Ecotone, PrestaShop, WordPress ou PHP puro.
- Privado por design. Seu código nunca sai da sua máquina, e o phpgraph nunca o executa: ele apenas o analisa.
Recursos
- Grafo de chamadas com inferência de tipos: tipos de retorno, chamadas encadeadas (
$a->b()->c()), variáveis locais, docblocks (@return,@var) e assinaturas das suas dependênciasvendor/, para que as cadeias passem por APIs do Doctrine, Symfony ou Laravel. - Arquitetura: camadas DDD e hexagonais, contextos delimitados, regras de camada verificadas no CI (
phpgraph check) e análise de impacto (impact_of): o que depende de uma classe ou método e quais testes executar. - Mensagens e eventos: remetente → mensagem → handler para Symfony Messenger, jobs e eventos do Laravel, Ecotone, CQRS do PrestaShop, hooks do WordPress, eventos de domínio e seus próprios barramentos, a partir de atributos, configuração de serviços (YAML, XML, PHP) ou formato do código.
- HTTP: rotas (atributos do Symfony, arquivos de rota YAML e PHP, recursos da API Platform, arquivos de rota do Laravel, controllers nomeados por id de serviço do container) vinculadas a controllers, e chamadas HTTP vinculadas às rotas que alcançam. Uma rota cuja classe de controller não existe nem no projeto nem em
vendor/é relatada com seu arquivo de rota. - Injeção de dependência: o que a configuração do container Symfony injeta além dos tipos de construtor, cada serviço de uma tag (
tagged_iterator,#[AutowireIterator]) ou um serviço nomeado por id, a partir de configuração PHP, YAML ou XML, incluindo helpers chamados com argumentos literais (Wiring::wire($services, 'sales', ...), avaliados, nunca executados): um validador é vinculado às regras que recebe. Uma regra que declara o que aceita (supports(object $input): bool { return $input instanceof CreateEstimateQuery; }, tambémcanHandle,accepts…) é mantida fora dos casos de uso que passam outra entrada para o pipeline compartilhado, incluindo as classes que as propriedades da entrada contêm. Uma entrada tipada por uma interface é qualquer classe do projeto que a implementa e os tipos de parâmetro para os quais é passada (QueryInterface $querydado asetUp(CreateEstimateQueryInterface $query): umCreateEstimateQuery); uma proteção que não consegue ler, ou uma regra que roda apenas para algumas dessas classes, é mantida, marcada comosupports() not resolved. - Microsserviços: um espaço de ids por serviço e links entre serviços por meio de seus contratos: classes de mensagem compartilhadas, chaves de roteamento, rotas HTTP.
- Esquemas de API: as operações de documentos OpenAPI (Swagger) vinculadas às rotas que as implementam ou, para um documento de outra API, às chamadas HTTP do seu cliente; os rpcs de serviços protobuf vinculados às classes que implementam sua interface gerada. O que está documentado e não implementado, e as rotas que nenhum documento descreve, são relatados.
impactlista as operações por trás de uma mudança (o contrato no qual seus clientes dependem),outlineaquelas que um recurso atende. - Servidor MCP de configuração zero: construído no primeiro uso, reconstruído incrementalmente quando o código muda (cerca de 3 s por edição em um projeto de 8.400 arquivos).
Início rápido
1. Instale. Um único executável, sem necessidade de PHP (Linux e macOS em x86_64 e ARM, Windows em x86_64):
curl -fsSL https://raw.githubusercontent.com/hbenabdallah/phpgraph/main/install.sh | sh # latest release
curl -fsSL https://raw.githubusercontent.com/hbenabdallah/phpgraph/main/install.sh | sh -s -- 0.2.0 # a given release
curl -fsSL https://raw.githubusercontent.com/hbenabdallah/phpgraph/main/install.sh | sh -s -- dev-main # the main branch
irm https://raw.githubusercontent.com/hbenabdallah/phpgraph/main/install.ps1 | iex # Windows
$env:PHPGRAPH_VERSION = "dev-main"; irm https://raw.githubusercontent.com/hbenabdallah/phpgraph/main/install.ps1 | iex
O script baixa o binário, verifica seu SHA-256 e o coloca em ~/.local/bin (Windows: %LOCALAPPDATA%\Programs\phpgraph, adicionado ao PATH); PHPGRAPH_INSTALL_DIR muda o diretório. Execute novamente para atualizar. dev-main é reconstruído a cada push para main (o pré-lançamento edge). O binário é um runtime PHP estático com phpgraph dentro, construído com static-php-cli.
Com PHP 8.2+ instalado, ou com Docker:
composer require --dev hbenabdallah/phpgraph # or: composer global require hbenabdallah/phpgraph
curl -LO https://github.com/hbenabdallah/phpgraph/releases/latest/download/phpgraph.phar
docker pull ghcr.io/hbenabdallah/phpgraph # nothing else to install
2. Conecte seu agente. mcp-config imprime uma configuração pronta para colar com os caminhos preenchidos (vendor/bin/phpgraph quando instalado com Composer):
phpgraph mcp-config sherpa # Sherpa: ~/.config/sherpa/mcp.json, one declaration for every project
phpgraph mcp-config claude # Claude Code: a `claude mcp add` command, or .mcp.json
phpgraph mcp-config codex # Codex: ~/.codex/config.toml
phpgraph mcp-config cursor # Cursor: .cursor/mcp.json, shareable with the team
phpgraph mcp-config json # any other MCP client over stdio
Com Sherpa, um agente de codificação de terminal, uma declaração em ~/.config/sherpa/mcp.json atende a todos os projetos: o Sherpa inicia seus servidores MCP a partir do projeto em que executa.
{
"mcpServers": {
"phpgraph": { "command": "/home/you/.local/bin/phpgraph", "args": ["serve"] }
}
}
Com Claude Code (caminhos absolutos, como mcp-config os imprime):
claude mcp add phpgraph -- /home/you/.local/bin/phpgraph serve /path/to/project
3. Pergunte ao seu agente algo como "Me dê uma visão geral deste projeto", "Como um pedido é feito?" ou "O que quebra se eu mudar OrderRepository::save?". O grafo é construído na primeira chamada e depois mantido atualizado.
Ferramentas MCP
| Ferramenta | Use para |
|---|---|
overview | Comece aqui: stack do Composer, árvore de namespaces com camadas, contextos delimitados, violações de regras de camada, mensagens, rotas HTTP (cada uma com método, caminho, controller e arquivo até 30, senão agrupadas por prefixo de caminho), serviços e o que o grafo não consegue ver. |
query_graph | Encontre o código sobre um tópico quando você não sabe os nomes das classes, pelas palavras de seus nomes (validado encontra Validator), com as classes do recurso ao redor das que ele nomeia. Uma pergunta sobre rotas ("rotas de cursos mooc", "GET /courses") retorna as rotas correspondentes e seus controllers. As famílias de classes ao redor da resposta são contadas por pasta (ContextRuleInterface: 34 em …), listas injetadas uma vez (ContextValidator recebe 34 ContextRuleInterface). |
outline | Antes de explicar um recurso: seu contorno estrutural, suficiente para ler apenas os poucos arquivos com lógica não óbvia. As classes que o compõem, por namespace e camada, com a primeira frase do docblock, assinaturas públicas, constantes e casos de enum; as interfaces e classes base ao redor com suas implementações por módulo (0 declarado); as rotas que chegam até ele, cadeia por cadeia, através dos closures que executa; comportamento lido nos corpos (throws protegidos, retornos antecipados, ramificações em constantes, limites de loop, estado comparado antes e depois); a fiação do container com ids de serviço; seus usuários, testes contados por módulo, o que nenhum teste toca e o que nada usa. Cerca de 15 KB; uma lista cortada nomeia a seção que tem tudo (section behaviour, 1 a 9 KB). |
get_node | Leia uma classe, método, rota ou canal com todas as suas relações; testes que o usam são contados por módulo. |
get_neighbors | Veja o que usa um nó (in) ou do que ele depende (out). |
impact_of | Antes de uma mudança: toda classe que depende de uma classe ou método, mais próxima primeiro, com as linhas das chamadas; chamadores através da interface que implementa; código que depende do estado que ele escreve; as rotas que o alcançam, com cada cadeia; e os testes a executar, encontrados através de helpers de teste. Ele sinaliza as classes que nenhum teste toca e aquelas que nada na aplicação usa. |
shortest_path | Veja como duas partes do código estão conectadas. |
god_nodes | Encontre os hubs dos quais a maior parte do código depende. |
O servidor diz ao agente para chamar overview primeiro. Um caminho, como um agente o vê, em php-ddd-example:
Shortest path (4 hops):
PUT /courses/{id} --handled_by--> CoursesPutController::__invoke()
--dispatches--> CreateCourseCommand --handled_by--> CreateCourseCommandHandler::__invoke()
--calls--> CourseCreator::__invoke()
Linha de comando
phpgraph build [path] # phpgraph-out/graph.json and GRAPH_REPORT.md
phpgraph overview # stack, structure and gaps
phpgraph impact "OrderRepository::save" # what depends on it, and the tests to run (--all, --section tests, --format json)
phpgraph check # layer rules, for CI: exit code 1 on a new violation
phpgraph explain "PlaceOrderHandler" # a node and its relations (-d in|out)
phpgraph path "StockChecker" "DbalOrderRepository"
phpgraph query "how is stock checked"
phpgraph outline "notification validation" # the outline of a feature (--section flow, --format json)
phpgraph serve [path] # the MCP server, over stdio
phpgraph mcp-config claude|codex|cursor|json [--docker image]
Opções de build: -e para excluir caminhos (repetível), --no-vendor para não ler dependências, --no-cache para analisar todos os arquivos novamente. vendor, node_modules, var, .git e os arquivos ignorados pelos próprios arquivos .gitignore do projeto são ignorados; um repositório git acima do projeto nunca aplica suas regras, então um projeto copiado para um diretório ignorado ainda é lido. Quando o diretório contém arquivos PHP, mas todos são excluídos, build falha e diz isso, e toda resposta MCP começa com esse aviso em vez de servir um grafo vazio. A memória é ilimitada por padrão; PHPGRAPH_MEMORY_LIMIT=2G define um limite. Com PHP instalado, opcache e seu JIT tornam as construções cerca de 20% mais rápidas: php -d opcache.enable_cli=1 -d opcache.jit=tracing -d opcache.jit_buffer_size=128M vendor/bin/phpgraph build (a imagem Docker e os comandos impressos por mcp-config já os usam).
O que o grafo contém
| Relação | De → para |
|---|---|
defines, imports | arquivo → classe, declaração use |
extends, implements, uses_trait | classe → pai, interface, trait |
has_method, overrides | classe → método, método → o método que ele sobrescreve |
instantiates, references | new Foo(); tipos de parâmetro, retorno e propriedade, catch, instanceof, constantes, atributos |
calls | método → método resolvido, com as linhas dos locais de chamada; um método que executa um closure que recebe ($apply(...)) chama o que o closure chama, INFERRED, via closure of <the method writing it> |
dispatches, handled_by | remetente → mensagem ou canal → handler; rota → controller; classe testada por um método supports() → essa estratégia |
contract | uma classe de mensagem enviada por um serviço → a mesma classe tratada por outro |
requests | chamada HTTP → a rota que ela alcança, no mesmo serviço ou em outro, ou a operação do documento OpenAPI de outra API |
implemented_by | operação de esquema de API (operation:) → a rota com seu método e caminho (OpenAPI), ou o método de uma classe que implementa o serviço gerado (protobuf) |
receives | serviço → cada serviço que a configuração do container injeta nele (uma tag, um id, o serviço decorado); via diz qual: service sales.pipeline para um de vários serviços de uma classe |
reads_state_of | método → um método da mesma classe que muda uma propriedade que ele lê, fora do construtor (hasErrors() → add()), INFERRED |
Cada relação carrega um nível de confiança:
| Nível | Significado |
|---|---|
EXTRACTED | Lido no código de um arquivo, ou na configuração (atributo, tag de serviço, arquivo de rota). |
INFERRED | Resolvido entre arquivos a partir de tipos declarados: parâmetros, propriedades, $this, herança, tipos de retorno, formato do código. |
AMBIGUOUS | Uma suposição, por exemplo, o único método do projeto com esse nome, chamado em um receptor de tipo desconhecido. |
Frameworks e convenções reconhecidos
| | Handlers e listeners | Mensagens enviadas | HTTP | |---|---|---|---| | **Symfony** | `#[AsMessageHandler]`, `#[AsEventListener]`, tags `messenger.message_handler` e `kernel.event_listener` (YAML, XML, PHP, `_instanceof`), `getSubscribedEvents()`, `addListener()` | `MessageBusInterface`, `EventDispatcherInterface`, eventos nomeados | `#[Route]` (caminhos em constantes de classe também), arquivos de roteamento YAML e PHP, controllers nomeados por id de serviço; API Platform `#[ApiResource]` e atributos de operação, tratados por seu provider, processor ou controller | | **Laravel** | `$listen`, `Event::listen()`, jobs (`ShouldQueue`, `Dispatchable`) | `event()`, `dispatch()`, facades, `Job::dispatch()` | `Route::get/post/…`, `match`, `resource`, grupos com `prefix`, `routes/api.php`; cliente `Http::` | | **Ecotone** | `#[CommandHandler]`, `#[EventHandler]`, `#[QueryHandler]`, chaves de roteamento | `CommandBus`, `EventBus`, `DistributedBus`, `sendWithRouting()` | | | **PrestaShop** | `#[AsCommandHandler]`, `#[AsQueryHandler]` | `CommandBusInterface::handle()` | Roteamento YAML | | **WordPress** | `add_action()`, `add_filter()` | `do_action()`, `apply_filters()` | | | **Seu próprio código** | atributos e tags de handler reconhecidos por nome, `__invoke`/`handle(Message $m)`, classes `*Handler` | `*Bus`, `*Dispatcher`, tipos `*Publisher`, `recordThat()` de agregados, seus wrappers de barramento | Guzzle, Symfony HttpClient, PSR-18 |Arquitetura: camadas, contextos delimitados, regras no CI
A camada de uma classe é lida a partir do seu namespace: o primeiro segmento explícito (Domain, Application, Infrastructure, UI, Presentation, Port, Adapter, UseCase), ou então o último genérico (Model, Entity, Controller, Http, Persistence…). O contexto delimitado é lido antes da camada (CodelyTv\Mooc\Courses\Domain) ou depois dela quando o projeto coloca a camada primeiro (Core\Domain\Product).
phpgraph check aplica as regras padrão de uma arquitetura em camadas ao código de aplicação: domain não deve depender de application, infrastructure ou interface; application e port não devem depender de infrastructure ou interface. Em um projeto que já possui violações:
phpgraph check --generate-baseline # accept today's violations in phpgraph-baseline.json
phpgraph check # fail on new ones only
Microsserviços
Um repositório contendo dois ou mais aplicativos Composer independentes é dividido em serviços. Pacotes de um monorepo permanecem em seu projeto: um pacote que o autoload raiz carrega, declara como repositório de caminho ou replaces é uma parte, não um serviço; uma biblioteca que os serviços exigem (um repositório de caminho, ou seu nome de pacote) é seu código comum. Os ids então carregam seu serviço, billing@App\Domain\Order, e um nome nunca resolve para outro serviço: os serviços se encontram por meio de contratos.
- a mesma classe de mensagem enviada por um serviço e tratada por outro:
contract; - uma chave de roteamento (
sendWithRouting('order.place', …),#[CommandHandler('ticket.create')], um evento nomeado): um nóchannel:compartilhado por todos os serviços; - uma chamada HTTP para uma rota de outro serviço:
requests. Uma chamada cujo caminho corresponde a uma rota declarada apenas para outros métodos HTTP é relatada: ela falharia como escrita; - um esquema de API: os nós
operation:de um documento OpenAPI (GET /api/hotels/{hotel}) ou de um serviço protobuf (CinemaService.Reserve),implemented_byas rotas ou classes que os servem. Um documento na raiz do repositório descreve todos os serviços (um gateway de API), um em um serviço apenas aquele serviço.
Um phpgraph.yaml opcional nomeia os serviços quando a detecção não se ajusta:
services:
- services/billing
- services/shipping
Como funciona
- Cada arquivo PHP é analisado em fatos: declarações, chamadas com a tipagem de expressão de seu receptor, envios e handlers de mensagens, rotas, chamadas HTTP.
- O builder resolve esses fatos entre arquivos: tipos por herança e tipos de retorno, assinaturas
vendor/lidas sob demanda dos metadados do Composer (analisados, nunca executados), definições de serviço da configuração do contêiner. - O grafo é salvo como
phpgraph-out/graph.json, comGRAPH_REPORT.mdpara humanos.
As reconstruções são incrementais: a extração de um arquivo é armazenada em cache por conteúdo, e o servidor MCP re-resolve apenas as chamadas de arquivos alterados quando nenhuma declaração mudou em qualquer lugar. O resultado é idêntico a uma construção completa.
Medido em projetos reais
phpgraph é desenvolvido contra um corpus de projetos de código aberto fixados em um commit, e cada mudança é julgada por seus números (composer corpus:measure). Apenas código de aplicação; receptor tipado é a parcela de chamadas de método cujo tipo de receptor o phpgraph pôde determinar.
| Projeto | Tipo | Arquivos PHP | Build | Receptor tipado |
|---|---|---|---|---|
| php-ddd-example | DDD, CQRS | 304 | 0,4 s | 98,9 % |
| Sylius | Symfony e-commerce | 4.946 | 6,5 s | 96,9 % |
| Akeneo PIM | hexagonal, contextos delimitados | 8.416 | 10,3 s | 90,4 % |
| PrestaShop | CQRS e legado | 7.850 | 10,0 s | 96,1 % |
| Ecotone quickstart | 47 serviços, mensageria | 583 | 0,6 s | 94,4 % |
| BookStack | Laravel | 1.513 | 2,8 s | 96,2 % |
| WordPress | sem framework, hooks | 1.899 | 5,3 s | 93,6 % |
Quatro projetos de microsserviços completam o corpus: dois serviços Laravel sobre RabbitMQ, ligados por 4 contratos de mensagem; quatro serviços Laravel sobre HTTP, onde o phpgraph encontrou um POST enviado a uma rota apenas GET; reserva de ingressos do Spiral, serviços chamando uns aos outros por serviços gRPC declarados em arquivos .proto; e StayHub, sete serviços Laravel atrás de um gateway de API descrito por um arquivo OpenAPI. Os dois últimos medem esquemas de API como contratos: todas as 45 operações do documento do StayHub e todos os 14 rpcs da reserva de ingressos estão ligados ao que os implementa. Zero falhas de análise em cerca de 26.600 arquivos. Os detalhes por projeto, incluindo mensagens, rotas e serviços, estão em corpus/RESULTS.md.
Com um agente
Medido em um projeto privado Symfony/DDD de 4.800 arquivos, 3 execuções por configuração, o mesmo agente (Sherpa) com e sem phpgraph 1.0.2:
| Tipo de pergunta | Com phpgraph | Qualidade |
|---|---|---|
Impacto ou refatoração (impact_of) | −42 % tokens, −50 % tempo, −59 % chamadas de ferramenta | igual ou melhor |
Explicando um recurso (outline) | −25 % tokens, −34 % tempo, −49 % chamadas de ferramenta; −31 % tokens com o esboço padrão | igual |
Os intervalos das execuções com e sem phpgraph não se sobrepõem.
Comparado com outros grafos de código
Sylius (o commit do corpus), outubro de 2026, sem qualquer LLM: phpgraph 1.0.2, codebase-memory-mcp 0.11.0 e graphify 0.9.76 (--code-only), na mesma máquina, em Docker. As chamadas foram comparadas site por site (arquivo, linha, método), e amostras das diferenças foram verificadas no código.
| phpgraph | codebase-memory-mcp | graphify | |
|---|---|---|---|
| Build | 10 s | 17 s | 32 s |
| Sites de chamada ligados a um método do projeto | 22.870 | 32.274 | 4.005 |
| dos quais resolvidos a partir de tipos | 22.747 | 4.314 | — |
Chamadores de OrderItemQuantityModifierInterface::modify() encontrados (17 no código) | 17, nenhum falso | 11, 2 falsos | 0, 1 falso |
| Handlers do Messenger ligados à sua mensagem | 206 | 0 | 0 |
| Injeções de contêiner, rotas | 1.360, 188 | 0, 1 (falso) | 0, 0 |
- codebase-memory-mcp concorda com phpgraph em 99,7 % das chamadas que resolve a partir de tipos. O resto de suas arestas é adivinhado a partir do nome do método (confiança 0,04 a 0,44): onde ambos ligam a mesma chamada, eles discordam em 9.400 sites, e suas 11.300 arestas extras são principalmente chamadas para
vendor/anexadas a um método do projeto com o mesmo nome (19 de 20 verificados). - graphify liga
$this->other->foo()afoo()da classe atual: correto para$this->foo(), errado para as 10 de 10 diferenças verificadas. - O que eles têm e o phpgraph não tem: outras linguagens, busca de texto completo e vetorial, consultas Cypher (codebase-memory-mcp), uma visualização interativa e documentos no grafo (graphify).
FAQ
O phpgraph envia meu código para algum lugar? Não. Ele roda localmente, não faz nenhuma chamada de rede e não usa LLM.
Ele executa meu código? Não. Ele analisa arquivos PHP e lê configuração JSON, YAML e XML. Até autoload_classmap.php e configuradores de serviço PHP são analisados, não incluídos.
Quais versões de PHP ele pode analisar? Qualquer coisa que nikic/php-parser 5 analise: sintaxe PHP 7 e 8. O phpgraph em si precisa de PHP 8.2 ou mais novo.
Com quais agentes ele funciona? Qualquer cliente MCP sobre stdio: Sherpa, Claude Code, Codex, Cursor, Claude Desktop, e outros. mcp-config imprime a configuração para os comuns.
Quão grande um projeto ele pode lidar? O maior projeto do corpus, Akeneo, tem 8.400 arquivos: cerca de 10 s e 500 MB para uma construção completa, cerca de 3 s para uma reconstrução após uma edição.
Limitações
- Apenas PHP é lido:
overviewnomeia os outros idiomas de um repositório misto (suas chamadas para a parte PHP não são vinculadas), e as instruções do MCP dizem imediatamente quando um projeto não tem PHP algum. - Chamadas para funções globais não são arestas do grafo, embora o que uma função retorna tipa as chamadas sobre ela (
now()->addDay(),response()->json()); chamadas dinâmicas ($this->$name(),__call) são resolvidas apenas através dos métodos que uma classe documenta (@method,@mixin). - Genéricos são lidos de docblocks (
@template,@extends Repository<Order>,@return Collection<int, Item>,ScalarNodeDefinition<$this>), no projeto e emvendor/: um template é vinculado pelo tipo do receptor e pelos pais que o repassam, e umforeachpercorre os elementos de qualquer coleção tipada. Uma união (Response|ResponseFactory, documentada ou declarada) é chamada no membro que declara o método, e somente quando um membro o faz; um closure passado a um método recebe os parâmetros que o método documenta passar (@param callable($this): mixed, owhen(fn ($query) => ...)do Laravel);@use HasFactory<HotelFactory>em um uso de trait vincula seus templates. Um template de um método é vinculado pelos argumentos de cada chamada: a classe que um argumento nomeia (getRepository(Order::class)com@param class-string<T>), o tipo de um valor (@param T $value), o que um closure retorna (@param callable(): T, suas declarações de retorno ou tipo nativo).@phpstan-returne@psalm-returnsão lidos antes de@return; uma condição que os argumentos não decidem (TAsync is false ? Response : PromiseInterface) dá a união de seus ramos. Propriedades mágicas documentadas com@propertye@property-readcarregam cadeias adiante; em um modelo Eloquent, também o fazem os casts ('paid_at' => 'datetime'é um Carbon, um enum, oTGetde uma classe de cast), os timestamps e as relações lidas como propriedades ($page->book, o quebook()->getResults()retorna). Um método que não declara tipo de retorno retorna o que o método que ele sobrescreve declara (@inheritDoc). Condições sobre um tipo ($id is array ? Collection : Model) não são avaliadas, e um método de facade documentado comomixed(DB::transaction()) permanece desconhecido. - Cadeias param em métodos mágicos e em dependências sem um tipo de retorno utilizável. As próprias classes do PHP (
DateTime,ArrayObject,SplObjectStorage,PDO...) são conhecidas a partir de assinaturas geradas uma vez (tools/internal-classes.php); aquelas das extensõesintlemysqlinão são. - Sem um
vendor/instalado, cadeias de chamadas param na primeira dependência. - Serviços construídos em tempo de execução (passes de compilador, extensões de bundle, ids computados a partir de valores conhecidos apenas em tempo de execução), rotas XML, recursos da API Platform declarados em XML ou YAML, e esquemas de API diferentes de OpenAPI e protobuf (GraphQL, AsyncAPI) não são lidos; um caminho OpenAPI reescrito por um gateway não é seguido; injeções que não podem ser vinculadas são listadas por
overview. - Dois serviços que declaram uma classe de mensagem com o mesmo nome são considerados como compartilhando-a.
Contribuindo
Issues e pull requests são bem-vindos. Veja CONTRIBUTING.md: composer check deve passar (php-cs-fixer, PHPStan nível máximo, PHPUnit), e mudanças na análise são julgadas no corpus. Sem PHP instalado, bin/dev composer check executa tudo em Docker.
O que permanece estável entre versões (comandos, ferramentas MCP, o formato graph.json) está listado em docs/STABILITY.md; mudanças estão no changelog.
Lançamentos são publicados ao enviar uma tag de versão: o workflow de lançamento constrói o PHAR, os executáveis para Linux, macOS e Windows (tools/build-binary.sh), e a imagem Docker ghcr.io/hbenabdallah/phpgraph.
Licença
phpgraph é de código-fonte disponível sob a PolyForm Shield License 1.0.0: você pode usar, modificar e redistribuir, inclusive em uma empresa, para qualquer propósito que não concorra com o phpgraph. Fornecer um produto concorrente a partir deste código não é permitido. Esta não é uma licença de código aberto aprovada pela OSI.