symplify/simple-php-doc-parser
Simple PHP Doc Parser by Symplify is a lightweight library for reading PHP docblocks. It extracts comments, tags, and annotations from classes, methods, and properties through a simple API, making PHPDoc data easy to use in static analysis, code generation, and documentation tools.
Install the package alongside its underlying parser:
composer require symplify/simple-php-doc-parser
Because the package is essentially a thin, service-oriented façade over phpstan/phpdoc-parser, the first place to look is the wrapper class itself (Symplify\SimplePhpDocParser\PhpDocParser) — not the upstream AST. The wrapper hides the boilerplate of instantiating PHPStan's Lexer, ConstExprParser, TypeParser, and PhpDocParser manually, and exposes a single method that turns a raw docblock string into a PhpDocNode.
The most common first use case is extracting type information from a docblock for a custom tool, rule, or generator. A minimal run:
use Symplify\SimplePhpDocParser\PhpDocParser;
$parser = new PhpDocParser(/* dependencies if not using DI */);
$docBlock = <<<DOC
/**
* @param string \$name
* @param int \$age
* @return bool
*/
DOC;
$phpDocNode = $parser->parseDocBlock($docBlock);
From there you traverse $phpDocNode->children to find ParamTagValueNode, ReturnTagValueNode, VarTagValueNode, etc. Note that the wrapper accepts docblocks including the /** ... */ delimiters when using parseDocBlock(), while other entry points (parseStringToNode(), parseDocBlockToNode()) trim differently — pick one and be consistent.
If you are inside a Symfony application or a Symplify-based tool (Rector, ECS, PHPStan extensions), the package registers itself as a service; you can inject PhpDocParser directly and skip manual construction entirely.
Extract signatures for generators / custom rules. The dominant pattern is: read a reflection docblock (ReflectionMethod::getDocComment()), feed it to the parser, pull out typed tag nodes, and act on them. This is what makes it useful in a Rector rule, a PHPStan extension, or a code generator.
$phpDocNode = $parser->parseDocBlock($reflectionMethod->getDocComment());
foreach ($phpDocNode->getParamTagValues() as $param) {
// $param->parameterName, $param->type, $param->description
}
The upstream PhpDocNode already exposes convenience getters like getParamTagValues(), getReturnTagValue(), getVarTagValues(), getMethodTagValues(), getPropertyTagValues() — use these instead of walking children by hand.
Magic property / method discovery. A very practical use is extracting @property and @method declarations from a class that relies on __call/__get. This is common in ORM entities, Laravel facades, and legacy proxy patterns. Parse the class docblock once, cache the result keyed by class name + file mtime, and expose the members to your analysis.
Visitor-based transformations. The package ships with a PhpDocNodeVisitor and a traverser. If you need to rewrite docblocks (e.g., normalize types, drop deprecated tags), implement a visitor rather than string-regexing the docblock — it keeps positions and node structure intact.
Integration with static analysis. Inside a Rector rule, you get the parser via constructor injection; inside a PHPStan rule, use the PhpDocParser service from the container. This is where the "service integration" flavor pays off — you don't manage parser state per file.
Caching. Always memoize parse results. Parsing the same docblock repeatedly in a hot path (a rule applied to thousands of files) is measurable overhead. Key on sha1($docBlock) or on file + line, whichever is more stable in your context.
@ — throws from the underlying parser. Wrap calls in a try/catch when processing third-party code you don't control, and fail gracefully (return null, log, skip).PhpDocNode trees. Be prepared for $node->type to be an IdentifierTypeNode, GenericTypeNode, UnionTypeNode, or a ConstTypeNode — union and intersection types are not plain strings.phpstan/phpdoc-parser. Because the package wraps a specific major range of the upstream parser, upgrading one may force the other. Pin phpstan/phpdoc-parser in your composer.json if you also use it directly, and check the wrapper's composer.json before bumping.$node->getStartLine() is relative to the docblock string you passed in, not the source file. Add the docblock's actual start line yourself if you're reporting diagnostics.parseDocBlock() expects the /** */ envelope; parseStringToNode() expects the inner content without comment markers. Mixing them silently produces empty nodes instead of an obvious error — a frequent source of "why is my result empty?" confusion.*/.array<string, list<int>>.Reflection or file reading); there is no parseClass(SomeClass::class) method.var_dump($raw)), then test it in isolation against https://phpstan.org/…-style playgrounds of the underlying parser. The wrapper rarely adds failure modes of its own; the error almost always comes from the upstream parser on bad input.How can I help you explore Laravel packages today?