Passed
Push — master ( 020c57...bead09 )
by Satoshi
02:21
created

DependencyGraphBuilder/ExtraPhpDocTagResolver.php (1 issue)

Labels
Severity
1
<?php
2
declare(strict_types=1);
3
4
namespace DependencyAnalyzer\DependencyGraphBuilder;
5
6
use DependencyAnalyzer\DependencyDumper;
7
use DependencyAnalyzer\DependencyGraph\ExtraPhpDocTagResolver\DepsInternal;
8
use DependencyAnalyzer\DependencyGraph\FullyQualifiedStructuralElementName as FQSEN;
9
use DependencyAnalyzer\Exceptions\InvalidFullyQualifiedStructureElementNameException;
10
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
11
use PHPStan\PhpDocParser\Lexer\Lexer;
12
use PHPStan\PhpDocParser\Parser\PhpDocParser;
13
use PHPStan\PhpDocParser\Parser\TokenIterator;
14
use PHPStan\Reflection\ClassReflection;
15
16
class ExtraPhpDocTagResolver
17
{
18
    const ONLY_USED_BY_TAGS = '@canOnlyUsedBy';
19
    const DEPENDER_TAGS = '@dependee';
20
    const DEPENDEE_TAGS = '@dependee';
21
22
    /**
23
     * @var Lexer
24
     */
25
    protected $phpDocLexer;
26
27
    /**
28
     * @var PhpDocParser
29
     */
30
    protected $phpDocParser;
31
32
    /**
33
     * @var ObserverInterface
34
     */
35
    protected $observer = null;
36
37
    public function __construct(Lexer $phpDocLexer, PhpDocParser $phpDocParser)
38
    {
39
        $this->phpDocLexer = $phpDocLexer;
40
        $this->phpDocParser = $phpDocParser;
41
    }
42
43
    public function setObserver(ObserverInterface $observer): void
44
    {
45
        $this->observer = $observer;
46
    }
47
48
    protected function notifyError(string $file, string $fqsen, InvalidFullyQualifiedStructureElementNameException $exception)
49
    {
50
        if (!is_null($this->observer)) {
51
            $this->observer->notifyResolvePhpDocError($file, $fqsen, $exception);
52
        }
53
    }
54
55
    public function collectExtraPhpDocs(\ReflectionClass $reflectionClass)
56
    {
57
//        $this->resolveInternalTag($reflectionClass);
58
        $this->resolveDepsInternalTag($reflectionClass);
59
//        $this->resolveUsesTag($reflectionClass);
60
    }
61
62
    /**
63
     * @param \ReflectionClass $reflectionClass
64
     * @return DepsInternal[]
65
     */
66
    public function resolveDepsInternalTag(\ReflectionClass $reflectionClass): array
67
    {
68
        $ret = [];
69
70
        if ($this->haveTag($reflectionClass, DepsInternal::TAG_NAME)) {
71
            try {
72
                $ret[] = new DepsInternal(
73
                    FQSEN::createClass($reflectionClass->getName()),
74
                    $this->resolve($reflectionClass->getDocComment(), DepsInternal::TAG_NAME)
0 ignored issues
show
It seems like $reflectionClass->getDocComment() can also be of type boolean; however, parameter $phpdoc of DependencyAnalyzer\Depen...cTagResolver::resolve() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

74
                    $this->resolve(/** @scrutinizer ignore-type */ $reflectionClass->getDocComment(), DepsInternal::TAG_NAME)
Loading history...
75
                );
76
            } catch (InvalidFullyQualifiedStructureElementNameException $e) {
77
                $this->notifyError($reflectionClass->getFileName(), $reflectionClass->getName(), $e);
78
            }
79
        }
80
81
        foreach ($reflectionClass->getProperties() as $reflectionProperty) {
82
            if ($this->haveTag($reflectionProperty, DepsInternal::TAG_NAME)) {
83
                try {
84
                    $ret[] = new DepsInternal(
85
                        FQSEN::createProperty($reflectionClass->getName(), $reflectionProperty->getName()),
86
                        $this->resolve($reflectionProperty->getDocComment(), DepsInternal::TAG_NAME)
87
                    );
88
                } catch (InvalidFullyQualifiedStructureElementNameException $e) {
89
                    $this->notifyError($reflectionClass->getFileName(), $reflectionClass->getName(), $e);
90
                }
91
            }
92
        }
93
94
        foreach ($reflectionClass->getMethods() as $reflectionMethod) {
95
            if ($this->haveTag($reflectionMethod, DepsInternal::TAG_NAME)) {
96
                try {
97
                    $ret[] = new DepsInternal(
98
                        FQSEN::createMethod($reflectionClass->getName(), $reflectionMethod->getName()),
99
                        $this->resolve($reflectionMethod->getDocComment(), DepsInternal::TAG_NAME)
100
                    );
101
                } catch (InvalidFullyQualifiedStructureElementNameException $e) {
102
                    $this->notifyError($reflectionClass->getFileName(), $reflectionClass->getName(), $e);
103
                }
104
            }
105
        }
106
107
        foreach ($reflectionClass->getReflectionConstants() as $reflectionClassConstant) {
108
            if ($this->haveTag($reflectionClassConstant, DepsInternal::TAG_NAME)) {
109
                try {
110
                    $ret[] = new DepsInternal(
111
                        FQSEN::createClassConstant($reflectionClass->getName(), $reflectionClassConstant->getName()),
112
                        $this->resolve($reflectionClassConstant->getDocComment(), DepsInternal::TAG_NAME)
113
                    );
114
                } catch (InvalidFullyQualifiedStructureElementNameException $e) {
115
                    $this->notifyError($reflectionClass->getFileName(), $reflectionClass->getName(), $e);
116
                }
117
            }
118
        }
119
120
        return $ret;
121
    }
122
123
    protected function resolveInternalTag(\ReflectionClass $reflectionClass)
124
    {
125
        $phpDocs = [];
126
127
        $phpDocs[] = $reflectionClass->getDocComment();
128
        foreach ($reflectionClass->getProperties() as $reflectionProperty) {
129
            $phpDocs[] = $reflectionProperty->getDocComment();
130
        }
131
        foreach ($reflectionClass->getMethods() as $reflectionMethod) {
132
            $phpDocs[] = $reflectionMethod->getDocComment();
133
        }
134
        foreach ($reflectionClass->getReflectionConstants() as $reflectionClassConstant) {
135
            $phpDocs[] = $reflectionClassConstant->getDocComment();
136
        }
137
138
139
    }
140
141
142
    protected function resolveUsesTag(\ReflectionClass $reflectionClass)
143
    {
144
        $reflectionClass->getDocComment();
145
    }
146
147
    /**
148
     * @param \ReflectionClass $reflectionClass
149
     * @return DepsInternal[]
150
     */
151
    public function resolveCanOnlyUsedByTag(\ReflectionClass $reflectionClass): array
152
    {
153
        $ret = [];
154
        if ($this->haveTag($reflectionClass, self::ONLY_USED_BY_TAGS)) {
155
            try {
156
                $ret[] = new DepsInternal(
157
                    FQSEN::createClass($reflectionClass->getName()),
158
                    $this->resolve($reflectionClass->getDocComment(), self::ONLY_USED_BY_TAGS)
159
                );
160
            } catch (InvalidFullyQualifiedStructureElementNameException $e) {
161
                $this->notifyError($reflectionClass->getFileName(), $reflectionClass->getName(), $e);
162
            }
163
        }
164
165
        return $ret;
166
    }
167
168
    /**
169
     * @param ClassReflection $classReflection
170
     * @return string[]
171
     */
172
    public function resolveDependerTag(ClassReflection $classReflection): array
173
    {
174
        if ($phpdoc = $classReflection->getNativeReflection()->getDocComment()) {
175
            return $this->resolve($phpdoc, self::DEPENDER_TAGS);
176
        }
177
178
        return [];
179
    }
180
181
    /**
182
     * @param ClassReflection $classReflection
183
     * @return string[]
184
     */
185
    public function resolveDependeeTag(ClassReflection $classReflection): array
186
    {
187
        if ($phpdoc = $classReflection->getNativeReflection()->getDocComment()) {
188
            return $this->resolve($phpdoc, self::DEPENDEE_TAGS);
189
        }
190
191
        return [];
192
    }
193
194
    protected function haveTag(\Reflector $reflector, string $tag): bool
195
    {
196
        if (!method_exists($reflector, 'getDocComment') && $reflector->getDocComment() !== false) {
197
            return false;
198
        } elseif (($docComment = $reflector->getDocComment()) === false) {
199
            return false;
200
    }
201
202
        $tokens = new TokenIterator($this->phpDocLexer->tokenize($reflector->getDocComment()));
203
        $phpDocNode = $this->phpDocParser->parse($tokens);
204
205
        foreach ($phpDocNode->getTagsByName($tag) as $phpDocTagNode) {
206
            /** @var PhpDocTagNode $phpDocTagNode */
207
            if (preg_match('/^' . $tag . '/', $phpDocTagNode->__toString()) === 1) {
208
                return true;
209
            }
210
        }
211
212
        return false;
213
    }
214
215
    protected function resolve(string $phpdoc, string $tag): array
216
    {
217
        $tokens = new TokenIterator($this->phpDocLexer->tokenize($phpdoc));
218
        $phpDocNode = $this->phpDocParser->parse($tokens);
219
220
        $ret = [];
221
        foreach ($phpDocNode->getTagsByName($tag) as $phpDocTagNode) {
222
            /** @var PhpDocTagNode $phpDocTagNode */
223
            if (preg_match('/^' . $tag . '\s+(.+)$/', $phpDocTagNode->__toString(), $matches) === 1) {
224
                $ret[] = $matches[1];
225
            }
226
        };
227
228
        return $ret;
229
    }
230
}
231