Passed
Push — develop ( 18ac87...d6d324 )
by Mikaël
02:10
created

AbstractElement   D

Complexity

Total Complexity 59

Size/Duplication

Total Lines 288
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 94
dl 0
loc 288
ccs 117
cts 117
cp 1
rs 4.08
c 1
b 0
f 0
wmc 59

29 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A cleanArrayToString() 0 10 3
A getChildContent() 0 10 4
A getContextualLineBeforeChildren() 0 9 2
A getIndentedString() 0 8 2
A getBracketBeforeChildren() 0 6 2
A getLineAfterChildren() 0 3 1
A useBracketsForChildren() 0 3 1
A getToStringBeforeChildren() 0 8 2
A setName() 0 8 2
A getBracketAfterChildren() 0 5 2
A getContextualLineAfterChildren() 0 9 2
A getCalledClass() 0 3 1
A addChild() 0 13 4
A toString() 0 15 4
A getName() 0 3 1
A setIndentation() 0 5 1
A getIndentation() 0 3 1
A getToStringAfterChildren() 0 8 2
A getToStringDeclaration() 0 8 2
A stringIsValid() 0 3 4
A getIndentationString() 0 3 2
A nameIsValid() 0 8 2
A childIsValid() 0 11 4
A objectIsValid() 0 3 3
A getChildren() 0 3 1
A getLineBeforeChildren() 0 3 1
A getPhpName() 0 3 1
A __toString() 0 3 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.

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