Passed
Push — develop ( 3d1775...ebd477 )
by Mathieu
02:47
created

AbstractReader::getClassAnnotations()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 25
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 15
c 1
b 0
f 0
dl 0
loc 25
rs 9.7666
cc 3
nc 2
nop 2
1
<?php
2
3
namespace KunicMarko\SonataAnnotationBundle\Reader;
4
5
use Doctrine\Common\Annotations\Reader;
6
use KunicMarko\SonataAnnotationBundle\Annotation\AnnotationInterface;
7
use KunicMarko\SonataAnnotationBundle\Annotation\AssociationFieldInterface;
8
use KunicMarko\SonataAnnotationBundle\Annotation\RouteAnnotationInterface;
9
use KunicMarko\SonataAnnotationBundle\Exception\MissingAnnotationArgumentException;
10
use ReflectionClass;
11
use ReflectionMethod;
12
use ReflectionProperty;
13
14
/**
15
 * Annotation reader.
16
 */
17
abstract class AbstractReader
18
{
19
20
    /**
21
     * Doctrine annotation reader.
22
     *
23
     * @var Reader
24
     */
25
    protected Reader $annotationReader;
26
27
    /**
28
     * @param Reader $annotationReader Doctrine annotation reader.
29
     */
30
    public function __construct(Reader $annotationReader)
31
    {
32
        $this->annotationReader = $annotationReader;
33
    }
34
35
    /**
36
     * Check the given annotation is well-defined.
37
     *
38
     * @param AnnotationInterface $annotation The annotation.
39
     * @param ReflectionClass     $class      Class having the annotation.
40
     *
41
     * @return void
42
     */
43
    protected function checkAnnotationIntegrity(
44
        AnnotationInterface $annotation,
45
        ReflectionClass $class
46
    ): void {
47
        if ($annotation instanceof AssociationFieldInterface
48
            && !isset($annotation->field)) {
49
            throw new MissingAnnotationArgumentException(
50
                $annotation,
51
                'field',
52
            );
53
        }
54
55
        if ($annotation instanceof RouteAnnotationInterface
56
            && !isset($annotation->name)) {
57
            throw new MissingAnnotationArgumentException(
58
                $annotation,
59
                'name',
60
                $class
61
            );
62
        }
63
    }
64
65
    /**
66
     * Get a specific annotation for the given entity class.
67
     *
68
     * @param ReflectionClass $class      Entity class.
69
     * @param string          $annotation Annotation class.
70
     *
71
     * @return object|AnnotationInterface|null
72
     */
73
    protected function getClassAnnotation(
74
        ReflectionClass $class,
75
        string $annotation
76
    ): ?object {
77
        return $this->annotationReader->getClassAnnotation($class, $annotation);
78
    }
79
80
    /**
81
     * Get the list of annotations for a given entity class.
82
     *
83
     * @param ReflectionClass $class              Entity class.
84
     * @param ?string         $annotationClass    If given, filter annotation
85
     *                                            having the specified class.
86
     *
87
     * @return array<object|AnnotationInterface>
88
     */
89
    protected function getClassAnnotations(
90
        ReflectionClass $class,
91
        ?string $annotationClass = null
92
    ): array {
93
        $annotations = $annotationClass === null
94
            ? $this->annotationReader->getClassAnnotations($class)
95
            : array_filter(
96
                $this->annotationReader->getClassAnnotations($class),
97
                fn(
98
                    object $annotation
99
                ) => $annotation instanceof $annotationClass
100
            );
101
102
        array_walk(
103
            $annotations,
104
            function(object $annotation) use ($class) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space after FUNCTION keyword; 0 found
Loading history...
105
                $annotation instanceof AnnotationInterface
106
                && $this->checkAnnotationIntegrity(
107
                    $annotation,
108
                    $class
109
                );
110
            }
111
        );
112
113
        return $annotations;
114
    }
115
116
    /**
117
     * Get class methods annotations.
118
     *
119
     * @param ReflectionClass $class           Entity class.
120
     * @param ?string         $annotationClass Annotation class.
121
     *
122
     * @return array<string, object|AnnotationInterface[]>
123
     */
124
    protected function getClassMethodsAnnotations(
125
        ReflectionClass $class,
126
        ?string $annotationClass = null
127
    ): array {
128
        $annotations = [];
129
130
        foreach ($class->getMethods() as $method) {
131
            $annotations[$method->getName()] = $this->getMethodAnnotations(
132
                $method,
133
                $annotationClass
0 ignored issues
show
Bug introduced by
It seems like $annotationClass can also be of type null; however, parameter $annotationClass of KunicMarko\SonataAnnotat...:getMethodAnnotations() 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

133
                /** @scrutinizer ignore-type */ $annotationClass
Loading history...
134
            );
135
        }
136
137
        return $annotations;
138
    }
139
140
    /**
141
     * Get class properties annotations.
142
     *
143
     * @param ReflectionClass $class           Entity class.
144
     * @param ?string         $annotationClass Annotation class.
145
     *
146
     * @return array<string, object|AnnotationInterface[]>
147
     */
148
    protected function getClassPropertiesAnnotations(
149
        ReflectionClass $class,
150
        ?string $annotationClass = null
151
    ): array {
152
        $annotations = [];
153
154
        foreach ($class->getProperties() as $property) {
155
            $annotations[$property->getName()] = $this->getPropertyAnnotations(
156
                $property,
157
                $annotationClass
158
            );
159
        }
160
161
        return $annotations;
162
    }
163
164
    /**
165
     * Get a method annotation.
166
     *
167
     * @param ReflectionMethod $method     Method.
168
     * @param string           $annotation Annotation class.
169
     *
170
     * @return object|AnnotationInterface|null
171
     */
172
    protected function getMethodAnnotation(
173
        ReflectionMethod $method,
174
        string $annotation
175
    ): ?object {
176
        return $this->annotationReader->getMethodAnnotation(
177
            $method,
178
            $annotation
179
        );
180
    }
181
182
    /**
183
     * Get the list of annotations for a given method.
184
     *
185
     * @param ReflectionMethod $method          Method.
186
     * @param string           $annotationClass Filter annotation having the
187
     *                                          specified class.
188
     *
189
     * @return array<object|AnnotationInterface>
190
     */
191
    protected function getMethodAnnotations(
192
        ReflectionMethod $method,
193
        string $annotationClass
194
    ): array {
195
        $annotations = array_filter(
196
            $this->annotationReader->getMethodAnnotations($method),
197
            fn(
198
                object $annotation
199
            ) => $annotation instanceof $annotationClass
200
        );
201
202
        array_walk(
203
            $annotations,
204
            function(object $annotation) use ($method) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space after FUNCTION keyword; 0 found
Loading history...
205
                $annotation instanceof AnnotationInterface
206
                && $this->checkAnnotationIntegrity(
207
                    $annotation,
208
                    $method->getDeclaringClass()
209
                );
210
            }
211
        );
212
213
        return $annotations;
214
    }
215
216
    /**
217
     * Get the list of annotations for a given property.
218
     *
219
     * @param ReflectionProperty $property        Property.
220
     * @param ?string            $annotationClass If given, filter annotation
221
     *                                            having the specified class.
222
     *
223
     * @return array<object|AnnotationInterface>
224
     */
225
    protected function getPropertyAnnotations(
226
        ReflectionProperty $property,
227
        ?string $annotationClass = null
228
    ): array {
229
        $annotations = $annotationClass === null
230
            ? $this->annotationReader->getPropertyAnnotations($property)
231
            : array_filter(
232
                $this->annotationReader->getPropertyAnnotations($property),
233
                fn(
234
                    object $annotation
235
                ) => $annotation instanceof $annotationClass
236
            );
237
238
        array_walk(
239
            $annotations,
240
            function(object $annotation) use ($property) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space after FUNCTION keyword; 0 found
Loading history...
241
                $annotation instanceof AnnotationInterface
242
                && $this->checkAnnotationIntegrity(
243
                    $annotation,
244
                    $property->getDeclaringClass()
245
                );
246
            }
247
        );
248
249
        return $annotations;
250
    }
251
252
}