Completed
Push — master ( 1d2d63...5f432b )
by Jens
09:03
created

ClassAnnotator::isPrimitive()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 12
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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