r/PHP • u/Patryyyck • 2d ago
I built an open-source static flow analyzer for Symfony — PHPFlow v0.1.0
Hi everyone,
I’ve just released the first public version of PHPFlow, an open-source static flow analyzer for PHP/Symfony applications.
The idea came from a problem I regularly run into on larger applications: answering a question like “what actually happens when this route is called?” can mean jumping through a controller, several services, Messenger dispatches and handlers, repositories, database queries and external APIs.
PHPFlow analyzes the source code without running the target application and reconstructs those flows as a graph.
For example, it can help answer:
- What happens when this Symfony route is called?
- Which routes/messages can reach this database table?
- What depends on this service?
- Where is this Messenger message handled?
- Which flows call this external API?
- Where can this exception surface?
v0.1.0 currently understands Symfony routes, dependency injection, Messenger, repositories, Doctrine DBAL/QueryBuilder, external HTTP calls, exceptions and a number of control-flow patterns.
It also generates a self-contained interactive HTML viewer with search, functional lanes, Messenger boundaries, minimap, path-to-effects and critical-path exploration.
PHPFlow is deliberately conservative: if it cannot prove a relationship statically, it doesn’t invent one. It also never boots or executes the application being analyzed.
There’s a bundled Symfony demo, so after cloning:
make setup
make demo
I’m especially interested in trying it against real-world Symfony applications now. If you give it a try and it misses a pattern used by your project, I’d really like to hear about it.
GitHub: github.com/patryyyck/phpflow
Feedback, bug reports and criticism are very welcome.
1
1
1
u/LavishnessOne1649 2d ago
This looks handy. Gave it a star, and actually will try it out at work tomorrow.
1
u/These_Reality519 2d ago
How does PHPFlow account for container-build wiring such as compiler passes, tagged iterators, and service aliases? Does it inspect a compiled container, or infer those relationships from configuration and type hints?
3
u/Patryyyck 1d ago
PHPFlow does not inspect or boot the compiled Symfony container. That's an intentional constraint: the target application is never executed.
In v0.1 it infers service relationships statically from the source code, type hints and the Symfony service configuration patterns it currently understands. This includes supported service aliases and interface-to-implementation resolution.
Compiler passes and tagged iterators are a different story. Since they can create or modify wiring during container compilation, PHPFlow does not currently model them as first-class wiring mechanisms. If a relationship only becomes visible through a compiler pass or tag processing, PHPFlow may therefore miss it rather than guess.
That's one of the interesting boundaries I'm hoping real-world testing will expose. A future direction could be deeper static modelling of common compiler-pass/tag patterns, while keeping the core guarantee that PHPFlow doesn't need to boot the target application.
So the short version is: static inference, not compiled-container inspection — with aliases/type-based wiring supported where PHPFlow can prove the relationship, and compiler-pass/tag-generated wiring currently a limitation.
1
u/These_Reality519 1d ago
Messenger seems like an interesting edge case here. Since a handler can be registered with a plain
messenger.message_handlertag in YAML, does PHPFlow pick that up too, or just#[AsMessageHandler]?2
u/Patryyyck 1d ago
PHPFlow currently supports
#[AsMessageHandler]explicitly.It also understands Messenger routing configuration, including supported YAML/PHP routing definitions and transports, but plain handler registration through a
messenger.message_handlerservice tag in YAML is not something I’d claim as fully supported in v0.1 yet.If the handler is still inferable from its callable/type information, PHPFlow may resolve part of the relationship, but if the tag itself is what establishes the handler registration, that can currently be a blind spot.
This is very much in the same family as the compiler-pass/tagged-service question: Symfony’s container can encode behavior through tags that only become explicit during container processing.
So the short answer is:
#[AsMessageHandler]yes; genericmessenger.message_handlertag registration is currently a limitation / roadmap item rather than guaranteed support.That’s a useful case to add to the compatibility matrix, because it’s common in real Symfony projects.
2
u/thmsbrss 2d ago
Would be nice to have it for other frameworks or vanilla, too.