Completed
Push — master ( b1e8a1...edf0e8 )
by Nikola
03:49
created

ClassMetadata   B

Complexity

Total Complexity 43

Size/Duplication

Total Lines 280
Duplicated Lines 28.57 %

Coupling/Cohesion

Components 1
Dependencies 5

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
wmc 43
lcom 1
cbo 5
dl 80
loc 280
ccs 0
cts 121
cp 0
rs 8.3157
c 0
b 0
f 0

16 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 15 2
A getName() 0 4 1
A getShortName() 0 4 1
A isAutoloadable() 0 4 1
A hasParent() 0 4 1
A getParent() 0 4 1
A hasTraits() 0 4 1
A getTraits() 0 4 1
A isFinal() 0 4 1
A isAbstract() 0 4 1
A getMethods() 0 4 1
D hasMethod() 29 29 9
D hasPublicMethod() 28 28 9
C getPublicMethod() 23 33 11
A getAst() 0 4 1
A __toString() 0 4 1

How to fix   Duplicated Code    Complexity   

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:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like ClassMetadata often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use ClassMetadata, and based on these observations, apply Extract Interface, too.

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\Metadata;
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\Metadata
21
 */
22
class ClassMetadata
23
{
24
    /**
25
     * @var string
26
     */
27
    private $name;
28
29
    /**
30
     * @var ClassMetadata
31
     */
32
    private $parent;
33
34
    /**
35
     * @var TraitMetadata[]
36
     */
37
    private $traits;
38
39
    /**
40
     * @var bool
41
     */
42
    private $final;
43
44
    /**
45
     * @var bool
46
     */
47
    private $abstract;
48
49
    /**
50
     * @var MethodMetadata[]
51
     */
52
    private $methods;
53
54
    /**
55
     * @var Class_
56
     */
57
    private $ast;
58
59
    /**
60
     * ClassMetadata constructor.
61
     *
62
     * @param string $name
63
     * @param ClassMetadata|null $parent
64
     * @param bool $final
65
     * @param bool $abstract
66
     * @param MethodMetadata[] $methods
67
     * @param Class_ $ast
68
     */
69
    public function __construct($name, ClassMetadata $parent = null, array $traits = [], $final = false, $abstract = false, array $methods = [], Class_ $ast = null)
70
    {
71
        $this->name = trim($name, '\\');
72
73
        if (!ClassUtils::isClassNameValid($this->name)) {
74
            throw new InvalidArgumentException(sprintf('Provided class name "%s" is not valid PHP class name.', $this->name));
75
        }
76
77
        $this->parent = $parent;
78
        $this->traits = $traits;
79
        $this->final = $final;
80
        $this->abstract = $abstract;
81
        $this->methods = $methods;
82
        $this->ast = $ast;
83
    }
84
85
    /**
86
     * @return string
87
     */
88
    public function getName()
89
    {
90
        return $this->name;
91
    }
92
93
    /**
94
     * @return string
95
     */
96
    public function getShortName()
97
    {
98
        return end($parts = explode('\\', $this->name));
0 ignored issues
show
Bug introduced by
$parts = explode('\\', $this->name) cannot be passed to end() as the parameter $array expects a reference.
Loading history...
99
    }
100
101
    /**
102
     * @return bool
103
     */
104
    public function isAutoloadable()
105
    {
106
        return class_exists($this->getName(), true);
107
    }
108
109
    /**
110
     * Check if class inherits some other class.
111
     *
112
     * @return bool
113
     */
114
    public function hasParent()
115
    {
116
        return null !== $this->parent;
117
    }
118
119
    /**
120
     * @return ClassMetadata|null
121
     */
122
    public function getParent()
123
    {
124
        return $this->parent;
125
    }
126
127
    /**
128
     * @return bool
129
     */
130
    public function hasTraits()
131
    {
132
        return count($this->traits) > 0;
133
    }
134
135
    /**
136
     * @return TraitMetadata[]
137
     */
138
    public function getTraits()
139
    {
140
        return $this->traits;
141
    }
142
143
    /**
144
     * @return bool
145
     */
146
    public function isFinal()
147
    {
148
        return $this->final;
149
    }
150
151
    /**
152
     * @return bool
153
     */
154
    public function isAbstract()
155
    {
156
        return $this->abstract;
157
    }
158
159
    /**
160
     * @return MethodMetadata[]
161
     */
162
    public function getMethods()
163
    {
164
        return $this->methods;
165
    }
166
167
    /**
168
     * Check if class has method, with optional inheritance tree and trait traverse.
169
     *
170
     * @param string $name
171
     * @param bool $traverse
172
     *
173
     * @return bool
174
     */
175 View Code Duplication
    public function hasMethod($name, $traverse = true)
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...
176
    {
177
        foreach ($this->methods as $method) {
178
179
            if ($name === $method->getName()) {
180
                return true;
181
            }
182
        }
183
184
        if ($traverse && $this->hasTraits()) {
185
186
            /**
187
             * @var TraitMetadata $trait
188
             */
189
            foreach ($this->traits as $trait) {
190
191
                if ($trait->hasMethod($name, $traverse)) {
192
                    return true;
193
                }
194
            }
195
        }
196
197
198
        if ($traverse && $this->hasParent()) {
199
            return $this->getParent()->hasMethod($name, $traverse);
200
        }
201
202
        return false;
203
    }
204
205
    /**
206
     * Check if class has public method, with optional inheritance tree and trait traverse.
207
     *
208
     * @param string $name
209
     * @param bool $traverse
210
     *
211
     * @return bool
212
     */
213 View Code Duplication
    public function hasPublicMethod($name, $traverse = true)
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...
214
    {
215
        foreach ($this->methods as $method) {
216
217
            if ($name === $method->getName()) {
218
                return $method->isPublic();
219
            }
220
        }
221
222
        if ($traverse && $this->hasTraits()) {
223
224
            /**
225
             * @var TraitMetadata $trait
226
             */
227
            foreach ($this->traits as $trait) {
228
229
                if ($trait->hasPublicMethod($name, $traverse)) {
230
                    return true;
231
                }
232
            }
233
        }
234
235
        if ($traverse && $this->hasParent()) {
236
            return $this->getParent()->hasPublicMethod($name, $traverse);
237
        }
238
239
        return false;
240
    }
241
242
    /**
243
     * Get public method for class, with optional inheritance tree and trait traverse.
244
     *
245
     * @param string $name
246
     * @param bool $traverse
247
     *
248
     * @return MethodMetadata
249
     *
250
     * @throws \RunOpenCode\AbstractBuilder\Exception\RuntimeException
251
     */
252
    public function getPublicMethod($name, $traverse = true)
253
    {
254 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...
255
256
            if ($name === $method->getName()) {
257
258
                if ($method->isPublic()) {
259
                    return $method;
260
                }
261
262
                throw new RuntimeException(sprintf('Method "%s()" for class "%s" exists, but it is not public.', $name, $this->name));
263
            }
264
        }
265
266 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...
267
268
            /**
269
             * @var TraitMetadata $trait
270
             */
271
            foreach ($this->traits as $trait) {
272
273
                if ($trait->hasPublicMethod($name, $traverse)) {
274
                    return $trait->getPublicMethod($name, $traverse);
275
                }
276
            }
277
        }
278
279
        if ($traverse && $this->hasParent() && $this->getParent()->hasPublicMethod($name, $traverse)) {
280
            return $this->getParent()->getPublicMethod($name, $traverse);
281
        }
282
283
        throw new RuntimeException(sprintf('Method "%s()" for class "%s" does not exists.', $name, $this->name));
284
    }
285
286
    /**
287
     * @return Class_
288
     */
289
    public function getAst()
290
    {
291
        return $this->ast;
292
    }
293
294
    /**
295
     * {@inheritdoc}
296
     */
297
    public function __toString()
298
    {
299
        return $this->getName();
300
    }
301
}
302