AttributeProcessor   A
last analyzed

Complexity

Total Complexity 31

Size/Duplication

Total Lines 217
Duplicated Lines 9.68 %

Coupling/Cohesion

Components 1
Dependencies 5

Test Coverage

Coverage 91.57%

Importance

Changes 0
Metric Value
wmc 31
lcom 1
cbo 5
dl 21
loc 217
ccs 76
cts 83
cp 0.9157
rs 9.92
c 0
b 0
f 0

9 Methods

Rating   Name   Duplication   Size   Complexity  
A process() 0 12 3
A processProperty() 0 14 3
A processMethod() 0 14 3
A validateMethodAttribute() 0 16 3
A createAttributeByProperty() 0 25 5
A processAttributeOptions() 0 14 4
A createAttributeByMethod() 0 18 4
A resolveNameByMethod() 0 10 2
A processDataType() 21 21 4

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
declare(strict_types = 1);
3
4
namespace Mikemirten\Component\JsonApi\Mapper\Definition\AnnotationProcessor;
5
6
use Mikemirten\Component\JsonApi\Mapper\Definition\Annotation\Attribute as AttributeAnnotation;
7
use Mikemirten\Component\JsonApi\Mapper\Definition\Definition;
8
use Mikemirten\Component\JsonApi\Mapper\Definition\Attribute;
9
10
/**
11
 * Processor of attributes
12
 *
13
 * @package Mikemirten\Component\JsonApi\Mapper\Definition\AnnotationProcessor
14
 */
15
class AttributeProcessor extends AbstractProcessor
16
{
17
    /**
18
     * Pattern of "type" parameter of attribute annotation
19
     */
20
    const DATATYPE_PATTERN = '~^(?<type>[a-z_][a-z0-9_]*(?:\.[a-z_][a-z0-9_]*)*)\s*(?:\((?<params>[^\)]*)\))?(?<many>\[\])?$~i';
21
22
    /**
23
     * Process annotations
24
     *
25
     * @param \ReflectionClass $reflection
26
     * @param Definition       $definition
27
     */
28 20
    public function process(\ReflectionClass $reflection, Definition $definition)
29
    {
30 20
        foreach ($reflection->getProperties() as $property)
31
        {
32 18
            $this->processProperty($property, $definition);
33
        }
34
35 19
        foreach ($reflection->getMethods() as $method)
36
        {
37 19
            $this->processMethod($method, $definition);
38
        }
39 19
    }
40
41
    /**
42
     * Process property of class
43
     *
44
     * @param \ReflectionProperty $property
45
     * @param Definition          $definition
46
     */
47 18
    protected function processProperty(\ReflectionProperty $property, Definition $definition)
48
    {
49 18
        $annotations = $this->reader->getPropertyAnnotations($property);
50
51 18
        foreach ($annotations as $annotation)
52
        {
53 18
            if ($annotation instanceof AttributeAnnotation) {
54 18
                $attribute = $this->createAttributeByProperty($annotation, $property);
55
56 17
                $definition->addAttribute($attribute);
57 17
                continue;
58
            }
59
        }
60 17
    }
61
62
    /**
63
     * Process method of class
64
     *
65
     * @param \ReflectionMethod $method
66
     * @param Definition        $definition
67
     */
68 19
    protected function processMethod(\ReflectionMethod $method, Definition $definition)
69
    {
70 19
        $annotations = $this->reader->getMethodAnnotations($method);
71
72 19
        foreach ($annotations as $annotation)
73
        {
74 2
            if ($annotation instanceof AttributeAnnotation) {
75 2
                $this->validateMethodAttribute($annotation, $method);
76
77 2
                $attribute = $this->createAttributeByMethod($annotation, $method);
78 2
                $definition->addAttribute($attribute);
79
            }
80
        }
81 19
    }
82
83
    /**
84
     * Validate method with attribute definition
85
     *
86
     * @param  AttributeAnnotation $annotation
87
     * @param  \ReflectionMethod   $method
88
     * @throws \LogicException
89
     */
90 2
    protected function validateMethodAttribute(AttributeAnnotation $annotation, \ReflectionMethod $method)
91
    {
92 2
        if (! $method->isPublic()) {
93
            throw new \LogicException(sprintf(
94
                'Attribute annotation can be applied only to non public method "%s".',
95
                $method->getName()
0 ignored issues
show
Bug introduced by
Consider using $method->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
96
            ));
97
        }
98
99 2
        if ($annotation->getter !== null) {
100
            throw new \LogicException(sprintf(
101
                'The "getter" property of Attribute annotation applied to method "%s" is useless.',
102
                $method->getName()
0 ignored issues
show
Bug introduced by
Consider using $method->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
103
            ));
104
        }
105 2
    }
106
107
    /**
108
     * Create attribute by annotation of property
109
     *
110
     * @param  AttributeAnnotation $annotation
111
     * @param  \ReflectionProperty $property
112
     * @return Attribute
113
     */
114 18
    protected function createAttributeByProperty(AttributeAnnotation $annotation, \ReflectionProperty $property): Attribute
115
    {
116 18
        $name = ($annotation->name === null)
117 18
            ? $property->getName()
118 18
            : $annotation->name;
119
120 18
        $getter = ($annotation->getter === null)
121 18
            ? $this->resolveGetter($property)
122 18
            : $annotation->getter;
123
124 18
        $setter = ($annotation->setter === null)
125 18
            ? $this->resolveSetter($property)
126 18
            : $annotation->setter;
127
128 18
        $attribute = new Attribute($name, $getter);
129 18
        $attribute->setPropertyName($property->getName());
130
131 18
        if ($setter !== null) {
132 2
            $attribute->setSetter($setter);
133
        }
134
135 18
        $this->processAttributeOptions($annotation, $attribute);
136
137 17
        return $attribute;
138
    }
139
140
    /**
141
     * Process optional properties of attribute
142
     *
143
     * @param AttributeAnnotation $annotation
144
     * @param Attribute           $attribute
145
     */
146 18
    protected function processAttributeOptions(AttributeAnnotation $annotation, Attribute $attribute)
147
    {
148 18
        if ($annotation->type !== null) {
149 17
            $this->processDataType($annotation->type, $attribute);
150
        }
151
152 17
        if ($annotation->many !== null) {
153 1
            $attribute->setMany($annotation->many);
154
        }
155
156 17
        if ($annotation->processNull !== null) {
157 2
            $attribute->setProcessNull($annotation->processNull);
158
        }
159 17
    }
160
161
    /**
162
     * Create attribute by annotation of method
163
     *
164
     * @param  AttributeAnnotation $annotation
165
     * @param  \ReflectionMethod   $method
166
     * @return Attribute
167
     */
168 2
    protected function createAttributeByMethod(AttributeAnnotation $annotation, \ReflectionMethod $method): Attribute
169
    {
170 2
        $name = ($annotation->name === null)
171 2
            ? $this->resolveNameByMethod($method)
172 2
            : $annotation->name;
173
174 2
        $attribute = new Attribute($name, $method->getName());
0 ignored issues
show
Bug introduced by
Consider using $method->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
175
176 2
        if ($annotation->setter !== null) {
177 2
            $attribute->setSetter($annotation->setter);
178
        }
179
180 2
        if ($annotation->type !== null) {
181 1
            $this->processDataType($annotation->type, $attribute);
182
        }
183
184 2
        return $attribute;
185
    }
186
187
    /**
188
     * Resolve name of attribute by method
189
     *
190
     * @param  \ReflectionMethod $method
191
     * @return string
192
     */
193 2
    protected function resolveNameByMethod(\ReflectionMethod $method): string
194
    {
195 2
        $name = $method->getName();
0 ignored issues
show
Bug introduced by
Consider using $method->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
196
197 2
        if (preg_match('~^(?:get|is)(?<name>[a-z0-9_]+)~i', $name, $matches)) {
198 2
            return lcfirst($matches['name']);
199
        }
200
201
        return $name;
202
    }
203
204
    /**
205
     * Process data-type
206
     *
207
     * @param string    $definition
208
     * @param Attribute $attribute
209
     */
210 18 View Code Duplication
    protected function processDataType(string $definition, Attribute $attribute)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
211
    {
212 18
        if (! preg_match(self::DATATYPE_PATTERN, $definition, $matches)) {
213 1
            throw new \LogicException(sprintf('Data-type definition "%s" is invalid.', $definition));
214
        }
215
216 17
        $attribute->setType($matches['type']);
217
218 17
        if (! empty($matches['many'])) {
219 5
            $attribute->setMany();
220
        }
221
222 17
        if (empty($matches['params'])) {
223 10
            return;
224
        }
225
226 7
        $parameters = explode(',', $matches['params']);
227 7
        $parameters = array_map('trim', $parameters);
228
229 7
        $attribute->setTypeParameters($parameters);
230
    }
231
}