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

ReflectedClass::getConstructorArgs()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 0
cts 4
cp 0
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
crap 2
1
<?php
2
/**
3
 * @author @jayS-de <[email protected]>
4
 */
5
6
namespace Commercetools\Core\Helper\Annotate;
7
8
/**
9
 * @package Commercetools\Core\Helper\Annotate
10
 */
11
class ReflectedClass
12
{
13
    /**
14
     * @var bool
15
     */
16
    protected $abstract;
17
18
    protected $fileName;
19
20
    protected $className;
21
    protected $shortClassName;
22
23
    protected $namespace;
24
    protected $uses = [];
25
    protected $methods = [];
26
    protected $magicGetSetMethods = [];
27
    protected $docBlockLines = [];
28
29
    protected $constructorArgs = [];
30
31
    public function __construct($className)
32
    {
33
        $this->className = $className;
34
        $this->reflectClass();
35
        $this->reflectUseStatements();
36
        $this->reflectDocBlock();
37
        $this->reflectConstructorArgs();
38
    }
39
40
    public function addMagicGetSetMethod(
41
        $type,
42
        $fieldName,
43
        $args,
44
        $returnTypeHint = null,
45
        $returnsReference = null,
46
        $shortDescription = null
47
    ) {
48
        $name = $type . ucfirst($fieldName);
49
        if ($this->hasMethod($name)) {
50
            return;
51
        }
52
53
        $magicMethod = [
54
            'name' => $name,
55
            'returnTypeHint' => $returnTypeHint,
56
            'returnsReference' => $returnsReference,
57
            'type' => $type,
58
            'fieldName' => $fieldName,
59
            'args' => $args,
60
            'shortDescription' => $shortDescription
61
        ];
62
63
        if (isset($this->magicGetSetMethods[$name])) {
64
            $magicMethod = array_merge($this->magicGetSetMethods[$name], $magicMethod);
65
        }
66
67
        $this->magicGetSetMethods[$name] = $magicMethod;
68
    }
69
70
    public function addMagicMethod(
71
        $methodName,
72
        $args,
73
        $returnTypeHint = null,
74
        $returnsReference = null,
75
        $shortDescription = null,
76
        $static = false,
77
        $force = false
78
    ) {
79
        if (!$force && $this->hasMethod($methodName)) {
80
            return;
81
        }
82
83
        $magicMethod = [
84
            'name' => $methodName,
85
            'returnTypeHint' => $returnTypeHint,
86
            'returnsReference' => $returnsReference,
87
            'args' => $args,
88
            'shortDescription' => $shortDescription,
89
            'static' => $static
90
        ];
91
92
        if (isset($this->magicGetSetMethods[$methodName])) {
93
            $magicMethod = array_merge($this->magicGetSetMethods[$methodName], $magicMethod);
94
        }
95
96
        $this->magicGetSetMethods[$methodName] = $magicMethod;
97
    }
98
99
    public function hasMethod($methodName)
100
    {
101
        return isset($this->methods[$methodName]);
102
    }
103
104
    public function hasMagicGetSetMethod($methodName)
105
    {
106
        return isset($this->magicGetSetMethods[$methodName]);
107
    }
108
109
    public function getMethod($methodName)
110
    {
111
        if ($this->hasMethod($methodName)) {
112
            return $this->methods[$methodName];
113
        }
114
        return false;
115
    }
116
117
    public function getMagicGetSetMethod($methodName)
118
    {
119
        if ($this->hasMethod($methodName)) {
120
            return $this->magicGetSetMethods[$methodName];
121
        }
122
        return false;
123
    }
124
125
    /**
126
     * @return array
127
     */
128
    public function getConstructorArgs()
129
    {
130
        return $this->constructorArgs;
131
    }
132
133
    /**
134
     * @param $className
135
     * @param $alias
136
     */
137
    public function addUse($className, $alias = null)
138
    {
139
        $className = trim($className, '\\');
140
        try {
141
            $reflect = new \ReflectionClass($className);
142
        } catch (\ReflectionException $e) {
143
            return;
144
        }
145
        if ($reflect->getNamespaceName() !== $this->namespace && !isset($this->uses[$className])) {
146
            $this->uses[$className] = [
147
                'class' => $className,
148
                'alias' => $alias
149
            ];
150
        }
151
    }
152
153
    protected function reflectClass()
154
    {
155
        $reflectionClass = new \ReflectionClass($this->getClassName());
156
        $this->shortClassName = $reflectionClass->getShortName();
157
        $this->namespace = $reflectionClass->getNamespaceName();
158
        $this->fileName = $reflectionClass->getFileName();
159
        $this->abstract = $reflectionClass->isAbstract();
160
161
        $methods = [];
162
        foreach ($reflectionClass->getMethods() as $method) {
163
            $methods[$method->getName()] = $method;
164
        }
165
        $this->methods = $methods;
166
    }
167
168
    protected function reflectUseStatements()
169
    {
170
        $content = file_get_contents($this->getFileName());
171
        $tokens = token_get_all($content);
172
        for ($index = 0; isset($tokens[$index]); $index++) {
173
            if (!isset($tokens[$index][0])) {
174
                continue;
175
            }
176
            if (T_USE == $tokens[$index][0]) {
177
                $use = '';
178
                $index += 2;
179
                while ($tokens[$index][0] != T_AS && isset($tokens[$index]) && is_array($tokens[$index])) {
180
                    if ($tokens[$index][0] == T_WHITESPACE) {
181
                        $index++;
182
                        continue;
183
                    }
184
                    $use .= $tokens[$index++][1];
185
                }
186
                $alias = null;
187 View Code Duplication
                if ($tokens[$index][0] == T_AS) {
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...
188
                    $alias = '';
189
                    $index += 2;
190
                    while (isset($tokens[$index]) && is_array($tokens[$index])) {
191
                        $alias .= $tokens[$index++][1];
192
                    }
193
                }
194
                $this->addUse($use, $alias);
195
            }
196
            if (T_CLASS == $tokens[$index][0]) {
197
                break;
198
            }
199
        }
200
    }
201
202
    protected function reflectDocBlock()
203
    {
204
        $reflectionClass = new \ReflectionClass($this->getClassName());
205
        $docBlock = $reflectionClass->getDocComment();
206
207
        $docBlockLines = explode(PHP_EOL, $docBlock);
208
209
        $lines = [];
210
        foreach ($docBlockLines as $line) {
211
            if ($this->skipDocBlockLine($line)) {
212
                continue;
213
            } elseif (preg_match($this->getMethodPattern(), $line, $matches)) {
214
                $this->reflectMagicMethods($matches);
215
            } else {
216
                $lines[] = trim(preg_replace('/^ \*/', '', $line));
217
            }
218
        }
219
220
        $this->docBlockLines = $lines;
221
    }
222
223
    protected function reflectConstructorArgs()
224
    {
225
        $reflectionClass = new \ReflectionClass($this->getClassName());
226
        $constructor = $reflectionClass->getConstructor();
227
        $parameters = $constructor->getParameters();
228
229
        $args = [];
230
        foreach ($parameters as $parameter) {
231
            if ($parameter->isOptional()) {
232
                continue;
233
            }
234
            $typeClass = $parameter->getClass();
235
            $typeName = '';
236
            if (!is_null($typeClass)) {
237
                $typeName = $typeClass->getShortName();
238
            }
239
            $args[] = trim($typeName . ' $' . $parameter->getName());
240
        }
241
        $this->constructorArgs = $args;
242
    }
243
244
    /**
245
     * @param $type
246
     * @param $fieldName
247
     * @return string
248
     */
249
    protected function getMethodName($type, $fieldName)
250
    {
251
        return $type . ucfirst($fieldName);
252
    }
253
254
    protected function reflectMagicMethods($matches)
255
    {
256
        list(, $static, $returnTypeHint, $returnsReference, $name, $args, $shortDescription) = $matches;
257
        $type = $fieldName = '';
258
        if (preg_match('~^(set|get)(.*)~', $name, $matches)) {
259
            $type = $matches[1];
260
            $fieldName = $matches[2];
261
        }
262
        $args = array_map('trim', explode(',', $args));
263
        if (empty($type) || !$this->hasMethod($name)) {
264
            $this->magicGetSetMethods[$name] = [
265
                'name' => $name,
266
                'returnTypeHint' => $returnTypeHint,
267
                'returnsReference' => $returnsReference,
268
                'type' => $type,
269
                'fieldName' => $fieldName,
270
                'args' => $args,
271
                'static' => ($static === 'static'),
272
                'shortDescription' => $shortDescription
273
            ];
274
        }
275
    }
276
277
    protected function skipDocBlockLine($line)
278
    {
279
        return (strpos($line, '/**') === 0)
280
            || (strpos($line, ' */') === 0)
281
            || (strpos($line, '@package') > 0)
282
            || (strpos($line, '* Class') > 0);
283
    }
284
285
    /**
286
     * @return string
287
     */
288
    protected function getMethodPattern()
289
    {
290
        return '~@method (?:(static)\\s+)?(?:([\\w\\\\]+(?:\\|[\\w\\\\]+)*)\\s+)?' .
291
            '(&)?\\s*(\\w+)\\s*\\(\\s*(.*)\\s*\\)\\s*(.*|$)~s';
292
    }
293
294
    /**
295
     * @return mixed
296
     */
297
    public function getClassName()
298
    {
299
        return $this->className;
300
    }
301
302
    /**
303
     * @return mixed
304
     */
305
    public function getNamespace()
306
    {
307
        return $this->namespace;
308
    }
309
310
    /**
311
     * @return mixed
312
     */
313
    public function getUses()
314
    {
315
        return $this->uses;
316
    }
317
318
    /**
319
     * @return mixed
320
     */
321
    public function getMethods()
322
    {
323
        return $this->methods;
324
    }
325
326
    /**
327
     * @return mixed
328
     */
329
    public function getMagicGetSetMethods()
330
    {
331
        return $this->magicGetSetMethods;
332
    }
333
334
    /**
335
     * @return array
336
     */
337
    public function getDocBlockLines()
338
    {
339
        return $this->docBlockLines;
340
    }
341
342
    /**
343
     * @return mixed
344
     */
345
    public function getFileName()
346
    {
347
        return $this->fileName;
348
    }
349
350
    /**
351
     * @return bool
352
     */
353
    public function isAbstract()
354
    {
355
        return $this->abstract;
356
    }
357
358
    /**
359
     * @return mixed
360
     */
361
    public function getShortClassName()
362
    {
363
        return $this->shortClassName;
364
    }
365
}
366