phpstan/phpdoc-parser
phpstan/phpdoc-parser parses PHPDocs into a typed AST so you can read and modify docblocks programmatically. It handles rich types like unions, generics, array shapes, callables, and conditional types, plus Doctrine annotations. Powers PHPStan.
Architecture fit. phan/phpdoc-parser is a deliberately low-level, framework-agnostic building block: a lexer → type parser → const-expr parser → PhpDocParser pipeline that emits a typed AST (PhpDocNode, ParamTagValueNode, IdentifierTypeNode, etc.). It is not a ready-made docblock service; we own the orchestration layer that wires ParserConfig, Lexer, TypeParser, ConstExprParser, and PhpDocParser together. That is a good fit if our value comes from acting on PHPDoc (validation, codegen, custom linting, API documentation, migration tooling in a Laravel codebase), and a poor fit if we just need to read @param strings — regex or phpDocumentor/reflection-docblock will be faster to ship for the trivial case.
Integration feasibility. Composer-installable, MIT-licensed, zero infrastructure footprint, no service provider required. Feature coverage is genuinely strong: union/intersection types, generics with variance, array/object shapes, callable signatures, conditional types, offset access, constant expressions, and Doctrine-annotation AST nodes. The format-preserving printer (via CloningVisitor + Printer::printFormatPreserving) is the differentiating capability — it lets us rewrite PHPDoc without destroying author formatting, which is what makes automated codemods or doc-sync tooling viable rather than a whitespace-destroying find-and-replace.
Technical risk. The primary risk is API surface churn. The project has crossed a major version boundary (1.x → 2.x) with breaking signature changes (e.g., ParserConfig as a required constructor argument, usedAttributes replacing implicit token attributes). Nodes are mutable and public; the visitor/traverser control constants (DONT_TRAVERSE_CHILDREN, STOP_TRAVERSAL, REMOVE_NODE, DONT_TRAVERSE_CURRENT_AND_CHILDREN) are stable but the node class hierarchy is large and evolving (new tags land in minor releases — e.g., @pure-unless-parameter-passed in 2.3.3). Parser behavior is intentionally permissive and tuned to PHPStan's dialect; it is not a spec-enforcing validator, so "did it parse?" is not the same as "is this PHPDoc correct?"
Key questions.
Stack fit. Native PHP, no Laravel coupling. In a Laravel codebase it belongs behind a small application-level abstraction — e.g., a PhpDocAnalyzer service registered as a singleton — so controllers, Artisan commands, and queued jobs depend on our interface, not on PHPStan\PhpDocParser\* classes directly. The parser objects are stateless enough to be reused per process, so binding them once in the container is appropriate.
Migration path. If replacing regex-based tag scraping or reflection-docblock, migrate in stages: (1) stand up a shadow parser that runs alongside the existing extractor and logs disagreements; (2) swap reads to the AST-based path for the highest-value tags (@param, @return, @var, @property); (3) only then introduce write/rewrite paths using the format-preserving printer. If migrating from a 1.x-based internal tool, treat the 2.x jump as a rewrite of the wiring layer — ParserConfig is now mandatory and attribute handling is explicit.
Compatibility. Verify PHP version support against our runtime matrix (the project tracks modern PHP, including 8.5 in CI). Because it is a transitive dependency of PHPStan itself, version pinning collides with any PHPStan version we already run — resolve that constraint before committing, not after. Doctrine annotation parsing is supported but is a secondary surface; do not assume parity with doctrine/annotations.
Sequencing. Week 1: spike the read path on a representative sample of our real docblocks, measuring parse success rate and failure taxonomy. Week 2: build the adapter + position-attribute diagnostics. Week 3: golden-file tests, including deliberately malformed docs. Week 4: integrate into CI or the target command; defer the printer until the read path is trusted.
Maintenance. Upstream is actively maintained with frequent, small releases and a disciplined 2.3.x maintenance branch; MIT removes legal friction. The burden is ours, not theirs: each upgrade requires diffing the AST and re-running golden files. Budget a recurring, non-trivial upgrade task rather than treating this as install-and-forget.
Support. Community-supported, no SLA, no vendor. Escalation path is upstream issues and PRs — acceptable for a build-time/CI tool, unacceptable for anything on the request-serving critical path without an internal fallback.
Scaling. Parsing is CPU-bound and linear in docblock volume. Cost is negligible per file but material across a full repository scan; cache parsed results by file hash and content, and exploit __set_state() support for serializing ASTs to a cache layer. Avoid parsing on every request — confine to CI, Artisan commands, or queued analysis jobs. Memory risk comes from holding many PhpDocNode trees live at once; process in chunks.
Failure modes. (1) Malformed PHPDoc triggers parser exceptions rather than silent degradation — every call site needs a try/catch with a defined policy (skip, warn, fail the build). (2) Unrecognized tags may fall through to generic nodes, producing false confidence. (3) Printer round-trips can subtly alter whitespace or ordering on unusual input; guard with format-preserving golden tests. (4) Dependency conflicts with PHPStan versions surfacing only at composer update. (5) Silent behavior drift on minor bumps.
Ramp-up. Medium. The visitor/traverser model is familiar to anyone who has used nikic/PHP-Parser, but the node hierarchy is broad and the entry-point wiring is non-obvious from the README alone. Plan for one engineer to own the adapter and write a short internal primer covering the pipeline setup, the attribute configuration required by the printer, and the exception policy; the rest of the team should interact only through that adapter.
How can I help you explore Laravel packages today?