PHPGraph MCP

🐘 🏠 🍎 🪟 🐧 - Grafo de conocimiento de cualquier base de código PHP construido mediante análisis estático (sin LLM, sin red): llamadas, rutas, buses de mensajes, capas DDD y contratos de microservicios, con niveles de confianza, además de análisis de impacto y esquemas de funcionalidades para agentes de codificación.

Documentación

phpgraph turns a PHP codebase into a knowledge graph of domains, handlers, repositories, controllers and adapters, served to AI agents through a CLI and an MCP server

phpgraph

Un grafo de conocimiento de tu código PHP, para agentes de IA de codificación.
Análisis estático servido a través de MCP para Sherpa, Claude Code, Codex, Cursor y cualquier cliente MCP.
Sin LLM, sin red, sin embeddings: hechos deterministas, cada uno con un nivel de confianza.

CI Packagist version Downloads PHP 8.2+ License: PolyForm Shield 1.0.0

English · Français


¿Por qué phpgraph?

Un agente de IA insertado en un proyecto PHP desconocido lee archivos uno por uno y busca nombres con grep. Se pierde lo que más importa en un código real: quién llama a qué, qué handler recibe un mensaje, qué controlador sirve una ruta, qué se rompe si cambia un método y qué capa depende de cuál.

phpgraph analiza todo el proyecto una vez (con nikic/php-parser) y responde esas preguntas desde un grafo:

  • Hechos deterministas, no suposiciones. Cada relación es EXTRACTED (leída en el código), INFERRED (resuelta a partir de tipos declarados) o AMBIGUOUS (una suposición, etiquetada como tal).
  • Dice lo que no sabe. Llamadas no resueltas, vendor/ faltantes, mensajes sin handlers: los vacíos se reportan, para que el agente sepa cuándo leer el código él mismo.
  • Diseñado para DDD y arquitectura hexagonal, luego microservicios, luego PHP en general: Symfony, Laravel, Ecotone, PrestaShop, WordPress o PHP plano.
  • Privado por diseño. Tu código nunca sale de tu máquina, y phpgraph nunca lo ejecuta: solo lo analiza.

Características

  • Grafo de llamadas con inferencia de tipos: tipos de retorno, llamadas encadenadas ($a->b()->c()), variables locales, docblocks (@return, @var) y firmas de tus dependencias vendor/, para que las cadenas atraviesen las APIs de Doctrine, Symfony o Laravel.
  • Arquitectura: capas DDD y hexagonales, contextos delimitados, reglas de capa verificadas en CI (phpgraph check) y análisis de impacto (impact_of): qué depende de una clase o método, y qué pruebas ejecutar.
  • Mensajes y eventos: emisor → mensaje → handler para Symfony Messenger, trabajos y eventos de Laravel, Ecotone, CQRS de PrestaShop, hooks de WordPress, eventos de dominio y tus propios buses, desde atributos, configuración de servicios (YAML, XML, PHP) o la forma del código.
  • HTTP: rutas (atributos de Symfony, archivos de enrutamiento YAML y PHP, recursos de API Platform, archivos de rutas de Laravel, controladores nombrados por id de servicio del contenedor) vinculadas a controladores, y llamadas HTTP vinculadas a las rutas que alcanzan. Una ruta cuya clase de controlador no existe ni en el proyecto ni en vendor/ se reporta con su archivo de enrutamiento.
  • Inyección de dependencias: lo que la configuración del contenedor de Symfony inyecta más allá de los tipos de constructor, cada servicio de una etiqueta (tagged_iterator, #[AutowireIterator]) o un servicio nombrado por id, desde configuración PHP, YAML o XML, incluidos helpers llamados con argumentos literales (Wiring::wire($services, 'sales', ...), evaluados, nunca ejecutados): un validador se vincula a las reglas que recibe. Una regla que declara lo que acepta (supports(object $input): bool { return $input instanceof CreateEstimateQuery; }, también canHandle, accepts…) se mantiene fuera de los casos de uso que pasan otra entrada al pipeline compartido, incluidas las clases que contienen las propiedades de la entrada. Una entrada tipada por una interfaz es cualquier clase del proyecto que la implemente y los tipos de parámetros a los que se pasa (QueryInterface $query dado a setUp(CreateEstimateQueryInterface $query): un CreateEstimateQuery); una guarda que no puede leer, o una regla que se ejecuta solo para algunas de estas clases, se mantiene, marcada como supports() not resolved.
  • Microservicios: un espacio de ids por servicio, y enlaces entre servicios a través de sus contratos: clases de mensajes compartidas, claves de enrutamiento, rutas HTTP.
  • Esquemas de API: las operaciones de documentos OpenAPI (Swagger) vinculadas a las rutas que las implementan, o, para un documento de otra API, a las llamadas HTTP de su cliente; los rpcs de servicios protobuf vinculados a las clases que implementan su interfaz generada. Lo documentado y no implementado, y las rutas que ningún documento describe, se reportan. impact lista las operaciones detrás de un cambio (el contrato del que dependen sus clientes), outline las que sirve una funcionalidad.
  • Servidor MCP de configuración cero: construido en el primer uso, reconstruido incrementalmente cuando el código cambia (aproximadamente 3 s por edición en un proyecto de 8,400 archivos).

Inicio rápido

1. Instala. Un único ejecutable, sin necesidad de PHP (Linux y macOS en x86_64 y ARM, Windows en 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

El script descarga el binario, verifica su SHA-256 y lo coloca en ~/.local/bin (Windows: %LOCALAPPDATA%\Programs\phpgraph, añadido al PATH); PHPGRAPH_INSTALL_DIR cambia el directorio. Ejecútalo de nuevo para actualizar. dev-main se reconstruye en cada push a main (la pre-release edge). El binario es un runtime PHP estático con phpgraph dentro, construido con static-php-cli.

Con PHP 8.2+ instalado, o con 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. Conecta tu agente. mcp-config imprime una configuración lista para pegar con las rutas completadas (vendor/bin/phpgraph cuando se instala con 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

Con Sherpa, un agente de codificación de terminal, una declaración en ~/.config/sherpa/mcp.json sirve para cada proyecto: Sherpa inicia sus servidores MCP desde el proyecto en el que se ejecuta.

{
  "mcpServers": {
    "phpgraph": { "command": "/home/you/.local/bin/phpgraph", "args": ["serve"] }
  }
}

Con Claude Code (rutas absolutas, como las imprime mcp-config):

claude mcp add phpgraph -- /home/you/.local/bin/phpgraph serve /path/to/project

3. Pregunta a tu agente algo como "Dame una visión general de este proyecto", "¿Cómo se realiza un pedido?" o "¿Qué se rompe si cambio OrderRepository::save?". El grafo se construye en la primera llamada y luego se mantiene actualizado.

Herramientas MCP

HerramientaÚsala para
overviewEmpieza aquí: stack de Composer, árbol de namespaces con capas, contextos delimitados, violaciones de reglas de capa, mensajes, rutas HTTP (cada una con método, ruta, controlador y archivo hasta 30, si no agrupadas por prefijo de ruta), servicios y lo que el grafo no puede ver.
query_graphEncuentra el código sobre un tema cuando no conoces los nombres de las clases, por las palabras de sus nombres (validado encuentra Validator), con las clases de la funcionalidad alrededor de las que nombra. Una pregunta sobre rutas ("rutas de cursos mooc", "GET /courses") devuelve las rutas coincidentes y sus controladores. Las familias de clases alrededor de la respuesta se cuentan por carpeta (ContextRuleInterface: 34 en …), las listas inyectadas una vez (ContextValidator recibe 34 ContextRuleInterface).
outlineAntes de explicar una funcionalidad: su esquema estructural, suficiente para leer solo los pocos archivos con lógica no obvia. Las clases que la componen, por namespace y capa, con la primera frase de su docblock, sus firmas públicas, constantes y casos de enum; las interfaces y clases base alrededor con sus implementaciones por módulo (0 indicado); las rutas que llegan a ella, cadena por cadena, a través de los closures que ejecuta; comportamiento leído en los cuerpos (throws protegidos, retornos tempranos, ramas en constantes, límites de bucles, estado comparado antes y después); el cableado del contenedor con ids de servicio; sus usuarios, pruebas contadas por módulo, lo que ninguna prueba toca y lo que nada usa. Aproximadamente 15 KB; una lista cortada nombra la sección que lo tiene todo (section behaviour, 1 a 9 KB).
get_nodeLee una clase, método, ruta o canal con todas sus relaciones; las pruebas que lo usan se cuentan por módulo.
get_neighborsVe qué usa un nodo (in) o de qué depende (out).
impact_ofAntes de un cambio: cada clase que depende de una clase o método, las más cercanas primero, con las líneas de las llamadas; llamadores a través de la interfaz que implementa; código que depende del estado que escribe; las rutas que lo alcanzan, con cada cadena; y las pruebas a ejecutar, encontradas a través de helpers de prueba. Marca las clases que ninguna prueba toca y las que nada en la aplicación usa.
shortest_pathVe cómo dos piezas de código están conectadas.
god_nodesEncuentra los hubs de los que depende la mayor parte del código.

El servidor le dice al agente que llame a overview primero. Una ruta, como la ve un agente, en php-ddd-example:

Shortest path (4 hops):
  PUT /courses/{id} --handled_by--> CoursesPutController::__invoke()
    --dispatches--> CreateCourseCommand --handled_by--> CreateCourseCommandHandler::__invoke()
    --calls--> CourseCreator::__invoke()

Línea de comandos

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]

Opciones de build: -e para excluir rutas (repetible), --no-vendor para no leer dependencias, --no-cache para analizar cada archivo de nuevo. vendor, node_modules, var, .git y los archivos ignorados por los archivos .gitignore del propio proyecto se omiten; un repositorio git por encima del proyecto nunca aplica sus reglas, por lo que un proyecto copiado en un directorio ignorado aún se lee. Cuando el directorio contiene archivos PHP pero todos están excluidos, build falla y lo dice, y cada respuesta MCP comienza con esa advertencia en lugar de servir un grafo vacío. La memoria es ilimitada por defecto; PHPGRAPH_MEMORY_LIMIT=2G establece un límite. Con PHP instalado, opcache y su JIT hacen las compilaciones aproximadamente un 20% más rápidas: php -d opcache.enable_cli=1 -d opcache.jit=tracing -d opcache.jit_buffer_size=128M vendor/bin/phpgraph build (la imagen Docker y los comandos impresos por mcp-config ya los usan).

Qué contiene el grafo

RelaciónDe → a
defines, importsarchivo → clase, sentencia use
extends, implements, uses_traitclase → padre, interfaz, trait
has_method, overridesclase → método, método → el método que sobrescribe
instantiates, referencesnew Foo(); tipos de parámetro, retorno y propiedad, catch, instanceof, constantes, atributos
callsmétodo → método resuelto, con las líneas de los sitios de llamada; un método que ejecuta un closure que recibe ($apply(...)) llama a lo que el closure llama, INFERRED, via closure of <the method writing it>
dispatches, handled_byemisor → mensaje o canal → handler; ruta → controlador; clase probada por un método supports() → esa estrategia
contractuna clase de mensaje enviada por un servicio → la misma clase manejada por otro
requestsllamada HTTP → la ruta que alcanza, en el mismo servicio u otro, o la operación del documento OpenAPI de otra API
implemented_byoperación de esquema de API (operation:) → la ruta con su método y ruta (OpenAPI), o el método de una clase que implementa el servicio generado (protobuf)
receivesservicio → cada servicio que la configuración del contenedor inyecta en él (una etiqueta, un id, el servicio decorado); via dice cuál: service sales.pipeline para uno de varios servicios de una clase
reads_state_ofmétodo → un método de la misma clase que cambia una propiedad que lee, fuera del constructor (hasErrors() → add()), INFERRED

Cada relación lleva un nivel de confianza:

NivelSignificado
EXTRACTEDLeído en el código de un archivo, o en la configuración (atributo, etiqueta de servicio, archivo de ruta).
INFERREDResuelto entre archivos a partir de tipos declarados: parámetros, propiedades, $this, herencia, tipos de retorno, forma del código.
AMBIGUOUSUna suposición, por ejemplo el único método del proyecto con ese nombre, llamado en un receptor de tipo desconocido.
Frameworks y convenciones reconocidos | | Handlers y listeners | Mensajes enviados | HTTP | |---|---|---|---| | **Symfony** | `#[AsMessageHandler]`, `#[AsEventListener]`, etiquetas `messenger.message_handler` y `kernel.event_listener` (YAML, XML, PHP, `_instanceof`), `getSubscribedEvents()`, `addListener()` | `MessageBusInterface`, `EventDispatcherInterface`, eventos nombrados | `#[Route]` (rutas en constantes de clase también), archivos de enrutado YAML y PHP, controladores nombrados por id de servicio; API Platform `#[ApiResource]` y atributos de operación, gestionados por su proveedor, procesador o controlador | | **Laravel** | `$listen`, `Event::listen()`, trabajos (`ShouldQueue`, `Dispatchable`) | `event()`, `dispatch()`, fachadas, `Job::dispatch()` | `Route::get/post/…`, `match`, `resource`, grupos con `prefix`, `routes/api.php`; cliente `Http::` | | **Ecotone** | `#[CommandHandler]`, `#[EventHandler]`, `#[QueryHandler]`, claves de enrutado | `CommandBus`, `EventBus`, `DistributedBus`, `sendWithRouting()` | | | **PrestaShop** | `#[AsCommandHandler]`, `#[AsQueryHandler]` | `CommandBusInterface::handle()` | Enrutado YAML | | **WordPress** | `add_action()`, `add_filter()` | `do_action()`, `apply_filters()` | | | **Tu propio código** | atributos de handler y etiquetas reconocidas por nombre, `__invoke`/`handle(Message $m)`, clases `*Handler` | `*Bus`, `*Dispatcher`, tipos `*Publisher`, `recordThat()` de agregados, tus envoltorios de bus | Guzzle, Symfony HttpClient, PSR-18 |
Arquitectura: capas, contextos delimitados, reglas en CI

La capa de una clase se lee desde su espacio de nombres: el primer segmento explícito (Domain, Application, Infrastructure, UI, Presentation, Port, Adapter, UseCase), o si no, el último genérico (Model, Entity, Controller, Http, Persistence…). El contexto delimitado se lee antes de la capa (CodelyTv\Mooc\Courses\Domain) o después de ella cuando el proyecto pone la capa primero (Core\Domain\Product).

phpgraph check aplica las reglas predeterminadas de una arquitectura en capas al código de aplicación: domain no debe depender de application, infrastructure o interface; application y port no deben depender de infrastructure o interface. En un proyecto que ya tiene violaciones:

phpgraph check --generate-baseline   # accept today's violations in phpgraph-baseline.json
phpgraph check                       # fail on new ones only
Microservicios

Un repositorio que contiene dos o más aplicaciones Composer independientes se divide en servicios. Los paquetes de un monorepo permanecen en su proyecto: un paquete que el autoload raíz carga, declara como repositorio de ruta o replaces es una parte, no un servicio; una biblioteca que los servicios requieren (un repositorio de ruta, o su nombre de paquete) es su código común. Los ids entonces llevan su servicio, billing@App\Domain\Order, y un nombre nunca se resuelve en otro servicio: los servicios se encuentran a través de contratos.

  • la misma clase de mensaje enviada por un servicio y gestionada por otro: contract;
  • una clave de enrutado (sendWithRouting('order.place', …), #[CommandHandler('ticket.create')], un evento nombrado): un nodo channel: compartido por todos los servicios;
  • una llamada HTTP a una ruta de otro servicio: requests. Una llamada cuya ruta coincide con una ruta declarada solo para otros métodos HTTP se informa: fallaría tal como está escrita;
  • un esquema de API: los nodos operation: de un documento OpenAPI (GET /api/hotels/{hotel}) o de un servicio protobuf (CinemaService.Reserve), implemented_by las rutas o clases que los sirven. Un documento en la raíz del repositorio describe cada servicio (una puerta de enlace de API), uno en un servicio solo ese servicio.

Un phpgraph.yaml opcional nombra los servicios cuando la detección no encaja:

services:
  - services/billing
  - services/shipping
Cómo funciona
  1. Cada archivo PHP se analiza en hechos: declaraciones, llamadas con el tipado de expresión que tipa su receptor, envíos y handlers de mensajes, rutas, llamadas HTTP.
  2. El constructor resuelve esos hechos entre archivos: tipos a través de herencia y tipos de retorno, firmas vendor/ leídas bajo demanda desde los metadatos de Composer (analizadas, nunca ejecutadas), definiciones de servicios desde la configuración del contenedor.
  3. El grafo se guarda como phpgraph-out/graph.json, con GRAPH_REPORT.md para humanos.

Las reconstrucciones son incrementales: la extracción de un archivo se almacena en caché por contenido, y el servidor MCP re-resuelve solo las llamadas de archivos cambiados cuando no ha cambiado ninguna declaración en ningún lugar. El resultado es idéntico a una construcción completa.

Medido en proyectos reales

phpgraph se desarrolla contra un corpus de proyectos de código abierto fijados a un commit, y cada cambio se juzga por sus números (composer corpus:measure). Solo código de aplicación; receptor tipado es la proporción de llamadas a métodos cuyo tipo de receptor phpgraph pudo determinar.

ProyectoTipoArchivos PHPConstrucciónReceptor tipado
php-ddd-exampleDDD, CQRS3040.4 s98.9 %
SyliusComercio electrónico Symfony4,9466.5 s96.9 %
Akeneo PIMhexagonal, contextos delimitados8,41610.3 s90.4 %
PrestaShopCQRS y legado7,85010.0 s96.1 %
Ecotone quickstart47 servicios, mensajería5830.6 s94.4 %
BookStackLaravel1,5132.8 s96.2 %
WordPresssin framework, hooks1,8995.3 s93.6 %

Cuatro proyectos de microservicios completan el corpus: dos servicios Laravel sobre RabbitMQ, vinculados por 4 contratos de mensajes; cuatro servicios Laravel sobre HTTP, donde phpgraph encontró un POST enviado a una ruta solo GET; la reserva de entradas de Spiral, servicios que se llaman entre sí a través de servicios gRPC declarados en archivos .proto; y StayHub, siete servicios Laravel detrás de una puerta de enlace de API descrita por un archivo OpenAPI. Los últimos dos miden esquemas de API como contratos: las 45 operaciones del documento de StayHub y los 14 rpcs de reserva de entradas están vinculados a lo que los implementa. Cero fallos de análisis en aproximadamente 26,600 archivos. Los detalles por proyecto, incluidos mensajes, rutas y servicios, están en corpus/RESULTS.md.

Con un agente

Medido en un proyecto privado Symfony/DDD de 4,800 archivos, 3 ejecuciones por configuración, el mismo agente (Sherpa) con y sin phpgraph 1.0.2:

Tipo de preguntaCon phpgraphCalidad
Impacto o refactorización (impact_of)−42 % tokens, −50 % tiempo, −59 % llamadas a herramientasigual o mejor
Explicar una característica (outline)−25 % tokens, −34 % tiempo, −49 % llamadas a herramientas; −31 % tokens con el esquema predeterminadoigual

Los rangos de las ejecuciones con y sin phpgraph no se superponen.

Comparado con otros grafos de código

Sylius (el commit del corpus), octubre de 2026, sin ningún LLM: phpgraph 1.0.2, codebase-memory-mcp 0.11.0 y graphify 0.9.76 (--code-only), en la misma máquina, en Docker. Las llamadas se compararon sitio por sitio (archivo, línea, método), y se verificaron muestras de las diferencias en el código.

phpgraphcodebase-memory-mcpgraphify
Construcción10 s17 s32 s
Sitios de llamada vinculados a un método de proyecto22,87032,2744,005
de los cuales resueltos desde tipos22,7474,314—
Llamadores de OrderItemQuantityModifierInterface::modify() encontrados (17 en el código)17, sin falsos11, 2 falsos0, 1 falso
Handlers de Messenger vinculados a su mensaje20600
Inyecciones de contenedor, rutas1,360, 1880, 1 (falso)0, 0
  • codebase-memory-mcp coincide con phpgraph en el 99.7 % de las llamadas que resuelve desde tipos. El resto de sus aristas se adivinan desde el nombre del método (confianza 0.04 a 0.44): donde ambos vinculan la misma llamada, discrepan en 9,400 sitios, y sus 11,300 aristas extra son en su mayoría llamadas a vendor/ adjuntas a un método de proyecto del mismo nombre (19 de 20 verificados).
  • graphify vincula $this->other->foo() a foo() de la clase actual: correcto para $this->foo(), incorrecto para las 10 de 10 diferencias verificadas.
  • Lo que tienen y phpgraph no: otros lenguajes, búsqueda de texto completo y vectorial, consultas Cypher (codebase-memory-mcp), una visualización interactiva y documentos en el grafo (graphify).

FAQ

¿phpgraph envía mi código a algún lugar? No. Se ejecuta localmente, no hace ninguna llamada de red y no usa ningún LLM.

¿Ejecuta mi código? No. Analiza archivos PHP y lee configuración JSON, YAML y XML. Incluso autoload_classmap.php y configuradores de servicios PHP se analizan, no se incluyen.

¿Qué versiones de PHP puede analizar? Cualquier cosa que nikic/php-parser 5 analice: sintaxis de PHP 7 y 8. phpgraph en sí necesita PHP 8.2 o más reciente.

¿Con qué agentes funciona? Cualquier cliente MCP sobre stdio: Sherpa, Claude Code, Codex, Cursor, Claude Desktop, y otros. mcp-config imprime la configuración para los comunes.

¿Qué tan grande puede ser un proyecto que maneje? El proyecto más grande del corpus, Akeneo, tiene 8,400 archivos: aproximadamente 10 s y 500 MB para una construcción completa, aproximadamente 3 s para una reconstrucción después de una edición.

Limitaciones

  • Solo se lee PHP: overview nombra los otros lenguajes de un repositorio mixto (sus llamadas a la parte PHP no están enlazadas), y las instrucciones de MCP indican de inmediato cuando un proyecto no tiene PHP en absoluto.
  • Las llamadas a funciones globales no son aristas del grafo, aunque lo que una función devuelve tipa las llamadas sobre ella (now()->addDay(), response()->json()); las llamadas dinámicas ($this->$name(), __call) se resuelven solo a través de los métodos que una clase documenta (@method, @mixin).
  • Los genéricos se leen de los docblocks (@template, @extends Repository<Order>, @return Collection<int, Item>, ScalarNodeDefinition<$this>), en el proyecto y en vendor/: una plantilla se vincula por el tipo del receptor y los padres que la transmiten, y un foreach recorre los elementos de cualquier colección tipada. Una unión (Response|ResponseFactory, documentada o declarada) se llama en el miembro que declara el método, y solo cuando un miembro lo hace; un cierre pasado a un método recibe los parámetros que el método documenta que pasa (@param callable($this): mixed, el when(fn ($query) => ...) de Laravel); @use HasFactory<HotelFactory> en un uso de trait vincula sus plantillas. Una plantilla de un método se vincula por los argumentos de cada llamada: la clase que nombra un argumento (getRepository(Order::class) con @param class-string<T>), el tipo de un valor (@param T $value), lo que devuelve un cierre (@param callable(): T, sus declaraciones de retorno o tipo nativo). @phpstan-return y @psalm-return se leen antes de @return; una condición que los argumentos no deciden (TAsync is false ? Response : PromiseInterface) da la unión de sus ramas. Las propiedades mágicas documentadas con @property y @property-read continúan las cadenas; en un modelo Eloquent, también lo hacen los casts ('paid_at' => 'datetime' es un Carbon, un enum, el TGet de una clase de cast), las marcas de tiempo y las relaciones leídas como propiedades ($page->book, lo que devuelve book()->getResults()). Un método que no declara tipo de retorno devuelve lo que declara el método que sobrescribe (@inheritDoc). Las condiciones sobre un tipo ($id is array ? Collection : Model) no se evalúan, y un método de fachada documentado como mixed (DB::transaction()) permanece desconocido.
  • Las cadenas se detienen en métodos mágicos y en dependencias sin un tipo de retorno utilizable. Las clases propias de PHP (DateTime, ArrayObject, SplObjectStorage, PDO...) se conocen a partir de firmas generadas una vez (tools/internal-classes.php); las de las extensiones intl y mysqli no.
  • Sin un vendor/ instalado, las cadenas de llamadas se detienen en la primera dependencia.
  • Los servicios construidos en tiempo de ejecución (pases de compilador, extensiones de bundle, ids calculados a partir de valores solo conocidos en tiempo de ejecución), rutas XML, recursos de API Platform declarados en XML o YAML, y esquemas de API distintos de OpenAPI y protobuf (GraphQL, AsyncAPI) no se leen; una ruta OpenAPI reescrita por una puerta de enlace no se sigue; las inyecciones que no se pueden enlazar se enumeran mediante overview.
  • Se asume que dos servicios que declaran una clase de mensaje con el mismo nombre la comparten.

Contribuciones

Las incidencias y las solicitudes de extracción son bienvenidas. Consulta CONTRIBUTING.md: composer check debe pasar (php-cs-fixer, PHPStan nivel máximo, PHPUnit), y los cambios en el análisis se juzgan sobre el corpus. Sin PHP instalado, bin/dev composer check ejecuta todo en Docker.

Lo que permanece estable entre versiones (comandos, herramientas MCP, el formato graph.json) se enumera en docs/STABILITY.md; los cambios están en el registro de cambios.

Las versiones se publican empujando una etiqueta de versión: el flujo de trabajo de lanzamiento compila el PHAR, los ejecutables para Linux, macOS y Windows (tools/build-binary.sh), y la imagen Docker ghcr.io/hbenabdallah/phpgraph.

Licencia

phpgraph está disponible con código fuente bajo la Licencia PolyForm Shield 1.0.0: puedes usarlo, modificarlo y redistribuirlo, incluso en una empresa, para cualquier propósito que no compita con phpgraph. Proporcionar un producto competidor a partir de este código no está permitido. Esta no es una licencia de código abierto aprobada por OSI.