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

tochka-developers / jsonrpc / 4135501466

pending completion
4135501466

push

github

darkdarin
Merge remote-tracking branch 'origin/v5.0'

209 of 813 new or added lines in 51 files covered. (25.71%)

233 of 1307 relevant lines covered (17.83%)

1.84 hits per line

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

0.0
/src/Support/JsonRpcDocBlock.php
1
<?php
2

3
namespace Tochka\JsonRpc\Support;
4

5
use phpDocumentor\Reflection\DocBlock;
6
use phpDocumentor\Reflection\DocBlock\Tag;
7
use phpDocumentor\Reflection\DocBlockFactory;
8
use phpDocumentor\Reflection\Types\ContextFactory;
9
use Tochka\JsonRpc\Contracts\AnnotationReaderInterface;
10

11
/**
12
 * @psalm-type Attributes = object
13
 * @psalm-api
14
 */
15
class JsonRpcDocBlock
16
{
17
    private ?DocBlock $docBlock = null;
18
    private AnnotationReaderInterface $annotationReader;
19
    private \Reflector $reflector;
20
    private DocBlockFactory $docBlockFactory;
21

22
    public function __construct(
23
        \Reflector $reflector,
24
        AnnotationReaderInterface $annotationReader,
25
        DocBlockFactory $docBlockFactory
26
    ) {
27
        $this->reflector = $reflector;
×
28
        $this->annotationReader = $annotationReader;
×
29
        $this->docBlockFactory = $docBlockFactory;
×
30

31
        if (!method_exists($reflector, 'getDocComment')) {
×
32
            return;
×
33
        }
34

35
        /** @var string|false $docComment */
36
        $docComment = $reflector->getDocComment();
×
NEW
37
        if ($docComment === false) {
×
38
            return;
×
39
        }
40

41
        $phpDocContext = (new ContextFactory())->createFromReflector($reflector);
×
42
        $this->docBlock = $this->docBlockFactory->create($docComment, $phpDocContext);
×
43
    }
44

45
    public function getSummary(): ?string
46
    {
47
        if (
48
            !empty($this->docBlock)
×
49
            && (
50
                $this->reflector instanceof \ReflectionMethod
×
51
                || $this->reflector instanceof \ReflectionClass
×
52
            )
53
        ) {
54
            return $this->docBlock->getSummary() ?: null;
×
55
        }
56

57
        if (
58
            $this->reflector instanceof \ReflectionProperty
×
59
            || $this->reflector instanceof \ReflectionClassConstant
×
60
        ) {
61
            if (!empty($this->docBlock)) {
×
62
                $summary = $this->docBlock->getSummary() ?: null;
×
63
                if (!empty($summary)) {
×
64
                    return $summary;
×
65
                }
66
            }
67

68
            $tag = $this->firstTag(DocBlock\Tags\Var_::class);
×
69

NEW
70
            return $tag?->getDescription()?->getBodyTemplate();
×
71
        }
72

73
        if ($this->reflector instanceof \ReflectionParameter) {
×
74
            if (!empty($this->docBlock)) {
×
75
                $summary = $this->docBlock->getSummary() ?: null;
×
76
                if (!empty($summary)) {
×
77
                    return $summary;
×
78
                }
79
            }
80

81
            $reflectionMethod = $this->reflector->getDeclaringFunction();
×
82
            $docComment = $reflectionMethod->getDocComment();
×
83
            $phpDocContext = (new ContextFactory())->createFromReflector($reflectionMethod);
×
84
            $docBlock = $this->docBlockFactory->create($docComment, $phpDocContext);
×
NEW
85
            $reflectionParameterName = $this->reflector->getName();
×
86

87
            $tag = $this->firstTag(
×
88
                DocBlock\Tags\Param::class,
×
NEW
89
                fn (DocBlock\Tags\Param $tag) => $tag->getVariableName() === $reflectionParameterName,
×
90
                $docBlock
×
91
            );
×
92

NEW
93
            return $tag?->getDescription()?->getBodyTemplate();
×
94
        }
95

96
        return null;
×
97
    }
98

99
    public function getDescription(): ?string
100
    {
101
        if (empty($this->docBlock)) {
×
102
            return null;
×
103
        }
104

105
        $description = $this->docBlock->getDescription()->getBodyTemplate();
×
106
        if (!empty($description)) {
×
107
            return $description;
×
108
        }
109

110
        $summary = $this->docBlock->getSummary();
×
111

112
        if (!empty($summary)) {
×
113
            return $summary;
×
114
        }
115

116
        return $this->getSummary();
×
117
    }
118

119
    /**
120
     * @template T of Tag
121
     * @param class-string<T> $tagClassName
122
     * @return bool
123
     */
124
    public function hasTag(string $tagClassName): bool
125
    {
126
        return $this->firstTag($tagClassName) !== null;
×
127
    }
128

129
    /**
130
     * @template T of Tag
131
     * @param class-string<T>|null $tagClassName
132
     * @param callable|null $filter
133
     * @param DocBlock|null $docBlock
134
     * @return T|null
135
     */
136
    public function firstTag(
137
        ?string $tagClassName = null,
138
        callable $filter = null,
139
        ?DocBlock $docBlock = null
140
    ): ?Tag {
141
        $docBlock = $docBlock ?: $this->docBlock;
×
142

143
        if ($docBlock === null) {
×
144
            return null;
×
145
        }
146

147
        $tags = $docBlock->getTags();
×
148

149
        foreach ($tags as $tag) {
×
150
            if ($tagClassName !== null && !$tag instanceof $tagClassName) {
×
151
                continue;
×
152
            }
153

154
            /** @var T $tag */
155

156
            if ($filter !== null && !$filter($tag)) {
×
157
                continue;
×
158
            }
159

160
            return $tag;
×
161
        }
162

163
        return null;
×
164
    }
165

166
    /**
167
     * @template T of Tag
168
     * @param class-string<T>|null $tagClassName
169
     * @param callable(T):bool|null $filter
170
     * @param DocBlock|null $docBlock
171
     * @return array<T>
172
     */
173
    public function getTags(?string $tagClassName = null, callable $filter = null, ?DocBlock $docBlock = null): array
174
    {
175
        $docBlock = $docBlock ?: $this->docBlock;
×
176

177
        if ($docBlock === null) {
×
178
            return [];
×
179
        }
180

181
        /** @var array<T> $tags */
182
        $tags = $docBlock->getTags();
×
NEW
183
        return array_filter(
×
NEW
184
            $tags,
×
NEW
185
            static function (DocBlock\Tag $tag) use ($tagClassName, $filter) {
×
NEW
186
                if ($tagClassName !== null && !$tag instanceof $tagClassName) {
×
NEW
187
                    return false;
×
188
                }
189

190
                /** @var T $tag */
191

NEW
192
                if ($filter !== null) {
×
NEW
193
                    return $filter($tag);
×
194
                }
195

NEW
196
                return true;
×
197
            }
×
NEW
198
        );
×
199
    }
200

201
    /**
202
     * @template T of Attributes
203
     * @param class-string<T> $annotationClassName
204
     * @return bool
205
     */
206
    public function hasAnnotation(string $annotationClassName): bool
207
    {
208
        return $this->firstAnnotation($annotationClassName) !== null;
×
209
    }
210

211
    /**
212
     * @template T of Attributes
213
     * @param class-string<T>|null $annotationClassName
214
     * @param callable(T):bool|null $filter
215
     * @return array<T>
216
     */
217
    public function getAnnotations(?string $annotationClassName = null, callable $filter = null): array
218
    {
NEW
219
        $annotations = $this->getAnnotationsByReflector();
×
220

221
        $result = [];
×
222

223
        foreach ($annotations as $annotation) {
×
NEW
224
            if ($annotationClassName !== null && !$annotation instanceof $annotationClassName) {
×
NEW
225
                continue;
×
226
            }
227

228
            /** @var T $annotation */
229
            if ($filter !== null && !$filter($annotation)) {
×
230
                continue;
×
231
            }
232

233
            $result[] = $annotation;
×
234
        }
235

236
        return $result;
×
237
    }
238

239
    /**
240
     * @template T of Attributes
241
     * @param class-string<T> $annotationClassName
242
     * @param callable(T):bool|null $filter
243
     * @return T|null
244
     */
245
    public function firstAnnotation(string $annotationClassName, callable $filter = null): ?object
246
    {
NEW
247
        $annotations = $this->getAnnotationsByReflector();
×
248

249
        foreach ($annotations as $annotation) {
×
NEW
250
            if (!$annotation instanceof $annotationClassName) {
×
NEW
251
                continue;
×
252
            }
253

NEW
254
            if ($filter !== null && !$filter($annotation)) {
×
NEW
255
                continue;
×
256
            }
257

NEW
258
            return $annotation;
×
259
        }
260

261
        return null;
×
262
    }
263

264
    /**
265
     * @return iterable<Attributes>
266
     */
267
    private function getAnnotationsByReflector(): iterable
268
    {
269
        if ($this->reflector instanceof \ReflectionClass) {
×
NEW
270
            return $this->annotationReader->getClassMetadata($this->reflector);
×
NEW
271
        } elseif ($this->reflector instanceof \ReflectionProperty) {
×
NEW
272
            return $this->annotationReader->getPropertyMetadata($this->reflector);
×
NEW
273
        } elseif ($this->reflector instanceof \ReflectionFunctionAbstract) {
×
NEW
274
            return $this->annotationReader->getFunctionMetadata($this->reflector);
×
NEW
275
        } elseif ($this->reflector instanceof \ReflectionParameter) {
×
NEW
276
            return $this->annotationReader->getParameterMetadata($this->reflector);
×
NEW
277
        } elseif ($this->reflector instanceof \ReflectionClassConstant) {
×
NEW
278
            return $this->annotationReader->getConstantMetadata($this->reflector);
×
279
        } else {
NEW
280
            return [];
×
281
        }
282
    }
283

284
    public function getReflector(): \Reflector
285
    {
286
        return $this->reflector;
×
287
    }
288
}
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