Completed
Push — master ( 9634df...f1462e )
by Nikola
08:19
created

ClassMetadata::getTraits()   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
 * This file is part of the Abstract builder package, an RunOpenCode project.
4
 *
5
 * (c) 2017 RunOpenCode
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
namespace RunOpenCode\AbstractBuilder\Ast;
11
12
use PhpParser\Node\Stmt\Class_;
13
use RunOpenCode\AbstractBuilder\Exception\InvalidArgumentException;
14
use RunOpenCode\AbstractBuilder\Exception\RuntimeException;
15
use RunOpenCode\AbstractBuilder\Utils\ClassUtils;
16
17
/**
18
 * Class ClassMetadata
19
 *
20
 * @package RunOpenCode\AbstractBuilder\Ast
21
 */
22
class ClassMetadata
23
{
24
    /**
25
     * @var string
26
     */
27
    private $namespace;
28
29
    /**
30
     * @var string
31
     */
32
    private $class;
33
34
    /**
35
     * @var string
36
     */
37
    private $fqcn;
38
39
    /**
40
     * @var ClassMetadata
41
     */
42
    private $parent;
43
44
    /**
45
     * @var TraitMetadata[]
46
     */
47
    private $traits;
48
49
    /**
50
     * @var bool
51
     */
52
    private $final;
53
54
    /**
55
     * @var bool
56
     */
57
    private $abstract;
58
59
    /**
60
     * @var MethodMetadata[]
61
     */
62
    private $methods;
63
64
    /**
65
     * @var string
66
     */
67
    private $filename;
68
69
    /**
70
     * @var Class_
71
     */
72
    private $ast;
73
74
    /**
75
     * ClassMetadata constructor.
76
     *
77
     * @param string $namespace
78
     * @param string $class
79
     * @param ClassMetadata|null $parent
80
     * @param bool $final
81
     * @param bool $abstract
82
     * @param MethodMetadata[] $methods
83
     * @param string|null $filename
84
     * @param Class_ $ast
85
     */
86
    public function __construct($namespace, $class, ClassMetadata $parent = null, array $traits = [], $final = false, $abstract = false, array $methods = [], $filename = null, Class_ $ast = null)
87
    {
88
        $this->namespace = trim($namespace, '\\');
89
        $this->class = trim($class, '\\');
90
91
        $this->fqcn = '\\'.$this->class;
92
93
        if ($this->namespace) {
94
            $this->fqcn = '\\'.$this->namespace.'\\'.$this->class;
95
        }
96
97
        if (ClassUtils::isClassNameValid($this->fqcn)) {
98
            throw new InvalidArgumentException(sprintf('Provided full qualified class name "%s" is not valid PHP class name.', $this->fqcn));
99
        }
100
101
        $this->parent = $parent;
102
        $this->traits = $traits;
103
        $this->final = $final;
104
        $this->abstract = $abstract;
105
        $this->methods = $methods;
106
        $this->filename = $filename;
107
        $this->ast = $ast;
108
    }
109
110
    /**
111
     * @return string
112
     */
113
    public function getNamespace()
114
    {
115
        return $this->namespace;
116
    }
117
118
    /**
119
     * @return string
120
     */
121
    public function getClass()
122
    {
123
        return $this->class;
124
    }
125
126
    /**
127
     * @return string
128
     */
129
    public function getFqcn()
130
    {
131
        return $this->fqcn;
132
    }
133
134
    /**
135
     * @return bool
136
     */
137
    public function isAutoloadable()
138
    {
139
        return class_exists($this->getFqcn(), true);
140
    }
141
142
    /**
143
     * Check if class inherits some other class.
144
     *
145
     * @return bool
146
     */
147
    public function hasParent()
148
    {
149
        return null !== $this->parent;
150
    }
151
152
    /**
153
     * @return ClassMetadata|null
154
     */
155
    public function getParent()
156
    {
157
        return $this->parent;
158
    }
159
160
    /**
161
     * @return bool
162
     */
163
    public function hasTraits()
164
    {
165
        return count($this->traits) > 0;
166
    }
167
168
    /**
169
     * @return TraitMetadata[]
170
     */
171
    public function getTraits()
172
    {
173
        return $this->traits;
174
    }
175
176
    /**
177
     * @return bool
178
     */
179
    public function isFinal()
180
    {
181
        return $this->final;
182
    }
183
184
    /**
185
     * @return bool
186
     */
187
    public function isAbstract()
188
    {
189
        return $this->abstract;
190
    }
191
192
    /**
193
     * @return MethodMetadata[]
194
     */
195
    public function getMethods()
196
    {
197
        return $this->methods;
198
    }
199
200
    /**
201
     * Check if class has public method, with optional inheritance tree and trait traverse.
202
     *
203
     * @param string $name
204
     * @param bool $traverse
205
     *
206
     * @return bool
207
     */
208
    public function hasPublicMethod($name, $traverse = true)
209
    {
210
        foreach ($this->methods as $method) {
211
212
            if ($name === $method->getName()) {
213
                return $method->isPublic();
214
            }
215
        }
216
217 View Code Duplication
        if ($traverse && $this->hasTraits()) {
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...
218
219
            /**
220
             * @var TraitMetadata $trait
221
             */
222
            foreach ($this->traits as $trait) {
223
224
                if ($trait->hasPublicMethod($name, $traverse)) {
225
                    return true;
226
                }
227
            }
228
        }
229
230
        if ($traverse && $this->hasParent()) {
231
            return $this->getParent()->hasPublicMethod($name, $traverse);
232
        }
233
234
        return false;
235
    }
236
237
    /**
238
     * Get public method for class, with optional inheritance tree and trait traverse.
239
     *
240
     * @param string $name
241
     * @param bool $traverse
242
     *
243
     * @return MethodMetadata
244
     *
245
     * @throws \RunOpenCode\AbstractBuilder\Exception\RuntimeException
246
     */
247
    public function getPublicMethod($name, $traverse = true)
248
    {
249 View Code Duplication
        foreach ($this->methods as $method) {
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...
250
251
            if ($name === $method->getName()) {
252
253
                if ($method->isPublic()) {
254
                    return $method;
255
                }
256
257
                throw new RuntimeException(sprintf('Method "%s()" for class "%s" exists, but it is not public.', $name, $this->fqcn));
258
            }
259
        }
260
261 View Code Duplication
        if ($traverse && $this->hasTraits()) {
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...
262
263
            /**
264
             * @var TraitMetadata $trait
265
             */
266
            foreach ($this->traits as $trait) {
267
268
                if ($trait->hasPublicMethod($name, $traverse)) {
269
                    return $trait->getPublicMethod($name, $traverse);
270
                }
271
            }
272
        }
273
274
        if ($traverse && $this->hasParent() && $this->getParent()->hasPublicMethod($name, $traverse)) {
275
            return $this->getParent()->getPublicMethod($name, $traverse);
276
        }
277
278
        throw new RuntimeException(sprintf('Method "%s()" for class "%s" does not exists.', $name, $this->fqcn));
279
    }
280
281
    /**
282
     * @return string
283
     */
284
    public function getFilename()
285
    {
286
        return $this->filename;
287
    }
288
289
    /**
290
     * @return Class_
291
     */
292
    public function getAst()
293
    {
294
        return $this->ast;
295
    }
296
297
    /**
298
     * {@inheritdoc}
299
     */
300
    public function __toString()
301
    {
302
        return $this->getFqcn();
303
    }
304
305
    /**
306
     * Initialize new, non-existing class.
307
     *
308
     * @param string $fqcn
309
     *
310
     * @return ClassMetadata|static $this
311
     */
312
    public static function create($fqcn)
313
    {
314
        $parts = explode('\\', trim($fqcn, '\\'));
315
        $class = array_pop($parts);
316
        $namespace = implode('\\', $parts);
317
318
        return new static($namespace, $class);
319
    }
320
321
    /**
322
     * Clones original metadata object, with possible values overwrite
323
     *
324
     * @param ClassMetadata $original
325
     * @param array $overwrite
326
     *
327
     * @return ClassMetadata|static $this
328
     */
329
    public static function clone(ClassMetadata $original, array $overwrite = [])
330
    {
331
        $data = [
332
            'namespace' => $original->getNamespace(),
333
            'class' => $original->getClass(),
334
            'parent' => $original->getParent(),
335
            'final' => $original->isFinal(),
336
            'abstract ' => $original->isAbstract(),
337
            'methods' => $original->getMethods(),
338
            'filename' => $original->getFilename(),
339
            'ast' => $original->getAst(),
340
        ];
341
342
        $data = array_merge($data, $overwrite);
343
344
        return (new \ReflectionClass(static::class))->newInstanceArgs(array_values($data));
345
    }
346
}
347