This file contains important instructions for working on the phpdoc-parser project.
phpstan/phpdoc-parser is a library that represents PHPDocs with an Abstract Syntax Tree (AST). It supports parsing and modifying PHPDocs, and is primarily used by PHPStan for static analysis.
- Parses PHPDoc comments into an AST representation
- Supports all PHPDoc tags and types (see PHPStan documentation)
- Format-preserving printer for modifying and printing AST nodes
- Support for Doctrine Annotations parsing
- Nullable, intersection, generic, and conditional types support
- PHP ^7.4 || ^8.0
- Platform target: PHP 7.4.6
The source code is organized into the following main components:
-
Lexer (
src/Lexer/)Lexer.php- Tokenizes PHPDoc strings
-
Parser (
src/Parser/)PhpDocParser.php- Main PHPDoc parser (parses tags and structure)TypeParser.php- Parses PHPDoc type expressionsConstExprParser.php- Parses constant expressionsTokenIterator.php- Iterator for tokensStringUnescaper.php- Handles string unescapingParserException.php- Exception handling
-
AST (
src/Ast/)Node.php- Base AST node interfaceNodeTraverser.php- Traverses and transforms ASTNodeVisitor.php- Visitor pattern for AST traversalType/- Type nodes (GenericTypeNode, ArrayTypeNode, UnionTypeNode, etc.)PhpDoc/- PHPDoc tag nodes (ParamTagValueNode, ReturnTagValueNode, etc.)ConstExpr/- Constant expression nodesNodeVisitor/- Built-in visitors (CloningVisitor, etc.)
-
Printer (
src/Printer/)Printer.php- Prints AST back to PHPDoc formatDiffer.php- Computes differences between AST nodesDiffElem.php- Represents diff elements
-
Configuration
ParserConfig.php- Parser configuration (attributes to use)
Tests mirror the source structure and include:
-
Parser Tests (
tests/PHPStan/Parser/)TypeParserTest.php- Type parsing testsPhpDocParserTest.php- PHPDoc parsing testsConstExprParserTest.php- Constant expression parsing testsFuzzyTest.php- Fuzzy testingDoctrine/- Doctrine annotation test fixtures
-
AST Tests (
tests/PHPStan/Ast/)NodeTraverserTest.php- Node traversal testsAttributes/AttributesTest.php- AST attribute testsToString/- Tests for converting AST to stringNodeVisitor/- Visitor pattern tests
-
Printer Tests (
tests/PHPStan/Printer/)- Tests for format-preserving printing functionality
phpunit.xml- PHPUnit test configurationphpstan.neon- PHPStan static analysis configurationphpstan-baseline.neon- PHPStan baseline (known issues)phpcs.xml- PHP CodeSniffer configurationcomposer.json- Dependencies and autoloading
The parsing flow follows these steps:
- Lexing:
Lexertokenizes the PHPDoc string into tokens - Parsing:
PhpDocParserusesTypeParserandConstExprParserto build an AST - Traversal/Modification:
NodeTraverserwithNodeVisitorcan traverse and modify the AST - Printing:
Printerconverts the AST back to PHPDoc format (optionally preserving formatting)
$config = new ParserConfig(usedAttributes: []);
$lexer = new Lexer($config);
$constExprParser = new ConstExprParser($config);
$typeParser = new TypeParser($config, $constExprParser);
$phpDocParser = new PhpDocParser($config, $typeParser, $constExprParser);
$tokens = new TokenIterator($lexer->tokenize('/** @param Lorem $a */'));
$phpDocNode = $phpDocParser->parse($tokens);For format-preserving printing (used when modifying existing PHPDocs), enable these attributes:
lines- Preserve line informationindexes- Preserve token indexescomments- Preserve comments
- Create a new
*TagValueNodeclass insrc/Ast/PhpDoc/ - Add parsing logic in
PhpDocParser.php - Add tests in
tests/PHPStan/Parser/PhpDocParserTest.php - Run tests and PHPStan
- Create a new
*TypeNodeclass insrc/Ast/Type/ - Add parsing logic in
TypeParser.php - Add printing logic in
Printer.php - Add tests in
tests/PHPStan/Parser/TypeParserTest.php - Run tests and PHPStan
- Update token generation in
Lexer.php - Update parsers that consume those tokens
- Add/update tests
- Run comprehensive checks with
make check
Tests are run using PHPUnit:
make testsOr directly:
php vendor/bin/phpunitPHPStan static analysis is run with:
make phpstanOr directly:
php vendor/bin/phpstanTo run all quality checks (lint, code style, tests, and PHPStan):
make checkThis runs:
lint- PHP syntax checking with parallel-lintcs- Code style checking with phpcstests- PHPUnit test suitephpstan- Static analysis
You MUST run both tests and PHPStan after every code change:
make tests && make phpstanOr use the comprehensive check:
make checkDO NOT commit or consider work complete until both tests and PHPStan pass successfully.
NEVER delete any tests. Tests are critical to the project's quality and regression prevention. If tests are failing:
- Fix the implementation to make tests pass
- Only modify tests if they contain actual bugs or if requirements have legitimately changed
- When in doubt, ask before modifying any test
make cs-fix- Automatically fix code style issuesmake lint- Check PHP syntax onlymake cs- Check code style onlymake phpstan-generate-baseline- Generate PHPStan baseline (use sparingly)
- Make code changes
- Run
make tests- ensure all tests pass - Run
make phpstan- ensure static analysis passes - Fix any issues found
- Commit only when both pass
- Repeat as needed
Remember: Tests and PHPStan MUST pass before any commit.
- Follow PSR-12 coding standards (enforced by phpcs)
- Use tabs for indentation (project convention)
- Run
make cs-fixto automatically fix code style issues - Always run
make csto verify code style before committing
- Project uses strict PHPStan rules (level max)
- All code must pass static analysis
- Avoid adding to phpstan-baseline.neon unless absolutely necessary
- Type hints are required for all public APIs
- All new features must include tests
- Tests should be in the corresponding test directory matching src/ structure
- Use data providers for testing multiple similar cases
- Test both valid and invalid inputs
- Include edge cases and error conditions
- All AST nodes implement the
Nodeinterface - Nodes should be immutable where possible
- Use
__toString()for debugging output - Implement proper equality checks if needed
- Follow the visitor pattern for AST traversal
- Parsers should be recursive descent style
- Use
TokenIteratorfor token consumption - Throw
ParserExceptionfor syntax errors - Support optional attributes through
ParserConfig - Maintain backwards compatibility when adding features
- This library is used by PHPStan and many other tools
- Breaking changes should be avoided
- New features should be opt-in when possible
- Deprecate before removing functionality
- The parser is performance-critical (runs on large codebases)
- Avoid unnecessary object allocations
- Be careful with regex patterns
- Consider memory usage in loops
- Public APIs should have PHPDoc comments
- Complex logic should include inline comments
- Update README.md when adding major features
- Reference PHPStan documentation for PHPDoc tag specifications