Completed
Branch great-split (84e0e9)
by Anton
02:38
created

ClassDeclaration::render()   B

Complexity

Conditions 4
Paths 8

Size

Total Lines 32
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 16
nc 8
nop 1
dl 0
loc 32
rs 8.5806
c 0
b 0
f 0
1
<?php
2
/**
3
 * Spiral Framework.
4
 *
5
 * @license   MIT
6
 * @author    Anton Titov (Wolfy-J)
7
 */
8
9
namespace Spiral\Reactor;
10
11
use Doctrine\Common\Inflector\Inflector;
12
use Spiral\Reactor\ClassDeclaration\Aggregators\ConstantAggregator;
13
use Spiral\Reactor\ClassDeclaration\Aggregators\MethodAggregator;
14
use Spiral\Reactor\ClassDeclaration\Aggregators\PropertyAggregator;
15
use Spiral\Reactor\ClassDeclaration\ConstantDeclaration;
16
use Spiral\Reactor\ClassDeclaration\MethodDeclaration;
17
use Spiral\Reactor\ClassDeclaration\PropertyDeclaration;
18
use Spiral\Reactor\Exceptions\ReactorException;
19
use Spiral\Reactor\Prototypes\NamedDeclaration;
20
use Spiral\Reactor\Traits\CommentTrait;
21
22
/**
23
 * Class declaration.
24
 *
25
 * @todo interface, trait declarations
26
 */
27
class ClassDeclaration extends NamedDeclaration implements ReplaceableInterface
28
{
29
    use CommentTrait;
30
31
    /**
32
     * @var string
33
     */
34
    private $extends = '';
35
36
    /**
37
     * @var array
38
     */
39
    private $interfaces = [];
40
41
    /**
42
     * Class traits.
43
     *
44
     * @var array
45
     */
46
    private $traits = [];
47
48
    /**
49
     * @var ConstantAggregator
50
     */
51
    private $constants = null;
52
53
    /**
54
     * @var PropertyAggregator
55
     */
56
    private $properties = null;
57
58
    /**
59
     * @var MethodAggregator
60
     */
61
    private $methods = null;
62
63
    /**
64
     * @param string $name
65
     * @param string $extends
66
     * @param array  $interfaces
67
     * @param string $comment
68
     *
69
     * @throws ReactorException When name is invalid.
70
     */
71
    public function __construct(
72
        string $name,
73
        string $extends = '',
74
        array $interfaces = [],
75
        string $comment = ''
76
    ) {
77
        parent::__construct($name);
78
79
        if (!empty($extends)) {
80
            $this->setExtends($extends);
81
        }
82
83
        $this->setInterfaces($interfaces);
84
        $this->initComment($comment);
85
86
        $this->constants = new ConstantAggregator([]);
87
        $this->properties = new PropertyAggregator([]);
88
        $this->methods = new MethodAggregator([]);
89
    }
90
91
    /**
92
     * {@inheritdoc}
93
     */
94
    public function setName(string $name): ClassDeclaration
95
    {
96
        return parent::setName(Inflector::classify($name));
97
    }
98
99
    /**
100
     * @param string $class Class name.
101
     *
102
     * @return self
103
     */
104
    public function setExtends($class): ClassDeclaration
105
    {
106
        $this->extends = ltrim($class, '\\');
107
108
        return $this;
109
    }
110
111
    /**
112
     * @return string
113
     */
114
    public function getExtends(): string
115
    {
116
        return $this->extends;
117
    }
118
119
    /**
120
     * @param string $interface
121
     *
122
     * @return bool
123
     */
124
    public function hasInterface(string $interface): bool
125
    {
126
        $interface = ltrim($interface, '\\');
127
128
        return isset($this->interfaces[$interface]);
129
    }
130
131
    /**
132
     * Declare class interfaces.
133
     *
134
     * @param array $interfaces
135
     *
136
     * @return self
137
     */
138
    public function setInterfaces(array $interfaces): ClassDeclaration
139
    {
140
        $this->interfaces = [];
141
        foreach ($interfaces as $interface) {
142
            $this->addInterface($interface);
143
        }
144
145
        return $this;
146
    }
147
148
    /**
149
     * @param string $interface
150
     *
151
     * @return self
152
     */
153
    public function addInterface(string $interface): ClassDeclaration
154
    {
155
        $this->interfaces[ltrim($interface, '\\')] = true;
156
157
        return $this;
158
    }
159
160
    /**
161
     * @param string $interface
162
     *
163
     * @return self
164
     */
165
    public function removeInterface(string $interface): ClassDeclaration
166
    {
167
        unset($this->interfaces[ltrim($interface, '\\')]);
168
169
        return $this;
170
    }
171
172
    /**
173
     * Declared interfaces.
174
     *
175
     * @return array
176
     */
177
    public function getInterfaces(): array
178
    {
179
        return array_keys($this->interfaces);
180
    }
181
182
    /**
183
     * @param string $class
184
     *
185
     * @return bool
186
     */
187
    public function hasTrait(string $class): bool
188
    {
189
        $class = ltrim($class, '\\');
190
191
        return isset($this->traits[$class]);
192
    }
193
194
    /**
195
     * Declare class traits.
196
     *
197
     * @param array $traits
198
     *
199
     * @return self
200
     */
201
    public function setTraits(array $traits): ClassDeclaration
202
    {
203
        $this->traits = [];
204
        foreach ($traits as $trait) {
205
            $this->addTrait($trait);
206
        }
207
208
        return $this;
209
    }
210
211
    /**
212
     * @param string $class
213
     *
214
     * @return self
215
     */
216
    public function addTrait(string $class): ClassDeclaration
217
    {
218
        $this->traits[ltrim($class, '\\')] = true;
219
220
        return $this;
221
    }
222
223
    /**
224
     * @param string $class
225
     *
226
     * @return self
227
     */
228
    public function removeTrait(string $class): ClassDeclaration
229
    {
230
        unset($this->traits[ltrim($class, '\\')]);
231
232
        return $this;
233
    }
234
235
    /**
236
     * @return array
237
     */
238
    public function getUses(): array
239
    {
240
        return array_keys($this->traits);
241
    }
242
243
    /**
244
     * @return ConstantAggregator|ConstantDeclaration[]
245
     */
246
    public function getConstants(): ConstantAggregator
247
    {
248
        return $this->constants;
249
    }
250
251
    /**
252
     * @param string $name
253
     *
254
     * @return ConstantDeclaration
255
     */
256
    public function constant(string $name): ConstantDeclaration
257
    {
258
        return $this->constants->get($name);
259
    }
260
261
    /**
262
     * @return PropertyAggregator|PropertyDeclaration[]
263
     */
264
    public function getProperties(): PropertyAggregator
265
    {
266
        return $this->properties;
267
    }
268
269
    /**
270
     * @param string $name
271
     *
272
     * @return PropertyDeclaration
273
     */
274
    public function property(string $name): PropertyDeclaration
275
    {
276
        return $this->properties->get($name);
277
    }
278
279
    /**
280
     * @return MethodAggregator|MethodDeclaration[]
281
     */
282
    public function getMethods(): MethodAggregator
283
    {
284
        return $this->methods;
285
    }
286
287
    /**
288
     * @param string $name
289
     *
290
     * @return MethodDeclaration
291
     */
292
    public function method(string $name): MethodDeclaration
293
    {
294
        return $this->methods->get($name);
295
    }
296
297
    /**
298
     * {@inheritdoc}
299
     *
300
     * @return self
301
     */
302
    public function replace($search, $replace): ClassDeclaration
303
    {
304
        $this->constants->replace($search, $replace);
305
        $this->properties->replace($search, $replace);
306
        $this->methods->replace($search, $replace);
307
308
        return $this;
309
    }
310
311
    /**
312
     * @param int $indentLevel
313
     *
314
     * @return string
315
     */
316
    public function render(int $indentLevel = 0): string
317
    {
318
        $result = '';
319
320
        if (!$this->docComment->isEmpty()) {
321
            $result .= $this->docComment->render($indentLevel) . "\n";
322
        }
323
324
        //Class header
325
        $header = "class {$this->getName()}";
326
327
        //Rendering extends
328
        if (!empty($this->extends)) {
329
            $header .= " extends {$this->extends}";
330
        }
331
332
        if (!empty($this->interfaces)) {
333
            $interfaces = join(", ", array_keys($this->interfaces));
334
            $header .= " implements {$interfaces}";
335
        }
336
337
        $result .= $this->addIndent($header, $indentLevel) . "\n";
338
        $result .= $this->addIndent("{", $indentLevel) . "\n";
339
340
        //Rendering class body
341
        $result .= $this->renderBody($indentLevel);
342
343
        $result = rtrim($result, "\n") . "\n";
344
        $result .= $this->addIndent("}", $indentLevel);
345
346
        return $result;
347
    }
348
349
    /**
350
     * @param int $indentLevel
351
     *
352
     * @return string
353
     */
354
    private function renderTraits(int $indentLevel = 0): string
355
    {
356
        $lines = [];
357
        foreach ($this->traits as $class => $options) {
358
            $lines[] = $this->addIndent("use {$class};", $indentLevel);
359
        }
360
361
        return join("\n", $lines);
362
    }
363
364
    /**
365
     * @param int $indentLevel
366
     *
367
     * @return string
368
     */
369
    protected function renderBody(int $indentLevel): string
370
    {
371
        $result = '';
372
        if (!empty($this->traits)) {
373
            $result .= $this->renderTraits($indentLevel + 1) . "\n\n";
374
        }
375
376
        if (!$this->constants->isEmpty()) {
377
            $result .= $this->constants->render($indentLevel + 1) . "\n\n";
378
        }
379
380
        if (!$this->properties->isEmpty()) {
381
            $result .= $this->properties->render($indentLevel + 1) . "\n\n";
382
        }
383
384
        if (!$this->methods->isEmpty()) {
385
            $result .= $this->methods->render($indentLevel + 1) . "\n\n";
386
        }
387
388
        return $result;
389
    }
390
}