phpstan/phpdoc-parser

repository·2.3.x·Indexed 23 days ago

https://github.com/phpstan/phpdoc-parser

A library that represents PHPDocs as an Abstract Syntax Tree (AST) for robust parsing, inspection, and modification of PHPDoc comments. It supports a wide range of type syntaxes including generics, shapes, union/intersection types, and conditional types, as well as Doctrine Annotations. The library provides tools for format-preserving printing, AST traversal via NodeVisitor, and constant expression parsing.

Tokens
4.4K
Snippets
7
Records
22
Agent score
81%

What's inside phpdoc-parser

  1. Configure parser classes using ParserConfig

    2.3.x

    In version 2.0, parser classes (like Lexer, ConstExprParser, TypeParser, and PhpDocParser) share a common ParserConfig object. This ensures consistent configuration across the parser components and allows for future optional parameters without breaking constructor signatures.

    Instead of passing multiple boolean values and attribute arrays to each constructor, instantiate a ParserConfig and pass it to all parser components.

    use PHPStan\PhpDocParser\Lexer\Lexer;
    use PHPStan\PhpDocParser\ParserConfig;
    use PHPStan\PhpDocParser\Parser\ConstExprParser;
    use PHPStan\PhpDocParser\Parser\TypeParser;
    use PHPStan\PhpDocParser\Parser\PhpDocParser;
    
    $config = new ParserConfig(usedAttributes: ['lines' => true, 'indexes' => true]);
    $lexer = new Lexer($config);
    $constExprParser = new ConstExprParser($config);
    $typeParser = new TypeParser($config, $constExprParser);
    $phpDocParser = new PhpDocParser($config, $typeParser, $constExprParser);
  2. Supported PHPDoc Type Syntax

    2.3.x

    The parser supports a wide range of type syntaxes, including:

    • Basic types: string, int, bool, null, self, static, $this, etc.
    • Nullable types: ?string
    • Union and intersection types: string|int, Foo&Bar
    • Generics: array<string>, Collection<covariant T>
    • Shapes: array{name: string, age: int, ...}, object{name: string, age: int}
    • Callables: callable(string): bool, Closure(int): void
    • Conditional types: ($input is string ? string : int)
    • Offset access: T[K]
    • Constant expressions: self::CONST*, 123, 'string'
  3. Constant Expression Parsing

    2.3.x

    The ConstExprParser handles constant expressions used within PHPDoc tags. Supported values include:

    • Scalars: integers, floats, strings, true, false, null
    • Arrays: {1, 2, 'key' => 'value'}
    • Class constants: ClassName::CONSTANT
  4. Modify and print PHPDocs with format preservation

    2.3.x

    To modify a PHPDoc while keeping the original formatting (comments, spacing, etc.), follow these steps:

    1. Enable required attributes (lines, indexes, comments) in ParserConfig.
    2. Use a NodeTraverser with a CloningVisitor to create a deep copy of the AST.
    3. Modify the cloned AST nodes.
    4. Use Printer::printFormatPreserving() passing the modified node, the original node, and the original tokens.
    <?php
    
    require_once __DIR__ . '/vendor/autoload.php';
    
    use PHPStan\PhpDocParser\Ast\NodeTraverser;
    use PHPStan\PhpDocParser\Ast\NodeVisitor\CloningVisitor;
    use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocNode;
    use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
    use PHPStan\PhpDocParser\Lexer\Lexer;
    use PHPStan\PhpDocParser\ParserConfig;
    use PHPStan\PhpDocParser\Parser\ConstExprParser;
    use PHPStan\PhpDocParser\Parser\PhpDocParser;
    use PHPStan\PhpDocParser\Parser\TokenIterator;
    use PHPStan\PhpDocParser\Parser\TypeParser;
    use PHPStan\PhpDocParser\Printer\Printer;
    
    // basic setup with enabled required lexer attributes
    
    $config = new ParserConfig(usedAttributes: ['lines' => true, 'indexes' => true, 'comments' => true]);
    $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); // PhpDocNode
    
    $cloningTraverser = new NodeTraverser([new CloningVisitor()]);
    
    /** @var PhpDocNode $newPhpDocNode */
    [$newPhpDocNode] = $cloningTraverser->traverse([$phpDocNode]);
    
    // change something in $newPhpDocNode
    $newPhpDocNode->getParamTagValues()[0]->type = new IdentifierTypeNode('Ipsum');
    
    // print changed PHPDoc
    $printer = new Printer();
    $newPhpDoc = $printer->printFormatPreserving($newPhpDocNode, $phpDocNode, $tokens);
    echo $newPhpDoc; // '/** @param Ipsum $a */'
  5. Upgrade from phpdoc-parser 1.x to 2.0

    2.3.x

    When upgrading to version 2.0, note the following major changes:

    • PHP Version: Requires PHP 7.4 or newer.
    • Configuration: Parser classes no longer accept individual boolean/array parameters in constructors. They now use a shared ParserConfig object.
    • Doctrine Support: The parser now supports Doctrine Annotations. AST nodes for these are located in the PHPStan\PhpDocParser\Ast\PhpDoc\Doctrine namespace.
    • Stricter Parsing: Invalid PHPDoc syntax that was previously silently treated as a description (e.g., missing whitespace between a type and a description) will now result in an InvalidTagValueNode.
    • AST Changes:
      • QuoteAwareConstExprStringNode is removed; use ConstExprStringNode instead.
      • ArrayShapeNode construction is now via static methods.
      • Text between tags is now consistently treated as part of the preceding tag's description.
  6. Handle whitespace requirements for PHPDoc descriptions

    2.3.x

    In 2.0, if you want text to be treated as a description rather than part of an invalid type, you must provide whitespace between the type and the description text. Otherwise, the parser will produce an InvalidTagValueNode.

    Incorrect (results in InvalidTagValueNode):

    /** @return \Closure(...int, string): string */
    /** @return array{foo: int}} */

    Correct (whitespace separates type from description):

    /** @return \Closure (...int, string): string */
    /** @return array{foo: int} } */
  7. Parse PHPDoc strings into an AST

    2.3.x

    To parse a PHPDoc string, you must set up a Lexer, ConstExprParser, TypeParser, and the main PhpDocParser. The PhpDocParser::parse() method accepts a TokenIterator and returns a PhpDocNode representing the Abstract Syntax Tree (AST).

    <?php
    
    require_once __DIR__ . '/vendor/autoload.php';
    
    use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode;
    use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocNode;
    use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
    use PHPStan\PhpDocParser\Lexer\Lexer;
    use PHPStan\PhpDocParser\ParserConfig;
    use PHPStan\PhpDocParser\Parser\ConstExprParser;
    use PHPStan\PhpDocParser\Parser\PhpDocParser;
    use PHPStan\PhpDocParser\Parser\TokenIterator;
    use PHPStan\PhpDocParser\Parser\TypeParser;
    
    // basic setup
    
    $config = new ParserConfig(usedAttributes: []);
    $lexer = new Lexer($config);
    $constExprParser = new ConstExprParser($config);
    $typeParser = new TypeParser($config, $constExprParser);
    $phpDocParser = new PhpDocParser($config, $typeParser, $constExprParser);
    
    // parsing and reading a PHPDoc string
    
    $tokens = new TokenIterator($lexer->tokenize('/** @param Lorem $a */'));
    $phpDocNode = $phpDocParser->parse($tokens); // PhpDocNode
    $paramTags = $phpDocNode->getParamTagValues(); // ParamTagValueNode[]
    echo $paramTags[0]->parameterName; // '$a'
    echo $paramTags[0]->type; // IdentifierTypeNode - 'Lorem'
  8. Retrieve escaped string values from ConstExprStringNode

    2.3.x

    In version 2.0, ConstExprStringNode::$value contains unescaped values without surrounding quotes. The $trimStrings parameter has been removed from ConstExprParser::parse().

    To get the escaped value including the surrounding quotes ('' or ""), use ConstExprStringNode::__toString() or the Printer class.

  9. Traverse the AST using NodeVisitor

    2.3.x

    The library uses a visitor-based traversal system. You can implement AbstractNodeVisitor to inspect or transform nodes during traversal using a NodeTraverser.

    use PHPStan\PhpDocParser\Ast\AbstractNodeVisitor;
    use PHPStan\PhpDocParser\Ast\Node;
    use PHPStan\PhpDocParser\Ast\NodeTraverser;
    use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
    
    $visitor = new class extends AbstractNodeVisitor {
        public function enterNode(Node $node) {
            if ($node instanceof IdentifierTypeNode) {
                // inspect or transform the node
            }
            return $node;
        }
    };
    
    $traverser = new NodeTraverser([$visitor]);
    $traverser->traverse([$phpDocNode]);
  10. Configure Node Attributes

    2.3.x

    Nodes can carry metadata like line numbers, token indexes, and comments. These are essential for mapping AST nodes back to source positions and for the format-preserving printer. Enable them via ParserConfig using the usedAttributes parameter.

    $config = new ParserConfig(usedAttributes: ['lines' => true, 'indexes' => true, 'comments' => true]);