AnnotationReader   B
last analyzed

Complexity

Total Complexity 52

Size/Duplication

Total Lines 325
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 135
dl 0
loc 325
rs 7.44
c 0
b 0
f 0
wmc 52

11 Methods

Rating   Name   Duplication   Size   Complexity  
A initialize() 0 6 1
A readable() 0 3 1
A getAnnotationInfoList() 0 18 4
A __construct() 0 4 1
B readClass() 0 48 11
A useExtendReader() 0 3 1
C readProperty() 0 57 13
C readMethod() 0 63 16
A read() 0 9 2
A getException() 0 3 1
A setActionMethod() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like AnnotationReader often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use AnnotationReader, and based on these observations, apply Extract Interface, too.

1
<?php
2
namespace WebStream\Annotation\Reader;
3
4
use WebStream\Annotation\Base\IAnnotatable;
5
use WebStream\Annotation\Base\Annotation;
6
use WebStream\Annotation\Base\IClass;
7
use WebStream\Annotation\Base\IExtension;
8
use WebStream\Annotation\Base\IMethod;
9
use WebStream\Annotation\Base\IMethods;
10
use WebStream\Annotation\Base\IProperty;
11
use WebStream\Annotation\Base\IRead;
12
use WebStream\Annotation\Reader\Extend\ExtendReader;
13
use WebStream\Container\Container;
14
use WebStream\DI\Injector;
15
use WebStream\Exception\Delegate\ExceptionDelegator;
16
use WebStream\Exception\Extend\AnnotationException;
17
use Doctrine\Common\Annotations\AnnotationReader as DoctrineAnnotationReader;
0 ignored issues
show
Bug introduced by
The type Doctrine\Common\Annotations\AnnotationReader was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
18
use Doctrine\Common\Annotations\AnnotationException as DoctrineAnnotationException;
0 ignored issues
show
Bug introduced by
The type Doctrine\Common\Annotations\AnnotationException was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
19
20
/**
21
 * AnnotationReader
22
 * @author Ryuichi TANAKA.
23
 * @since 2014/05/10
24
 * @version 0.4
25
 */
26
class AnnotationReader
27
{
28
    use Injector;
29
30
    /**
31
     * @var IAnnotatable インスタンス
32
     */
33
    private $instance;
34
35
    /**
36
     * @var array<string> 読み込み可能アノテーション情報
37
     */
38
    private $readableMap;
39
40
    /**
41
     * @var array<ExtendReader> 拡張アノテーションリーダー
42
     */
43
    private $extendReaderMap;
44
45
    /**
46
     * @var array<string> アノテーション情報リスト
47
     */
48
    private $annotationInfoList;
49
50
    /**
51
     * @var array<string> アノテーション情報リスト(拡張リーダー処理済み)
52
     */
53
    private $annotationInfoExtendList;
0 ignored issues
show
introduced by
The private property $annotationInfoExtendList is not used, and could be removed.
Loading history...
54
55
    /**
56
     * @var callable 読み込み時の例外
57
     */
58
    private $exception;
59
60
    /**
61
     * @var string アクションメソッド
62
     */
63
    private $actionMethod;
64
65
    /**
66
     * @var Container デフォルト依存コンテナ
67
     */
68
    private $defaultContainer;
69
70
    /**
71
     * constructor
72
     * @param IAnnotatable ターゲットインスタンス
0 ignored issues
show
Documentation Bug introduced by
The doc comment ターゲットインスタンス at position 0 could not be parsed: Unknown type name 'ターゲットインスタンス' at position 0 in ターゲットインスタンス.
Loading history...
73
     */
74
    public function __construct(IAnnotatable $instance)
75
    {
76
        $this->initialize();
77
        $this->instance = $instance;
78
    }
79
80
    /**
81
     * 初期化処理
82
     */
83
    private function initialize()
84
    {
85
        $this->readableMap = [];
86
        $this->extendReaderMap = [];
87
        $this->annotationInfoList = [];
88
        $this->defaultContainer = new Container(false);
89
    }
90
91
    /**
92
     * アノテーション情報リストを返却する
93
     * @param array<mixed> アノテーション情報リスト
0 ignored issues
show
Documentation Bug introduced by
The doc comment アノテーション情報リスト at position 0 could not be parsed: Unknown type name 'アノテーション情報リスト' at position 0 in アノテーション情報リスト.
Loading history...
94
     */
95
    public function getAnnotationInfoList(): array
96
    {
97
        if (empty($this->extendReaderMap)) {
98
            return $this->annotationInfoList;
99
        }
100
101
        foreach ($this->annotationInfoList as $key => $annotationInfo) {
102
            if (!array_key_exists($key, $this->extendReaderMap)) {
103
                continue;
104
            }
105
            $readerClasspath = $this->extendReaderMap[$key];
106
            $refClass = new \ReflectionClass($readerClasspath);
107
            $reader = $refClass->newInstance();
108
            $reader->read($annotationInfo);
109
            $this->annotationInfoList[$key] = $reader->getAnnotationInfo();
110
        }
111
112
        return $this->annotationInfoList;
113
    }
114
115
    /**
116
     * 発生した例外を返却する
117
     * @param ExceptionDelegator 発生した例外
0 ignored issues
show
Documentation Bug introduced by
The doc comment 発生した例外 at position 0 could not be parsed: Unknown type name '発生した例外' at position 0 in 発生した例外.
Loading history...
118
     */
119
    public function getException()
120
    {
121
        return $this->exception;
122
    }
123
124
    /**
125
     * アクションメソッドを設定する
126
     * @param string アクションメソッド
0 ignored issues
show
Documentation Bug introduced by
The doc comment アクションメソッド at position 0 could not be parsed: Unknown type name 'アクションメソッド' at position 0 in アクションメソッド.
Loading history...
127
     */
128
    public function setActionMethod(string $actionMethod)
129
    {
130
        $this->actionMethod = $actionMethod;
131
    }
132
133
    /**
134
     * 読み込み可能アノテーション情報を設定する
135
     * @param string アノテーションクラスパス
0 ignored issues
show
Documentation Bug introduced by
The doc comment アノテーションクラスパス at position 0 could not be parsed: Unknown type name 'アノテーションクラスパス' at position 0 in アノテーションクラスパス.
Loading history...
136
     * @param Container アノテーションクラス依存コンテナ
137
     */
138
    public function readable(string $classpath, Container $container = null)
139
    {
140
        $this->readableMap[$classpath] = $container;
141
    }
142
143
     /**
144
      * 拡張アノテーションリーダーを設定する
145
      * @param string アノテーションクラスパス
0 ignored issues
show
Documentation Bug introduced by
The doc comment アノテーションクラスパス at position 0 could not be parsed: Unknown type name 'アノテーションクラスパス' at position 0 in アノテーションクラスパス.
Loading history...
146
      * @param string 拡張アノテーションリーダークラスパス
147
      */
148
    public function useExtendReader(string $annotationClasspath, string $readerClasspath)
149
    {
150
        $this->extendReaderMap[$annotationClasspath] = $readerClasspath;
151
    }
152
153
    /**
154
     * アノテーション情報を読み込む
155
     */
156
    public function read()
157
    {
158
        try {
159
            $this->readClass();
160
            $this->readMethod();
161
            $this->readProperty();
162
        } catch (DoctrineAnnotationException $e) {
163
            $this->initialize();
164
            throw new AnnotationException($e);
165
        }
166
    }
167
168
    /**
169
     * クラス情報を読み込む
170
     */
171
    public function readClass()
172
    {
173
        $reader = new DoctrineAnnotationReader();
174
        $refClass = new \ReflectionClass($this->instance);
175
176
        while ($refClass !== false) {
177
            $annotations = $reader->getClassAnnotations($refClass);
178
179
            if (!empty($annotations)) {
180
                for ($i = 0, $count = count($annotations); $i < $count; $i++) {
181
                    $annotation = $annotations[$i];
182
183
                    if (!$annotation instanceof IClass) {
184
                        continue;
185
                    }
186
187
                    $key = get_class($annotation);
188
                    $container = null;
189
                    if (!array_key_exists($key, $this->readableMap)) {
190
                        if ($annotation instanceof IExtension) {
191
                            $container = $this->defaultContainer;
192
                        } else {
193
                            continue;
194
                        }
195
                    } else {
196
                        $container = $this->readableMap[$key];
197
                    }
198
199
                    try {
200
                        $annotation->onClassInject($this->instance, $refClass, $container);
201
                    } catch (\Exception $e) {
202
                        if ($this->exception === null) {
203
                            $this->exception = new ExceptionDelegator($this->instance, $e);
0 ignored issues
show
Documentation Bug introduced by
It seems like new WebStream\Exception\...or($this->instance, $e) of type WebStream\Exception\Delegate\ExceptionDelegator is incompatible with the declared type callable of property $exception.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
204
                        }
205
                        continue;
206
                    }
207
208
                    // IReadを実装している場合、任意のデータを返却する
209
                    if ($annotation instanceof IRead) {
210
                        if (!array_key_exists($key, $this->annotationInfoList)) {
211
                            $this->annotationInfoList[$key] = [];
212
                        }
213
                        $this->annotationInfoList[$key][] = $annotation->getAnnotationInfo();
214
                    }
215
                }
216
            }
217
218
            $refClass = $refClass->getParentClass();
219
        }
220
    }
221
222
    /**
223
     * メソッド情報を読み込む
224
     */
225
    public function readMethod()
226
    {
227
        $reader = new DoctrineAnnotationReader();
228
        $refClass = new \ReflectionClass($this->instance);
229
230
        while ($refClass !== false) {
231
            foreach ($refClass->getMethods() as $refMethod) {
232
                if ($refClass->getName() !== $refMethod->class) {
233
                    continue;
234
                }
235
236
                $annotations = $reader->getMethodAnnotations($refMethod);
237
                if (empty($annotations)) {
238
                    continue;
239
                }
240
241
                for ($i = 0, $count = count($annotations); $i < $count; $i++) {
242
                    $annotation = $annotations[$i];
243
244
                    if (!$annotation instanceof IMethod && !$annotation instanceof IMethods) {
245
                        continue;
246
                    }
247
248
                    // IMethodを実装している場合、アクションメソッドのアノテーション以外は読み込まない
249
                    // PHPのメソッドは大文字小文字を区別しないため、そのまま比較するとルーティング解決結果と実際のメソッド名が合わないケースがある
250
                    // PHPの仕様に合わせてメソッド名の文字列比較は小文字に変換してから行う
251
                    if ($annotation instanceof IMethod && strtolower($this->actionMethod) !== strtolower($refMethod->name)) {
252
                        continue;
253
                    }
254
255
                    // 読み込み可能なアノテーション以外は読み込まない
256
                    $key = get_class($annotation);
257
                    $container = null;
258
                    if (!array_key_exists($key, $this->readableMap)) {
259
                        if ($annotation instanceof IExtension) {
260
                            $container = $this->defaultContainer;
261
                        } else {
262
                            continue;
263
                        }
264
                    } else {
265
                        $container = $this->readableMap[$key];
266
                    }
267
268
                    try {
269
                        $annotation->onMethodInject($this->instance, $refMethod, $container);
270
                    } catch (\Exception $e) {
271
                        if ($this->exception === null) {
272
                            $this->exception = new ExceptionDelegator($this->instance, $e, $this->actionMethod);
0 ignored issues
show
Documentation Bug introduced by
It seems like new WebStream\Exception\...e, $this->actionMethod) of type WebStream\Exception\Delegate\ExceptionDelegator is incompatible with the declared type callable of property $exception.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
273
                        }
274
                        continue;
275
                    }
276
277
                    // IReadを実装している場合、任意のデータを返却する
278
                    if ($annotation instanceof IRead) {
279
                        if (!array_key_exists($key, $this->annotationInfoList)) {
280
                            $this->annotationInfoList[$key] = [];
281
                        }
282
                        $this->annotationInfoList[$key][] = $annotation->getAnnotationInfo();
283
                    }
284
                }
285
            }
286
287
            $refClass = $refClass->getParentClass();
288
        }
289
    }
290
291
    /**
292
     * プロパティ情報を読み込む
293
     */
294
    private function readProperty()
295
    {
296
        $reader = new DoctrineAnnotationReader();
297
        $refClass = $this->refClass;
0 ignored issues
show
Bug Best Practice introduced by
The property refClass does not exist on WebStream\Annotation\Reader\AnnotationReader. Since you implemented __get, consider adding a @property annotation.
Loading history...
298
299
        while ($refClass !== false) {
300
            foreach ($refClass->getProperties() as $refProperty) {
301
                if ($refClass->getName() !== $refProperty->class) {
302
                    continue;
303
                }
304
305
                $annotations = $reader->getPropertyAnnotations($refProperty);
306
307
                // アノテーション定義がなければ次へ
308
                if (empty($annotations)) {
309
                    continue;
310
                }
311
312
                for ($i = 0, $count = count($annotations); $i < $count; $i++) {
313
                    $annotation = $annotations[$i];
314
315
                    if (!$annotation instanceof IProperty) {
316
                        continue;
317
                    }
318
319
                    $key = get_class($annotation);
320
                    $container = null;
321
                    if (!array_key_exists($key, $this->readableMap)) {
322
                        if ($annotation instanceof IExtension) {
323
                            $container = $this->defaultContainer;
324
                        } else {
325
                            continue;
326
                        }
327
                    } else {
328
                        $container = $this->readableMap[$key];
329
                    }
330
331
                    try {
332
                        $annotation->onPropertyInject($this->instance, $refProperty, $container);
333
                    } catch (\Exception $e) {
334
                        if ($this->exception === null) {
335
                            $this->exception = new ExceptionDelegator($this->instance, $e);
0 ignored issues
show
Documentation Bug introduced by
It seems like new WebStream\Exception\...or($this->instance, $e) of type WebStream\Exception\Delegate\ExceptionDelegator is incompatible with the declared type callable of property $exception.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
336
                        }
337
                        continue;
338
                    }
339
340
                    // IReadを実装している場合、任意のデータを返却する
341
                    if ($annotation instanceof IRead) {
342
                        if (!array_key_exists($key, $this->annotationInfoList)) {
343
                            $this->annotationInfoList[$key] = [];
344
                        }
345
                        $this->annotationInfoList[$key][] = $annotation->onInjected();
0 ignored issues
show
Bug introduced by
The method onInjected() does not exist on WebStream\Annotation\Base\IExtension. ( Ignorable by Annotation )

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

345
                        /** @scrutinizer ignore-call */ 
346
                        $this->annotationInfoList[$key][] = $annotation->onInjected();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method onInjected() does not exist on WebStream\Annotation\Base\IProperty. ( Ignorable by Annotation )

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

345
                        /** @scrutinizer ignore-call */ 
346
                        $this->annotationInfoList[$key][] = $annotation->onInjected();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method onInjected() does not exist on WebStream\Annotation\Base\IRead. ( Ignorable by Annotation )

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

345
                        /** @scrutinizer ignore-call */ 
346
                        $this->annotationInfoList[$key][] = $annotation->onInjected();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
346
                    }
347
                }
348
            }
349
350
            $refClass = $refClass->getParentClass();
351
        }
352
    }
353
}
354