Completed
Push — master ( 5f432b...5d5b15 )
by Jens
15:50 queued 05:48
created

ClassAnnotator::annotate()   D

Complexity

Conditions 11
Paths 216

Size

Total Lines 51
Code Lines 34

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 132

Importance

Changes 0
Metric Value
dl 0
loc 51
ccs 0
cts 43
cp 0
rs 4.9724
c 0
b 0
f 0
cc 11
eloc 34
nc 216
nop 0
crap 132

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * @author @jayS-de <[email protected]>
4
 */
5
6
namespace Commercetools\Core\Helper\Annotate;
7
8
use Commercetools\Core\Model\Common\JsonObject;
9
use Commercetools\Core\Request\AbstractApiRequest;
10
11
class ClassAnnotator
12
{
13
    /**
14
     * @var ReflectedClass
15
     */
16
    protected $class;
17
18
    protected $fields;
19
20
    public function __construct($className)
21
    {
22
        $this->class = new ReflectedClass($className);
23
    }
24
25
    /**
26
     * @return array
27
     */
28
    protected function reflectFields()
29
    {
30
        $reflectionClass = new \ReflectionClass($this->class->getClassName());
31
        if (!$reflectionClass->hasMethod('fieldDefinitions')) {
32
            return;
33
        }
34
        $reflectionMethod = $reflectionClass->getMethod('fieldDefinitions');
35
36
        $classObject = $reflectionClass->newInstanceWithoutConstructor();
37
        $this->fields = $reflectionMethod->invoke($classObject);
38
39
        foreach ($this->fields as $fieldName => $field) {
40
            $fieldType = '';
41
            if (isset($field[JsonObject::TYPE])) {
42
                $fieldType = $field[JsonObject::TYPE];
43
            }
44
            if (isset($field[JsonObject::DECORATOR])) {
45
                $getReturnType = $field[JsonObject::DECORATOR];
46
                $this->class->addUse($field[JsonObject::DECORATOR]);
47
            } else {
48
                if (empty($fieldType)) {
49
                    $getReturnType = 'mixed';
50
                } else {
51
                    $getReturnType = $fieldType;
52
                }
53
            }
54
            $getReturnTypeParts = explode('\\', trim($getReturnType, '\\'));
55
            if (!$this->isPrimitive($fieldType) && count($getReturnTypeParts) > 1) {
56
                $getReturnClassName = array_pop($getReturnTypeParts);
57
            } else {
58
                $getReturnClassName = $getReturnType;
59
            }
60
            if ($this->isOptional($field)) {
61
                $optional = ' = null';
62
            } else {
63
                $optional = '';
64
            }
65
66
            $fieldTypeParts = explode('\\', trim($fieldType, '\\'));
67
            if (!$this->isPrimitive($fieldType) && count($fieldTypeParts) > 1) {
68
                $this->class->addUse($fieldType);
69
                $fieldType = array_pop($fieldTypeParts);
70
            }
71
72
            $args = [trim($fieldType . ' $' . $fieldName . $optional)];
73
74
            $this->class->addMagicGetSetMethod('get', $fieldName, [], $getReturnClassName);
75
            $this->class->addMagicGetSetMethod('set', $fieldName, $args, $this->class->getShortClassName());
76
        }
77
    }
78
79
    protected function reflectReference()
80
    {
81
        $reflectionClass = new \ReflectionClass($this->class->getClassName());
82
        if (!$reflectionClass->isSubclassOf('Commercetools\Core\Model\Common\Resource')) {
83
            return;
84
        }
85
86
        $referenceClass = $this->class->getClassName() . 'Reference';
87
        $referenceShortName = $this->class->getShortClassName() . 'Reference';
88
        if (class_exists($referenceClass)) {
89
            $this->class->addMagicMethod(
90
                'getReference',
91
                [],
92
                $referenceShortName,
93
                null,
94
                null,
95
                false,
96
                true
97
            );
98
        }
99
    }
100
101
    protected function reflectElementType()
102
    {
103
        $reflectionClass = new \ReflectionClass($this->class->getClassName());
104
        if (!$reflectionClass->hasMethod('getType')) {
105
            return;
106
        }
107
        $reflectionMethod = $reflectionClass->getMethod('getType');
108
109
        $classObject = $reflectionClass->newInstanceWithoutConstructor();
110
        $elementType = $reflectionMethod->invoke($classObject);
111
112
        if ($elementType && !$this->isPrimitive($elementType)) {
113
            $elementTypeClass = new \ReflectionClass($elementType);
114
            $this->class->addUse($elementType);
115
            $getAtMethod = $reflectionClass->getMethod('getAt');
116 View Code Duplication
            if ($getAtMethod->getDeclaringClass()->getName() != $this->class->getClassName()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
117
                $this->class->addMagicMethod(
118
                    'getAt',
119
                    ['$offset'],
120
                    $elementTypeClass->getShortName(),
121
                    null,
122
                    null,
123
                    false,
124
                    true
125
                );
126
            }
127
            $addMethod = $reflectionClass->getMethod('add');
128 View Code Duplication
            if ($addMethod->getDeclaringClass()->getName() != $this->class->getClassName()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
129
                $this->class->addMagicMethod(
130
                    'add',
131
                    [$elementTypeClass->getShortName() . ' $element'],
132
                    $reflectionClass->getShortName(),
133
                    null,
134
                    null,
135
                    false,
136
                    true
137
                );
138
            }
139
            $current = $reflectionClass->getMethod('current');
140 View Code Duplication
            if ($current->getDeclaringClass()->getName() != $this->class->getClassName()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
141
                $this->class->addMagicMethod(
142
                    'current',
143
                    [],
144
                    $elementTypeClass->getShortName(),
145
                    null,
146
                    null,
147
                    false,
148
                    true
149
                );
150
            }
151
        }
152
    }
153
154
    protected function reflectResultClass()
155
    {
156
        $reflectionClass = new \ReflectionClass($this->class->getClassName());
157
        if (!$reflectionClass->hasMethod('getResultClass')) {
158
            return;
159
        }
160
        $reflectionMethod = $reflectionClass->getMethod('getResultClass');
161
162
        $classObject = $reflectionClass->newInstanceWithoutConstructor();
163
        $resultClass = $reflectionMethod->invoke($classObject);
164
165
        $resultClassReflection = new \ReflectionClass($resultClass);
166
        $this->class->addUse($resultClass);
167
        $mapResponseMethod = $reflectionClass->getMethod('mapResponse');
168 View Code Duplication
        if ($mapResponseMethod->getDeclaringClass()->getName() != $this->class->getClassName()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
169
            $this->class->addUse('\Commercetools\Core\Response\ApiResponseInterface');
170
            $this->class->addMagicMethod(
171
                'mapResponse',
172
                ['ApiResponseInterface $response'],
173
                $resultClassReflection->getShortName(),
174
                null,
175
                null,
176
                false,
177
                true
178
            );
179
        }
180
    }
181
182
    /**
183
     *
184
     */
185
    public function generate()
186
    {
187
        if ($this->class->isAbstract()) {
188
            return;
189
        }
190
191
        $this->reflectFields();
192
        $this->reflectReference();
193
        $this->annotate();
194
    }
195
196
    public function generateCurrentMethod()
197
    {
198
        if ($this->class->isAbstract()) {
199
            return;
200
        }
201
202
        $this->reflectElementType();
203
        $this->annotate();
204
    }
205
206
    public function generateMapResponseMethod()
207
    {
208
        if ($this->class->isAbstract()) {
209
            return;
210
        }
211
212
        $this->reflectResultClass();
213
        $this->annotate();
214
    }
215
216
    /**
217
     * @param $field
218
     * @return bool
219
     */
220
    protected function isOptional($field)
221
    {
222
        if (!isset($field['optional'])) {
223
            return true;
224
        } elseif (isset($field['optional']) && $field['optional'] == true) {
225
            return true;
226
        }
227
228
        return false;
229
    }
230
231
232
    protected function annotate()
233
    {
234
        $classHead = [];
235
        $classHead[] = 'namespace ' . $this->class->getNamespace() . ';';
236
237
        if (count($this->class->getUses()) > 0) {
238
            $classHead[] = '';
239
        }
240
241
        foreach ($this->class->getUses() as $use) {
242
            $classHead[] = 'use ' . $use['class'] . (isset($use['alias']) ? ' as ' . $use['alias'] : '') . ';';
243
        }
244
        $classHead[] = '';
245
        $classHead[] = '/**';
246
        $classHead[] = ' * @package ' . $this->class->getNamespace();
247
        $docBlockLines = $this->class->getDocBlockLines();
248
        foreach ($docBlockLines as $lineNr => $line) {
249
            if ($this->ignoreDocBlockLine($lineNr, $docBlockLines)) {
250
                continue;
251
            }
252
            $classHead[] = ' *' . (empty($line) ? '' : ' ' . $line);
253
        }
254
255
        foreach ($this->class->getMagicGetSetMethods() as $magicMethod) {
256
            $method = (isset($magicMethod['static']) && $magicMethod['static'] ? 'static ' : '');
257
            $method.= $magicMethod['returnTypeHint'] . ' ' . $magicMethod['name'];
258
            $method.= '(' . implode(', ', $magicMethod['args']) . ')';
259
            $methodString = ' * @method ' . trim($method);
260
261
            if (strlen($methodString) >= 120) {
262
                $classHead[] = ' * @codingStandardsIgnoreStart';
263
                $classHead[] = $methodString;
264
                $classHead[] = ' * @codingStandardsIgnoreEnd';
265
            } else {
266
                $classHead[] = $methodString;
267
            }
268
        }
269
        $classHead[] = ' */';
270
271
        $fileName = $this->class->getFileName();
272
273
        $source = file_get_contents($fileName);
274
275
        $newSource = preg_replace(
276
            '~namespace(.*)class ' . $this->class->getShortClassName() . '~s',
277
            implode(PHP_EOL, $classHead) . PHP_EOL . 'class ' . $this->class->getShortClassName(),
278
            $source
279
        );
280
281
        file_put_contents($fileName, $newSource);
282
    }
283
284
    protected function ignoreDocBlockLine($lineNr, $lines)
285
    {
286 View Code Duplication
        if (isset($lines[$lineNr+1]) &&
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
287
            strpos($lines[$lineNr], '@codingStandardsIgnoreStart') !== false &&
288
            strpos($lines[$lineNr+1], '@codingStandardsIgnoreEnd') !== false
289
        ) {
290
            return true;
291
        }
292 View Code Duplication
        if (isset($lines[$lineNr-1]) &&
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
293
            strpos($lines[$lineNr], '@codingStandardsIgnoreEnd') !== false &&
294
            strpos($lines[$lineNr-1], '@codingStandardsIgnoreStart') !== false
295
        ) {
296
            return true;
297
        }
298
299
        return false;
300
    }
301
302
    protected function isPrimitive($type)
303
    {
304
        $primitives = [
305
            'bool' => 'is_bool',
306
            'int' => 'is_int',
307
            'string' => 'is_string',
308
            'float' => 'is_float',
309
            'array' => 'is_array'
310
        ];
311
312
        return isset($primitives[$type]);
313
    }
314
}
315