Passed
Push — develop ( 32a57e...589086 )
by Mikaël
03:14 queued 36s
created

AbstractElement::getToStringDeclaration()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

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