Completed
Push — master ( 03494f...174675 )
by Mikaël
01:38
created

AbstractElement   C

Complexity

Total Complexity 57

Size/Duplication

Total Lines 337
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 0

Test Coverage

Coverage 99.11%

Importance

Changes 0
Metric Value
wmc 57
lcom 2
cbo 0
dl 0
loc 337
ccs 111
cts 112
cp 0.9911
rs 5.04
c 0
b 0
f 0

30 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
A setName() 0 8 2
A getName() 0 4 1
A nameIsValid() 0 8 2
A stringIsValid() 0 4 4
A objectIsValid() 0 4 3
A toString() 0 12 3
A getToStringDeclaration() 0 8 2
A getToStringBeforeChildren() 0 8 2
A getToStringAfterChildren() 0 8 2
A cleanArrayToString() 0 10 3
A getChildContent() 0 10 4
A getPhpName() 0 4 1
A addChild() 0 13 4
A childIsValid() 0 11 4
A getChildren() 0 4 1
getPhpDeclaration() 0 1 ?
getChildrenTypes() 0 1 ?
A getContextualLineBeforeChildren() 0 9 2
A getContextualLineAfterChildren() 0 9 2
A getLineBeforeChildren() 0 4 1
A getLineAfterChildren() 0 4 1
A useBracketsForChildren() 0 4 1
A getBracketBeforeChildren() 0 6 2
A getBracketAfterChildren() 0 5 2
A setIndentation() 0 5 1
A getIndentation() 0 4 1
A getIndentationString() 0 4 2
A getIndentedString() 0 8 2
A getCalledClass() 0 4 1

How to fix   Complexity   

Complex Class

Complex classes like AbstractElement 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 AbstractElement, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
declare(strict_types=1);
4
5
namespace WsdlToPhp\PhpGenerator\Element;
6
7
abstract class AbstractElement implements GenerateableInterface
8
{
9
    /**
10
     * @var string
11
     */
12
    protected $name;
13
    /**
14
     * @var AbstractElement[]|mixed[]
15
     */
16
    protected $children;
17
    /**
18
     * @var int
19
     */
20
    protected $indentation;
21
    /**
22
     * @param string $name
23
     */
24 288
    public function __construct(string $name)
25
    {
26 288
        $this->setName($name);
27 288
        $this->children = [];
28 288
        $this->indentation = 0;
29 288
    }
30
    /**
31
     * @throws \InvalidArgumentException
32
     * @param string $name
33
     * @return AbstractElement
34
     */
35 288
    public function setName(string $name)
36
    {
37 288
        if (!self::nameIsValid($name)) {
38
            throw new \InvalidArgumentException(sprintf('Name "%s" is invalid when instantiating %s object', $name, $this->getCalledClass()));
39
        }
40 288
        $this->name = $name;
41 288
        return $this;
42
    }
43
    /**
44
     * @return string
45
     */
46 204
    public function getName()
47
    {
48 204
        return $this->name;
49
    }
50
    /**
51
     * @param string $name
52
     * @param bool $allowBackslash
53
     * @return bool
54
     */
55 292
    public static function nameIsValid(string $name, bool $allowBackslash = false)
56
    {
57 292
        $pattern = '/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/';
58 292
        if ($allowBackslash === true) {
59 44
            $pattern = '/[a-zA-Z_\x7f-\xff\\\][a-zA-Z0-9_\x7f-\xff\\\]*/';
60
        }
61 292
        return preg_match($pattern, (string) $name);
62
    }
63
    /**
64
     * @param mixed $string
65
     * @param bool $checkName
66
     * @param bool $allowBackslash
67
     * @return bool
68
     */
69 112
    public static function stringIsValid($string, bool $checkName = true, bool $allowBackslash = false): bool
70
    {
71 112
        return (is_string($string) && !empty($string) && (!$checkName || self::nameIsValid($string, $allowBackslash)));
72
    }
73
    /**
74
     * @param mixed $object
75
     * @param mixed $checkClass
76
     * @return bool
77
     */
78 86
    public static function objectIsValid($object, ?string $checkClass = null): bool
79
    {
80 86
        return (is_object($object) && ($checkClass === null || get_class($object) === $checkClass));
81
    }
82
    /**
83
     * @see \WsdlToPhp\PhpGenerator\Element\GenerateableInterface::toString()
84
     * @param int $indentation
85
     * @return string
86
     */
87 78
    public function toString(int $indentation = null): string
88
    {
89
        $lines = [
90 78
            $this->getToStringDeclaration($indentation),
91 78
            $this->getToStringBeforeChildren($indentation),
92
        ];
93 78
        foreach ($this->getChildren() as $child) {
94 50
            $lines[] = $this->getChildContent($child, $indentation + ($this->useBracketsForChildren() ? 1 : 0));
95
        }
96 78
        $lines[] = $this->getToStringAfterChildren($indentation);
97 78
        return implode(self::BREAK_LINE_CHAR, self::cleanArrayToString($lines));
98
    }
99
    /**
100
     * @param int $indentation
101
     * @return string|null
102
     */
103 78
    private function getToStringDeclaration(int $indentation = null): ?string
104
    {
105 78
        $declaration = $this->getPhpDeclaration();
106 78
        if (!empty($declaration)) {
107 78
            return $this->getIndentedString($declaration, $indentation);
108
        }
109 26
        return null;
110
    }
111
    /**
112
     * @param int $indentation
113
     * @return string|null
114
     */
115 78
    private function getToStringBeforeChildren(int $indentation = null): ?string
116
    {
117 78
        $before = $this->getContextualLineBeforeChildren($indentation);
118 78
        if (!empty($before)) {
119 52
            return $before;
120
        }
121 64
        return null;
122
    }
123
    /**
124
     * @param int $indentation
125
     * @return string|null
126
     */
127 78
    private function getToStringAfterChildren(int $indentation = null): ?string
128
    {
129 78
        $after = $this->getContextualLineAfterChildren($indentation);
130 78
        if (!empty($after)) {
131 52
            return $after;
132
        }
133 64
        return null;
134
    }
135
    /**
136
     * @param array $array
137
     * @return array
138
     */
139 78
    private static function cleanArrayToString(array $array): array
140
    {
141 78
        $newArray = [];
142 78
        foreach ($array as $line) {
143 78
            if ($line !== null) {
144 78
                $newArray[] = $line;
145
            }
146
        }
147 78
        return $newArray;
148
    }
149
    /**
150
     * @throws \InvalidArgumentException
151
     * @param string|AbstractElement $child
152
     * @param int $indentation
153
     * @return string
154
     */
155 50
    protected function getChildContent($child, int $indentation = null): string
156
    {
157 50
        $content = '';
158 50
        if (is_string($child)) {
159 8
            $content = $this->getIndentedString($child, $indentation);
160 50
        } elseif ($child instanceof AbstractElement) {
161 50
            $content = $child->toString($indentation === null ? $this->getIndentation() : $indentation);
162
        }
163 50
        return $content;
164
    }
165
    /**
166
     * @return string
167
     */
168 190
    public function getPhpName(): string
169
    {
170 190
        return sprintf('%s', $this->getName());
171
    }
172
    /**
173
     * @throws \InvalidArgumentException
174
     * @param mixed $child
175
     * @return AbstractElement
176
     */
177 82
    public function addChild($child): AbstractElement
178
    {
179 82
        if (!$this->childIsValid($child)) {
180 20
            $types = $this->getChildrenTypes();
181 20
            if (empty($types)) {
182 8
                throw new \InvalidArgumentException('This element does not accept any child element');
183
            } else {
184 12
                throw new \InvalidArgumentException(sprintf('Element of type "%s:%s" is not authorized, please provide one of these types: %s', gettype($child), is_object($child) ? get_class($child) : 'unknown', implode(', ', $this->getChildrenTypes())));
185
            }
186
        }
187 62
        $this->children[] = $child;
188 62
        return $this;
189
    }
190
    /**
191
     * @param mixed $child
192
     * @return bool
193
     */
194 94
    protected function childIsValid($child): bool
195
    {
196 94
        $valid = false;
197 94
        $authorizedTypes = $this->getChildrenTypes();
198 94
        if (!empty($authorizedTypes)) {
199 86
            foreach ($authorizedTypes as $authorizedType) {
200 86
                $valid |= (gettype($child) === $authorizedType) || self::objectIsValid($child, $authorizedType);
201
            }
202
        }
203 94
        return (bool) $valid;
204
    }
205
    /**
206
     * @return AbstractElement[]|mixed[]
207
     */
208 110
    public function getChildren(): array
209
    {
210 110
        return $this->children;
211
    }
212
    /**
213
     * @return string
214
     */
215
    abstract public function getPhpDeclaration(): string;
216
    /**
217
     * defines authorized children element types
218
     * @return string[]
219
     */
220
    abstract public function getChildrenTypes(): array;
221
    /**
222
     * @param int $indentation
223
     * @return string
224
     */
225 78
    private function getContextualLineBeforeChildren(int $indentation = null): string
226
    {
227 78
        if ($this->useBracketsForChildren()) {
228 36
            $line = $this->getBracketBeforeChildren($indentation);
229
        } else {
230 64
            $line = $this->getLineBeforeChildren($indentation);
231
        }
232 78
        return $line;
233
    }
234
    /**
235
     * @param int $indentation
236
     * @return string
237
     */
238 78
    private function getContextualLineAfterChildren(int $indentation = null): string
239
    {
240 78
        if ($this->useBracketsForChildren()) {
241 36
            $line = $this->getBracketAfterChildren($indentation);
242
        } else {
243 64
            $line = $this->getLineAfterChildren($indentation);
244
        }
245 78
        return $line;
246
    }
247
    /**
248
     * Allows to generate content before children content is generated
249
     * @param int $indentation
250
     * @return string
251
     */
252 64
    public function getLineBeforeChildren(int $indentation = null): string
253
    {
254 64
        return '';
255
    }
256
    /**
257
     * Allows to generate content after children content is generated
258
     * @param int $indentation
259
     * @return string
260
     */
261 64
    public function getLineAfterChildren(int $indentation = null): string
262
    {
263 64
        return '';
264
    }
265
    /**
266
     * Allows to indicate that children are contained by brackets,
267
     * in the case the method returns true, getBracketBeforeChildren
268
     * is called instead of getLineBeforeChildren and getBracketAfterChildren
269
     * is called instead of getLineAfterChildren, but be aware that these methods
270
     * call the two others
271
     * @return bool
272
     */
273 62
    public function useBracketsForChildren(): bool
274
    {
275 62
        return false;
276
    }
277
    /**
278
     * Allows to generate content before children content is generated
279
     * @param int $indentation
280
     * @return string
281
     */
282 36
    public function getBracketBeforeChildren(int $indentation = null): string
283
    {
284 36
        $line = $this->getIndentedString(self::OPEN_BRACKET, $indentation);
285 36
        $this->setIndentation(($indentation === null ? $this->getIndentation() : $indentation) + 1);
286 36
        return $line;
287
    }
288
    /**
289
     * Allows to generate content after children content is generated
290
     * @param int $indentation
291
     * @return string
292
     */
293 36
    public function getBracketAfterChildren(int $indentation = null): string
294
    {
295 36
        $this->setIndentation(($indentation === null ? $this->getIndentation() : $indentation) - 1);
296 36
        return $this->getIndentedString(self::CLOSE_BRACKET, $indentation);
297
    }
298
    /**
299
     * @param int $indentation
300
     * @return AbstractElement
301
     */
302 36
    public function setIndentation(int $indentation): AbstractElement
303
    {
304 36
        $this->indentation = $indentation;
305 36
        return $this;
306
    }
307
    /**
308
     * @return int
309
     */
310 78
    public function getIndentation(): int
311
    {
312 78
        return $this->indentation;
313
    }
314
    /**
315
     * @see \WsdlToPhp\PhpGenerator\Element\GenerateableInterface::getIndentationString()
316
     * @param int $indentation
317
     * @return string
318
     */
319 78
    public function getIndentationString(int $indentation = null): string
320
    {
321 78
        return str_repeat(self::INDENTATION_CHAR, $indentation === null ? $this->getIndentation() : $indentation);
322
    }
323
    /**
324
     * @param string $string
325
     * @param int $indentation
326
     * @return string
327
     */
328 78
    public function getIndentedString(string $string, int $indentation = null): string
329
    {
330 78
        $strings = explode(self::BREAK_LINE_CHAR, $string);
331 78
        foreach ($strings as $i => $s) {
332 78
            $strings[$i] = sprintf('%s%s', $this->getIndentationString($indentation), $s);
333
        }
334 78
        return implode(self::BREAK_LINE_CHAR, $strings);
335
    }
336
    /**
337
     * @return string
338
     */
339 4
    final public function getCalledClass(): string
340
    {
341 4
        return substr(get_called_class(), strrpos(get_called_class(), '\\') + 1);
342
    }
343
}
344