Passed
Push — develop ( 6b3bb3...b964e0 )
by Mikaël
01:53 queued 11s
created

AbstractElement::__toString()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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