Skip to content

Instantly share code, notes, and snippets.

@ellisgl
Last active May 7, 2023 15:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ellisgl/7badc78672abb83c1568205368d5c55d to your computer and use it in GitHub Desktop.
Save ellisgl/7badc78672abb83c1568205368d5c55d to your computer and use it in GitHub Desktop.
Use PHPStan\PhpDocParser to parse the return types listed in a PHPDoc comment.
<?php
include 'vendor/autoload.php';
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode;
use PHPStan\PhpDocParser\Ast\Type\ArrayTypeNode;
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
use PHPStan\PhpDocParser\Ast\Type\IntersectionTypeNode;
use PHPStan\PhpDocParser\Ast\Type\UnionTypeNode;
use PHPStan\PhpDocParser\Lexer\Lexer;
use PHPStan\PhpDocParser\Parser\ConstExprParser;
use PHPStan\PhpDocParser\Parser\PhpDocParser;
use PHPStan\PhpDocParser\Parser\TokenIterator;
use PHPStan\PhpDocParser\Parser\TypeParser;
// Test PHPDocs
$docComment = '/**
* @return Collection&int[]
*/';
$docComment2 = '/**
* @return string|int[]|null
*/';
$docComment3 = '/**
* @return string
*/';
$docComment3 = '/**
* @return int[]
*/';
// Construct required objects for the PhpDocParser construct.
$lexer = new Lexer();
$typeParser = new TypeParser();
$constExprParser = new ConstExprParser();
// Set the required options.
$requireWhitespaceBeforeDescription = true;
$preserveTypeAliasesWithInvalidTypes = true;
// Only parse return tags from PHPDoc.
$usedAttributes = ['@return'];
// Create the parser.
$phpDocParser = new PhpDocParser(
$typeParser,
$constExprParser,
$requireWhitespaceBeforeDescription,
$preserveTypeAliasesWithInvalidTypes,
$usedAttributes
);
// Create the tokens from the doc comment.
$tokens = $lexer->tokenize($docComment3);
// Parse the tokens into a PhpDocNode object.
$phpDocNode = $phpDocParser->parse(new TokenIterator($tokens));
// var_dump($phpDocNode);
// Storage for the return types.
$returnTypes = [];
// Loop through the children of the PhpDocNode object.
foreach ($phpDocNode->children as $child) {
// Check if the child is a PhpDocTagNode.
if ($child instanceof PhpDocTagNode) {
$value = $child->value;
// print_r($value);
// Check if the child is a ReturnTagValueNode.
if ($value instanceof ReturnTagValueNode) {
// Are we dealing with an intersection type return?
if ($value->type instanceof IntersectionTypeNode) {
// Store the types in an array, which will be returned as an array inside $returnTypes.
$types = [];
foreach ($value->type->types as $type) {
if ($type instanceof IdentifierTypeNode) {
// Simple type, store it in the array.
$types[] = $type->name;
} elseif ($type instanceof ArrayTypeNode && $type->type instanceof IdentifierTypeNode) {
// Array type, store it in the array as shown in the string.
$types[] = $type->type->name . '[]';
}
}
// Store the array in $returnTypes.
$returnTypes[] = $types;
} elseif ($value->type instanceof UnionTypeNode) {
// This is of a union type.
// Loop through the types in the union.
foreach ($value->type->types as $type) {
if ($type instanceof IdentifierTypeNode) {
// Simple type, store it in the array.
$returnTypes[] = $type->name;
} elseif ($type instanceof ArrayTypeNode && $type->type instanceof IdentifierTypeNode) {
// Array type, store it in the array as shown in the string.
$returnTypes[] = $type->type->name . '[]';
}
}
} elseif ($value->type instanceof IdentifierTypeNode) {
$returnTypes[] = $value->type->name;
} elseif ($value->type instanceof ArrayTypeNode && $value->type->type instanceof IdentifierTypeNode) {
/** @var IdentifierTypeNode $type */
$type = $value->type->type;
$returnTypes[] = $type->name . '[]';
}
}
}
}
print_r($returnTypes);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment