• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

api-platform / core / 10903050455

17 Sep 2024 12:29PM UTC coverage: 7.684% (+0.7%) from 6.96%
10903050455

push

github

web-flow
fix: swagger ui with route identifier (#6616)

2 of 6 new or added lines in 6 files covered. (33.33%)

9000 existing lines in 286 files now uncovered.

12668 of 164863 relevant lines covered (7.68%)

22.93 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

85.71
/src/Metadata/Resource/Factory/PhpDocResourceMetadataCollectionFactory.php
1
<?php
2

3
/*
4
 * This file is part of the API Platform project.
5
 *
6
 * (c) Kévin Dunglas <dunglas@gmail.com>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11

12
declare(strict_types=1);
13

14
namespace ApiPlatform\Metadata\Resource\Factory;
15

16
use ApiPlatform\Metadata\Operations;
17
use ApiPlatform\Metadata\Resource\ResourceMetadataCollection;
18
use phpDocumentor\Reflection\DocBlockFactory;
19
use phpDocumentor\Reflection\DocBlockFactoryInterface;
20
use phpDocumentor\Reflection\Types\ContextFactory;
21
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocNode;
22
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTextNode;
23
use PHPStan\PhpDocParser\Lexer\Lexer;
24
use PHPStan\PhpDocParser\Parser\ConstExprParser;
25
use PHPStan\PhpDocParser\Parser\PhpDocParser;
26
use PHPStan\PhpDocParser\Parser\TokenIterator;
27
use PHPStan\PhpDocParser\Parser\TypeParser;
28

29
/**
30
 * Extracts descriptions from PHPDoc.
31
 *
32
 * @author Kévin Dunglas <dunglas@gmail.com>
33
 */
34
final class PhpDocResourceMetadataCollectionFactory implements ResourceMetadataCollectionFactoryInterface
35
{
36
    private readonly ?DocBlockFactoryInterface $docBlockFactory;
37
    private readonly ?ContextFactory $contextFactory;
38
    private readonly ?PhpDocParser $phpDocParser;
39
    private readonly ?Lexer $lexer;
40

41
    /** @var array<string, PhpDocNode> */
42
    private array $docBlocks = [];
43

44
    public function __construct(private readonly ResourceMetadataCollectionFactoryInterface $decorated, ?DocBlockFactoryInterface $docBlockFactory = null)
45
    {
UNCOV
46
        $contextFactory = null;
2,258✔
UNCOV
47
        if ($docBlockFactory instanceof DocBlockFactoryInterface) {
2,258✔
48
            trigger_deprecation('api-platform/core', '3.1', 'Using a 2nd argument to PhpDocResourceMetadataCollectionFactory is deprecated.');
×
49
        }
UNCOV
50
        if (class_exists(DocBlockFactory::class) && class_exists(ContextFactory::class)) {
2,258✔
UNCOV
51
            $docBlockFactory = $docBlockFactory ?? DocBlockFactory::createInstance();
2,258✔
UNCOV
52
            $contextFactory = new ContextFactory();
2,258✔
53
        }
UNCOV
54
        $this->docBlockFactory = $docBlockFactory;
2,258✔
UNCOV
55
        $this->contextFactory = $contextFactory;
2,258✔
UNCOV
56
        if (class_exists(DocBlockFactory::class) && !class_exists(PhpDocParser::class)) {
2,258✔
57
            trigger_deprecation('api-platform/core', '3.1', 'Using phpdocumentor/reflection-docblock is deprecated. Require phpstan/phpdoc-parser instead.');
×
58
        }
UNCOV
59
        $phpDocParser = null;
2,258✔
UNCOV
60
        $lexer = null;
2,258✔
UNCOV
61
        if (class_exists(PhpDocParser::class)) {
2,258✔
UNCOV
62
            $phpDocParser = new PhpDocParser(new TypeParser(new ConstExprParser()), new ConstExprParser());
2,258✔
UNCOV
63
            $lexer = new Lexer();
2,258✔
64
        }
UNCOV
65
        $this->phpDocParser = $phpDocParser;
2,258✔
UNCOV
66
        $this->lexer = $lexer;
2,258✔
67
    }
68

69
    /**
70
     * {@inheritdoc}
71
     */
72
    public function create(string $resourceClass): ResourceMetadataCollection
73
    {
UNCOV
74
        $resourceMetadataCollection = $this->decorated->create($resourceClass);
42✔
75

UNCOV
76
        foreach ($resourceMetadataCollection as $key => $resourceMetadata) {
42✔
UNCOV
77
            if (null !== $resourceMetadata->getDescription()) {
34✔
UNCOV
78
                continue;
3✔
79
            }
80

UNCOV
81
            $description = null;
34✔
82

83
            // Deprecated path. To remove in API Platform 4.
UNCOV
84
            if (!$this->phpDocParser instanceof PhpDocParser && $this->docBlockFactory instanceof DocBlockFactoryInterface && $this->contextFactory) {
34✔
85
                $reflectionClass = new \ReflectionClass($resourceClass);
×
86

87
                try {
88
                    $docBlock = $this->docBlockFactory->create($reflectionClass, $this->contextFactory->createFromReflector($reflectionClass));
×
89
                    $description = $docBlock->getSummary();
×
90
                } catch (\InvalidArgumentException) {
×
91
                    // Ignore empty DocBlocks
92
                }
93
            } else {
UNCOV
94
                $description = $this->getShortDescription($resourceClass);
34✔
95
            }
96

UNCOV
97
            if (!$description) {
34✔
UNCOV
98
                return $resourceMetadataCollection;
21✔
99
            }
100

UNCOV
101
            $resourceMetadataCollection[$key] = $resourceMetadata->withDescription($description);
18✔
102

UNCOV
103
            $operations = $resourceMetadata->getOperations() ?? new Operations();
18✔
UNCOV
104
            foreach ($operations as $operationName => $operation) {
18✔
UNCOV
105
                if (null !== $operation->getDescription()) {
18✔
106
                    continue;
×
107
                }
108

UNCOV
109
                $operations->add($operationName, $operation->withDescription($description));
18✔
110
            }
111

UNCOV
112
            $resourceMetadataCollection[$key] = $resourceMetadataCollection[$key]->withOperations($operations);
18✔
113

UNCOV
114
            if (!$resourceMetadata->getGraphQlOperations()) {
18✔
UNCOV
115
                continue;
10✔
116
            }
117

UNCOV
118
            foreach ($graphQlOperations = $resourceMetadata->getGraphQlOperations() as $operationName => $operation) {
18✔
UNCOV
119
                if (null !== $operation->getDescription()) {
18✔
UNCOV
120
                    continue;
16✔
121
                }
122

UNCOV
123
                $graphQlOperations[$operationName] = $operation->withDescription($description);
18✔
124
            }
125

UNCOV
126
            $resourceMetadataCollection[$key] = $resourceMetadataCollection[$key]->withGraphQlOperations($graphQlOperations);
18✔
127
        }
128

UNCOV
129
        return $resourceMetadataCollection;
26✔
130
    }
131

132
    /**
133
     * Gets the short description of the class.
134
     */
135
    private function getShortDescription(string $class): ?string
136
    {
UNCOV
137
        if (!$docBlock = $this->getDocBlock($class)) {
34✔
UNCOV
138
            return null;
21✔
139
        }
140

UNCOV
141
        foreach ($docBlock->children as $docChild) {
18✔
UNCOV
142
            if ($docChild instanceof PhpDocTextNode && !empty($docChild->text)) {
18✔
UNCOV
143
                return $docChild->text;
18✔
144
            }
145
        }
146

UNCOV
147
        return null;
4✔
148
    }
149

150
    private function getDocBlock(string $class): ?PhpDocNode
151
    {
UNCOV
152
        if (isset($this->docBlocks[$class])) {
34✔
UNCOV
153
            return $this->docBlocks[$class];
10✔
154
        }
155

156
        try {
UNCOV
157
            $reflectionClass = new \ReflectionClass($class);
34✔
158
        } catch (\ReflectionException) {
×
159
            return null;
×
160
        }
161

UNCOV
162
        $rawDocNode = $reflectionClass->getDocComment();
34✔
163

UNCOV
164
        if (!$rawDocNode) {
34✔
UNCOV
165
            return null;
21✔
166
        }
167

UNCOV
168
        $tokens = new TokenIterator($this->lexer->tokenize($rawDocNode));
18✔
UNCOV
169
        $phpDocNode = $this->phpDocParser->parse($tokens);
18✔
UNCOV
170
        $tokens->consumeTokenType(Lexer::TOKEN_END);
18✔
171

UNCOV
172
        return $this->docBlocks[$class] = $phpDocNode;
18✔
173
    }
174
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc