Passed
Branch master (046a17)
by Mathieu
02:22
created

AbstractReader   A

Complexity

Total Complexity 23

Size/Duplication

Total Lines 254
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 23
eloc 77
dl 0
loc 254
rs 10
c 0
b 0
f 0

10 Methods

Rating   Name   Duplication   Size   Complexity  
A getMethodAnnotation() 0 7 1
A getMethodAnnotations() 0 23 2
A getPropertyAnnotations() 0 25 3
A checkAnnotationIntegrity() 0 18 5
A getClassPropertiesAnnotations() 0 14 2
A getAnnotationFieldName() 0 9 2
A getClassAnnotation() 0 5 1
A getClassAnnotations() 0 25 3
A getClassMethodsAnnotations() 0 16 3
A __construct() 0 3 1
1
<?php
2
3
namespace Neimheadh\SonataAnnotationBundle\Reader;
4
5
use Doctrine\Common\Annotations\Reader;
6
use Neimheadh\SonataAnnotationBundle\Annotation\AnnotationInterface;
7
use Neimheadh\SonataAnnotationBundle\Annotation\AssociationFieldInterface;
8
use Neimheadh\SonataAnnotationBundle\Annotation\RouteAnnotationInterface;
9
use Neimheadh\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 the field name for the given annotation.
67
     *
68
     * @param string $baseName   Annotation base name.
69
     * @param object $annotation Annotation object.
70
     *
71
     * @return string
72
     */
73
    protected function getAnnotationFieldName(
74
        string $baseName,
75
        object $annotation
76
    ): string {
77
        if ($annotation instanceof AssociationFieldInterface) {
78
            return sprintf('%s.%s', $baseName, $annotation->field);
79
        }
80
81
        return $baseName;
82
    }
83
84
    /**
85
     * Get a specific annotation for the given entity class.
86
     *
87
     * @param ReflectionClass $class      Entity class.
88
     * @param string          $annotation Annotation class.
89
     *
90
     * @return object|AnnotationInterface|null
91
     */
92
    protected function getClassAnnotation(
93
        ReflectionClass $class,
94
        string $annotation
95
    ): ?object {
96
        return $this->annotationReader->getClassAnnotation($class, $annotation);
97
    }
98
99
    /**
100
     * Get the list of annotations for a given entity class.
101
     *
102
     * @param ReflectionClass $class              Entity class.
103
     * @param ?string         $annotationClass    If given, filter annotation
104
     *                                            having the specified class.
105
     *
106
     * @return array<object|AnnotationInterface>
107
     */
108
    protected function getClassAnnotations(
109
        ReflectionClass $class,
110
        ?string $annotationClass = null
111
    ): array {
112
        $annotations = $annotationClass === null
113
            ? $this->annotationReader->getClassAnnotations($class)
114
            : array_filter(
115
                $this->annotationReader->getClassAnnotations($class),
116
                fn(
117
                    object $annotation
118
                ) => $annotation instanceof $annotationClass
119
            );
120
121
        array_walk(
122
            $annotations,
123
            function (object $annotation) use ($class) {
124
                $annotation instanceof AnnotationInterface
125
                && $this->checkAnnotationIntegrity(
126
                    $annotation,
127
                    $class
128
                );
129
            }
130
        );
131
132
        return $annotations;
133
    }
134
135
    /**
136
     * Get class methods annotations.
137
     *
138
     * @param ReflectionClass $class           Entity class.
139
     * @param ?string         $annotationClass Annotation class.
140
     *
141
     * @return array<string, object|AnnotationInterface[]>
142
     */
143
    protected function getClassMethodsAnnotations(
144
        ReflectionClass $class,
145
        ?string $annotationClass = null
146
    ): array {
147
        $annotations = [];
148
149
        if ($annotationClass !== null) {
150
            foreach ($class->getMethods() as $method) {
151
                $annotations[$method->getName()] = $this->getMethodAnnotations(
152
                    $method,
153
                    $annotationClass
154
                );
155
            }
156
        }
157
158
        return $annotations;
159
    }
160
161
    /**
162
     * Get class properties annotations.
163
     *
164
     * @param ReflectionClass $class           Entity class.
165
     * @param ?string         $annotationClass Annotation class.
166
     *
167
     * @return array<string, object|AnnotationInterface[]>
168
     */
169
    protected function getClassPropertiesAnnotations(
170
        ReflectionClass $class,
171
        ?string $annotationClass = null
172
    ): array {
173
        $annotations = [];
174
175
        foreach ($class->getProperties() as $property) {
176
            $annotations[$property->getName()] = $this->getPropertyAnnotations(
177
                $property,
178
                $annotationClass
179
            );
180
        }
181
182
        return $annotations;
183
    }
184
185
    /**
186
     * Get a method annotation.
187
     *
188
     * @param ReflectionMethod $method     Method.
189
     * @param string           $annotation Annotation class.
190
     *
191
     * @return object|AnnotationInterface|null
192
     */
193
    protected function getMethodAnnotation(
194
        ReflectionMethod $method,
195
        string $annotation
196
    ): ?object {
197
        return $this->annotationReader->getMethodAnnotation(
198
            $method,
199
            $annotation
200
        );
201
    }
202
203
    /**
204
     * Get the list of annotations for a given method.
205
     *
206
     * @param ReflectionMethod $method          Method.
207
     * @param string           $annotationClass Filter annotation having the
208
     *                                          specified class.
209
     *
210
     * @return array<object|AnnotationInterface>
211
     */
212
    protected function getMethodAnnotations(
213
        ReflectionMethod $method,
214
        string $annotationClass
215
    ): array {
216
        $annotations = array_filter(
217
            $this->annotationReader->getMethodAnnotations($method),
218
            fn(
219
                object $annotation
220
            ) => $annotation instanceof $annotationClass
221
        );
222
223
        array_walk(
224
            $annotations,
225
            function (object $annotation) use ($method) {
226
                $annotation instanceof AnnotationInterface
227
                && $this->checkAnnotationIntegrity(
228
                    $annotation,
229
                    $method->getDeclaringClass()
230
                );
231
            }
232
        );
233
234
        return $annotations;
235
    }
236
237
    /**
238
     * Get the list of annotations for a given property.
239
     *
240
     * @param ReflectionProperty $property        Property.
241
     * @param ?string            $annotationClass If given, filter annotation
242
     *                                            having the specified class.
243
     *
244
     * @return array<object|AnnotationInterface>
245
     */
246
    protected function getPropertyAnnotations(
247
        ReflectionProperty $property,
248
        ?string $annotationClass = null
249
    ): array {
250
        $annotations = $annotationClass === null
251
            ? $this->annotationReader->getPropertyAnnotations($property)
252
            : array_filter(
253
                $this->annotationReader->getPropertyAnnotations($property),
254
                fn(
255
                    object $annotation
256
                ) => $annotation instanceof $annotationClass
257
            );
258
259
        array_walk(
260
            $annotations,
261
            function (object $annotation) use ($property) {
262
                $annotation instanceof AnnotationInterface
263
                && $this->checkAnnotationIntegrity(
264
                    $annotation,
265
                    $property->getDeclaringClass()
266
                );
267
            }
268
        );
269
270
        return $annotations;
271
    }
272
273
}