vendor/api-platform/core/src/Metadata/Resource/Factory/PhpDocResourceMetadataCollectionFactory.php line 44
<?php/** This file is part of the API Platform project.** (c) Kévin Dunglas <dunglas@gmail.com>** For the full copyright and license information, please view the LICENSE* file that was distributed with this source code.*/declare(strict_types=1);namespace ApiPlatform\Metadata\Resource\Factory;use ApiPlatform\Metadata\Operations;use ApiPlatform\Metadata\Resource\ResourceMetadataCollection;use phpDocumentor\Reflection\DocBlockFactory;use phpDocumentor\Reflection\DocBlockFactoryInterface;use phpDocumentor\Reflection\Types\ContextFactory;use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocNode;use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTextNode;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;/*** Extracts descriptions from PHPDoc.** @author Kévin Dunglas <dunglas@gmail.com>*/final class PhpDocResourceMetadataCollectionFactory implements ResourceMetadataCollectionFactoryInterface{private readonly ?DocBlockFactoryInterface $docBlockFactory;private readonly ?ContextFactory $contextFactory;private readonly ?PhpDocParser $phpDocParser;private readonly ?Lexer $lexer;/** @var array<string, PhpDocNode> */private array $docBlocks = [];public function __construct(private readonly ResourceMetadataCollectionFactoryInterface $decorated, ?DocBlockFactoryInterface $docBlockFactory = null){$contextFactory = null;if ($docBlockFactory instanceof DocBlockFactoryInterface) {trigger_deprecation('api-platform/core', '3.1', 'Using a 2nd argument to PhpDocResourceMetadataCollectionFactory is deprecated.');}if (class_exists(DocBlockFactory::class) && class_exists(ContextFactory::class)) {$docBlockFactory = $docBlockFactory ?? DocBlockFactory::createInstance();$contextFactory = new ContextFactory();}$this->docBlockFactory = $docBlockFactory;$this->contextFactory = $contextFactory;if (class_exists(DocBlockFactory::class) && !class_exists(PhpDocParser::class)) {trigger_deprecation('api-platform/core', '3.1', 'Using phpdocumentor/reflection-docblock is deprecated. Require phpstan/phpdoc-parser instead.');}$phpDocParser = null;$lexer = null;if (class_exists(PhpDocParser::class)) {$phpDocParser = new PhpDocParser(new TypeParser(new ConstExprParser()), new ConstExprParser());$lexer = new Lexer();}$this->phpDocParser = $phpDocParser;$this->lexer = $lexer;}/*** {@inheritdoc}*/public function create(string $resourceClass): ResourceMetadataCollection{$resourceMetadataCollection = $this->decorated->create($resourceClass);foreach ($resourceMetadataCollection as $key => $resourceMetadata) {if (null !== $resourceMetadata->getDescription()) {continue;}$description = null;// Deprecated path. To remove in API Platform 4.if (!$this->phpDocParser instanceof PhpDocParser && $this->docBlockFactory instanceof DocBlockFactoryInterface && $this->contextFactory) {$reflectionClass = new \ReflectionClass($resourceClass);try {$docBlock = $this->docBlockFactory->create($reflectionClass, $this->contextFactory->createFromReflector($reflectionClass));$description = $docBlock->getSummary();} catch (\InvalidArgumentException) {// Ignore empty DocBlocks}} else {$description = $this->getShortDescription($resourceClass);}if (!$description) {return $resourceMetadataCollection;}$resourceMetadataCollection[$key] = $resourceMetadata->withDescription($description);$operations = $resourceMetadata->getOperations() ?? new Operations();foreach ($operations as $operationName => $operation) {if (null !== $operation->getDescription()) {continue;}$operations->add($operationName, $operation->withDescription($description));}$resourceMetadataCollection[$key] = $resourceMetadataCollection[$key]->withOperations($operations);if (!$resourceMetadata->getGraphQlOperations()) {continue;}foreach ($graphQlOperations = $resourceMetadata->getGraphQlOperations() as $operationName => $operation) {if (null !== $operation->getDescription()) {continue;}$graphQlOperations[$operationName] = $operation->withDescription($description);}$resourceMetadataCollection[$key] = $resourceMetadataCollection[$key]->withGraphQlOperations($graphQlOperations);}return $resourceMetadataCollection;}/*** Gets the short description of the class.*/private function getShortDescription(string $class): ?string{if (!$docBlock = $this->getDocBlock($class)) {return null;}foreach ($docBlock->children as $docChild) {if ($docChild instanceof PhpDocTextNode && !empty($docChild->text)) {return $docChild->text;}}return null;}private function getDocBlock(string $class): ?PhpDocNode{if (isset($this->docBlocks[$class])) {return $this->docBlocks[$class];}try {$reflectionClass = new \ReflectionClass($class);} catch (\ReflectionException) {return null;}$rawDocNode = $reflectionClass->getDocComment();if (!$rawDocNode) {return null;}$tokens = new TokenIterator($this->lexer->tokenize($rawDocNode));$phpDocNode = $this->phpDocParser->parse($tokens);$tokens->consumeTokenType(Lexer::TOKEN_END);return $this->docBlocks[$class] = $phpDocNode;}}