Passed
Push — feature/0.7.0 ( 54b1aa...9c1bf9 )
by Ryuichi
44:30
created

AnnotationReader::getAnnotationInfoList()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 22
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 13
nc 5
nop 0
dl 0
loc 22
rs 8.6737
c 0
b 0
f 0
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\IMethod;
8
use WebStream\Annotation\Base\IMethods;
9
use WebStream\Annotation\Base\IProperty;
10
use WebStream\Annotation\Base\IRead;
11
use WebStream\Annotation\Reader\Extend\ExtendReader;
12
use WebStream\Container\Container;
13
use WebStream\DI\Injector;
14
use WebStream\Exception\Delegate\ExceptionDelegator;
15
use WebStream\Exception\Extend\AnnotationException;
16
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...
17
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...
18
19
/**
20
 * AnnotationReader
21
 * @author Ryuichi TANAKA.
22
 * @since 2014/05/10
23
 * @version 0.4
24
 */
25
class AnnotationReader
26
{
27
    use Injector;
28
29
    /**
30
     * @var \ReflectionClass リフレクションクラスオブジェクト
31
     */
32
    // private $refClass;
33
34
    /**
35
     * @var IAnnotatable インスタンス
36
     */
37
    private $instance;
38
39
    /**
40
     * @var Logger ロガー
41
     */
42
    // private $logger;
43
44
    /**
45
     * @var Container コンテナ
46
     */
47
    // private $container;
48
49
    /**
50
     * @var array<string> 読み込み可能アノテーション情報
51
     */
52
    private $readableMap;
53
54
    /**
55
     * @var array<ExtendReader> 拡張アノテーションリーダー
56
     */
57
    private $extendReaderMap;
58
59
    /**
60
     * @var array<string> アノテーション情報リスト
61
     */
62
    private $annotationInfoList;
63
64
    /**
65
     * @var array<string> アノテーション情報リスト(拡張リーダー処理済み)
66
     */
67
    private $annotationInfoExtendList;
68
69
    /**
70
     * @var callable 読み込み時の例外
71
     */
72
    private $exception;
73
74
    /**
75
     * @var string 読み込み対象アノテーションクラスパス
76
     */
77
    // private $annotationClasspath;
78
79
    /**
80
     * @var string アクションメソッド
81
     */
82
    private $actionMethod;
83
84
    /**
85
     * constructor
86
     * @param IAnnotatable ターゲットインスタンス
87
     * @param Container 依存コンテナ
88
     */
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...
89
    public function __construct(IAnnotatable $instance)
90
    {
91
        $this->initialize();
92
        $this->instance = $instance;
93
    }
94
95
    /**
96
     * 初期化処理
97
     */
98
    private function initialize()
99
    {
100
        $this->readableMap = [];
101
        $this->extendReaderMap = [];
102
        $this->annotationInfoList = [];
103
        $this->annotationInfoExtendList = [];
104
    }
105
106
    /**
107
     * アノテーション情報リストを返却する
108
     * @param array<mixed> アノテーション情報リスト
109
     */
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...
110
    public function getAnnotationInfoList(): array
111
    {
112
        if (empty($this->extendReaderMap)) {
113
            return $this->annotationInfoList;
114
        }
115
116
        if (!empty($this->annotationInfoExtendList)) {
117
            return $this->annotationInfoExtendList;
118
        }
119
120
        foreach ($this->annotationInfoList as $key => $annotationInfo) {
121
            if (!array_key_exists($key, $this->extendReaderMap)) {
122
                continue;
123
            }
124
            $readerClasspath = $this->extendReaderMap[$key];
125
            $refClass = new \ReflectionClass($readerClasspath);
126
            $reader = $refClass->newInstance();
127
            $reader->read($annotationInfo);
128
            $this->annotationInfoExtendList[$key] = $reader->getAnnotationInfo();
129
        }
130
131
        return $this->annotationInfoExtendList;
132
    }
133
134
    /**
135
     * 発生した例外を返却する
136
     * @param ExceptionDelegator 発生した例外
137
     */
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...
138
    public function getException()
139
    {
140
        return $this->exception;
141
    }
142
143
    /**
144
     * アクションメソッドを設定する
145
     * @param string アクションメソッド
146
     */
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...
147
    public function setActionMethod(string $actionMethod)
148
    {
149
        $this->actionMethod = $actionMethod;
150
    }
151
152
    /**
153
     * 読み込み可能アノテーション情報を設定する
154
     * @param string アノテーションクラスパス
155
     * @param Container アノテーションクラス依存コンテナ
156
     */
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...
157
    public function readable(string $classpath, Container $container = null)
158
    {
159
        $this->readableMap[$classpath] = $container;
160
    }
161
162
     /**
163
      * 拡張アノテーションリーダーを設定する
164
      * @param string アノテーションクラスパス
165
      * @param string 拡張アノテーションリーダークラスパス
166
      */
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...
167
    public function useExtendReader(string $annotationClasspath, string $readerClasspath)
168
    {
169
        $this->extendReaderMap[$annotationClasspath] = $readerClasspath;
170
    }
171
172
    /**
173
     * アノテーション情報を読み込む
174
     */
175
    public function read()
176
    {
177
        try {
178
            $this->readClass();
179
            $this->readMethod();
180
            $this->readProperty();
181
        } catch (DoctrineAnnotationException $e) {
182
            $this->initialize();
183
            throw new AnnotationException($e);
184
        }
185
    }
186
187
    /**
188
     * クラス情報を読み込む
189
     */
190
    public function readClass()
191
    {
192
        $reader = new DoctrineAnnotationReader();
193
        $refClass = new \ReflectionClass($this->instance);
194
195
        while ($refClass !== false) {
196
            $annotations = $reader->getClassAnnotations($refClass);
197
198
            if (!empty($annotations)) {
199
                for ($i = 0, $count = count($annotations); $i < $count; $i++) {
200
                    $annotation = $annotations[$i];
201
202
                    if (!$annotation instanceof IClass) {
203
                        continue;
204
                    }
205
206
                    $key = get_class($annotation);
207
                    if (!array_key_exists($key, $this->readableMap)) {
208
                        continue;
209
                    }
210
211
                    $container = $this->readableMap[$key];
212
213
                    try {
214
                        $annotation->onClassInject($this->instance, $refClass, $container);
0 ignored issues
show
Bug introduced by
$container of type string is incompatible with the type WebStream\Container\Container expected by parameter $container of WebStream\Annotation\Base\IClass::onClassInject(). ( Ignorable by Annotation )

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

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

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

278
                        $annotation->onMethodInject($this->instance, $refMethod, /** @scrutinizer ignore-type */ $container);
Loading history...
Bug introduced by
$container of type string is incompatible with the type WebStream\Container\Container expected by parameter $container of WebStream\Annotation\Bas...ethod::onMethodInject(). ( Ignorable by Annotation )

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

278
                        $annotation->onMethodInject($this->instance, $refMethod, /** @scrutinizer ignore-type */ $container);
Loading history...
279
                    } catch (\Exception $e) {
280
                        if ($this->exception === null) {
281
                            $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...
282
                        }
283
                        continue;
284
                    }
285
286
                    // IReadを実装している場合、任意のデータを返却する
287
                    if ($annotation instanceof IRead) {
288
                        if (!array_key_exists($key, $this->annotationInfoList)) {
289
                            $this->annotationInfoList[$key] = [];
290
                        }
291
                        $this->annotationInfoList[$key][] = $annotation->getAnnotationInfo();
292
                    }
293
                }
294
            }
295
296
            $refClass = $refClass->getParentClass();
297
        }
298
299
        // 拡張リーダー処理結果をクリアする
300
        $this->annotationInfoExtendList = [];
301
    }
302
303
    /**
304
     * プロパティ情報を読み込む
305
     */
306
    private function readProperty()
307
    {
308
        $reader = new DoctrineAnnotationReader();
309
        $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...
310
311
        while ($refClass !== false) {
312
            foreach ($refClass->getProperties() as $refProperty) {
313
                if ($refClass->getName() !== $refProperty->class) {
314
                    continue;
315
                }
316
317
                $annotations = $reader->getPropertyAnnotations($refProperty);
318
319
                // アノテーション定義がなければ次へ
320
                if (empty($annotations)) {
321
                    continue;
322
                }
323
324
                for ($i = 0, $count = count($annotations); $i < $count; $i++) {
325
                    $annotation = $annotations[$i];
326
                    // $annotation->inject('logger', $this->container->logger);
327
328
                    if (!$annotation instanceof IProperty) {
329
                        continue;
330
                    }
331
332
                    $key = get_class($annotation);
333
                    if (!array_key_exists($key, $this->readableMap)) {
334
                        continue;
335
                    }
336
337
                    $container = $this->readableMap[$key];
338
339
                    try {
340
                        $annotation->onPropertyInject($this->instance, $refProperty, $container);
0 ignored issues
show
Bug introduced by
$container of type string is incompatible with the type WebStream\Container\Container expected by parameter $container of WebStream\Annotation\Bas...rty::onPropertyInject(). ( Ignorable by Annotation )

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

340
                        $annotation->onPropertyInject($this->instance, $refProperty, /** @scrutinizer ignore-type */ $container);
Loading history...
341
                    } catch (\Exception $e) {
342
                        if ($this->exception === null) {
343
                            $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...
344
                        }
345
                        continue;
346
                    }
347
348
                    // IReadを実装している場合、任意のデータを返却する
349
                    if ($annotation instanceof IRead) {
350
                        if (!array_key_exists($key, $this->annotationInfoList)) {
351
                            $this->annotationInfoList[$key] = [];
352
                        }
353
                        $this->annotationInfoList[$key][] = $annotation->onInjected();
0 ignored issues
show
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

353
                        /** @scrutinizer ignore-call */ 
354
                        $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

353
                        /** @scrutinizer ignore-call */ 
354
                        $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...
354
                    }
355
                }
356
            }
357
358
            $refClass = $refClass->getParentClass();
359
        }
360
    }
361
}
362