Completed
Push — master ( e3df61...d27abb )
by Frank
02:55
created

CodeDumper::dumpConstructorValue()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 13
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 13
ccs 7
cts 7
cp 1
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 7
nc 4
nop 2
crap 3
1
<?php
2
3
namespace EventSauce\EventSourcing\CodeGeneration;
4
5
use EventSauce\EventSourcing\Event;
6
use LogicException;
7
use const null;
8
use function array_filter;
9
use function join;
10
use function sprintf;
11
use function ucfirst;
12
use function var_export;
13
14
class CodeDumper
15
{
16
    /**
17
     * @var DefinitionGroup
18
     */
19
    private $definitionGroup;
20
21 5
    public function dump(DefinitionGroup $definitionGroup, bool $withHelpers = true): string
22
    {
23 5
        $this->definitionGroup = $definitionGroup;
24 5
        $eventsCode = $this->dumpEvents($definitionGroup->events(), $withHelpers);
25 5
        $commandCode = $this->dumpCommands($definitionGroup->commands());
26 4
        $namespace = $definitionGroup->namespace();
27 4
        $allCode = join(array_filter([$eventsCode, $commandCode]), "\n\n");
28
29
        return <<<EOF
30
<?php
31
32 4
namespace $namespace;
33
34
use EventSauce\\EventSourcing\\Event;
35
36 4
$allCode
37
38
EOF;
39
    }
40
41 5
    private function dumpEvents(array $events, bool $withHelpers): string
42
    {
43 5
        $code = [];
44
45 5
        if (empty($events)) {
46 2
            return '';
47
        }
48
49 4
        foreach ($events as $event) {
50 4
            $name = $event->name();
51 4
            $fields = $this->dumpFields($event);
52 4
            $constructor = $this->dumpConstructor($event);
53 4
            $methods = $this->dumpMethods($event);
54 4
            $deserializer = $this->dumpSerializationMethods($event);
55 4
            $testHelpers = $withHelpers ? $this->dumpTestHelpers($event) : '';
56
57 4
            $code[] = <<<EOF
58 4
final class $name implements Event
59
{
60 4
$fields$constructor$methods$deserializer
61
62 4
$testHelpers}
63
64
65
EOF;
66
        }
67
68 4
        return rtrim(join('', $code));
69
    }
70
71 5
    private function dumpFields(DefinitionWithFields $definition): string
72
    {
73 5
        $fields = $this->fieldsFromDefinition($definition);
74 4
        $code = [];
75 4
        $code[] = <<<EOF
76
77
EOF;
78
79
80 4
        foreach ($fields as $field) {
81 4
            $name = $field['name'];
82 4
            $type = $field['type'];
83
84 4
            $code[] = <<<EOF
85
    /**
86 4
     * @var $type
87
     */
88 4
    private \$$name;
89
90
91
EOF;
92
93
        }
94
95 4
        return join('', $code);
96
    }
97
98 4
    private function dumpConstructor(DefinitionWithFields $definition): string
99
    {
100 4
        $arguments = [];
101 4
        $assignments = [];
102 4
        $fields = $this->fieldsFromDefinition($definition);
103
104 4
        if (empty($fields)) {
105 1
            return '';
106
        }
107
108 4
        foreach ($fields as $field) {
109 4
            $arguments[] = sprintf('        %s $%s', $field['type'], $field['name']);
110 4
            $assignments[] = sprintf('        $this->%s = $%s;', $field['name'], $field['name']);
111
        }
112
113 4
        $arguments = join(",\n", $arguments);
114 4
        $assignments = join("\n", $assignments);
115
116
117
        return <<<EOF
118
    public function __construct(
119 4
$arguments
120
    ) {
121 4
$assignments
122
    }
123
124
125
EOF;
126
127
    }
128
129 4
    private function dumpMethods(DefinitionWithFields $command): string
130
    {
131 4
        $methods = [];
132
133 4
        foreach ($this->fieldsFromDefinition($command) as $field) {
134 4
            $methods[] = <<<EOF
135 4
    public function {$field['name']}(): {$field['type']}
136
    {
137 4
        return \$this->{$field['name']};
138
    }
139
140
141
EOF;
142
        }
143
144 4
        return empty($methods) ? '' : rtrim(join('', $methods)) . "\n";
145
    }
146
147 4
    private function dumpSerializationMethods(EventDefinition $event)
148
    {
149 4
        $name = $event->name();
150 4
        $arguments = [];
151 4
        $serializers = [];
152
153 4
        foreach ($this->fieldsFromDefinition($event) as $field) {
154 4
            $parameter = sprintf('$payload[\'%s\']', $field['name']);
155 4
            $template = $event->deserializerForField($field['name'])
156 4
                ?: $event->deserializerForType($field['type']);
157 4
            $arguments[] = trim(strtr($template, ['{type}' => $field['type'], '{param}' => $parameter]));
158
159 4
            $property = sprintf('$this->%s', $field['name']);
160 4
            $template = $event->serializerForField($field['name'])
161 4
                ?: $event->serializerForType($field['type']);
162 4
            $template = sprintf("'%s' => %s", $field['name'], $template);
163 4
            $serializers[] = trim(strtr($template, ['{type}' => $field['type'], '{param}' => $property]));
164
        }
165
166 4
        $arguments = preg_replace('/^.{2,}$/m', '            $0', join(",\n", $arguments));
167
168 4
        if ( ! empty($arguments)) {
169 4
            $arguments = "\n$arguments";
170
        }
171
172 4
        $serializers = preg_replace('/^.{2,}$/m', '            $0', join(",\n", $serializers));
173
174 4
        if ( ! empty($serializers)) {
175 4
            $serializers = "\n$serializers,\n        ";
176
        }
177
178
        return <<<EOF
179
    public static function fromPayload(array \$payload): Event
180
    {
181 4
        return new $name($arguments);
182
    }
183
184
    public function toPayload(): array
185
    {
186 4
        return [$serializers];
187
    }
188
EOF;
189
190
191
    }
192
193 2
    private function dumpTestHelpers(EventDefinition $event): string
194
    {
195 2
        $constructor = [];
196 2
        $constructorArguments = '';
197 2
        $constructorValues = [];
198 2
        $helpers = [];
199
200 2
        foreach ($this->fieldsFromDefinition($event) as $field) {
201 2
            if ($field['example'] === null) {
202 2
                $constructor[] = ucfirst($field['name']);
203
204 2
                if ($constructorArguments !== '') {
205 1
                    $constructorArguments .= ', ';
206
                }
207
208 2
                $constructorArguments .= sprintf('%s $%s', $field['type'], $field['name']);
209 2
                $constructorValues[] = sprintf('$%s', $field['name']);
210
            } else {
211 2
                $constructorValues[] = $this->dumpConstructorValue($field, $event);
212 2
                $method = sprintf('with%s', ucfirst($field['name']));
213 2
                $helpers[] = <<<EOF
214
    /**
215
     * @codeCoverageIgnore
216
     */
217 2
    public function $method({$field['type']} \${$field['name']}): {$event->name()}
218
    {
219 2
        \$this->{$field['name']} = \${$field['name']};
220
221
        return \$this;
222
    }
223
224
225
EOF;
226
            }
227
        }
228
229 2
        $constructor = sprintf('with%s', join('And', $constructor));
230 2
        $constructorValues = join(",\n            ", $constructorValues);
231
232 2
        if ($constructorValues !== "") {
233 2
            $constructorValues = "\n            $constructorValues\n        ";
234
        }
235
236 2
        $helpers[] = <<<EOF
237
    /**
238
     * @codeCoverageIgnore
239
     */
240 2
    public static function $constructor($constructorArguments): {$event->name()}
241
    {
242 2
        return new {$event->name()}($constructorValues);
243
    }
244
245
246
EOF;
247
248
249 2
        return rtrim(join('', $helpers))."\n";
250
    }
251
252 2
    private function dumpConstructorValue(array $field, EventDefinition $event): string
253
    {
254 2
        $parameter = rtrim($field['example']);
255
256 2
        if (gettype($parameter) === $field['type']) {
257 1
            $parameter = var_export($parameter, true);
258
        }
259
260 2
        $template = $event->deserializerForField($field['name'])
261 2
            ?: $event->deserializerForType($field['type']);
262
263 2
        return rtrim(strtr($template, ['{type}' => $field['type'], '{param}' => $parameter]));
264
    }
265
266
    /**
267
     * @param CommandDefinition[] $commands
268
     * @return string
269
     */
270 5
    private function dumpCommands(array $commands): string
271
    {
272 5
        $code = [];
273
274 5
        foreach ($commands as $command) {
275 4
            $code[] = <<<EOF
276 5
final class {$command->name()}
277
{
278 5
{$this->dumpFields($command)}{$this->dumpConstructor($command)}{$this->dumpMethods($command)}}
279
280
281
EOF;
282
        }
283
284 4
        return rtrim(join('', $code));
285
    }
286
287
    /**
288
     * @param DefinitionWithFields $definition
289
     * @return array
290
     */
291 5
    private function fieldsFromDefinition(DefinitionWithFields $definition): array
292
    {
293 5
        $fields = $this->fieldsFrom($definition->fieldsFrom());
294
295 4
        foreach ($definition->fields() as $field) {
296 4
            array_push($fields, $field);
297
        }
298
299 4
        return $fields;
300
    }
301
302 5
    private function fieldsFrom(string $fieldsFrom): array
303
    {
304 5
        if (empty($fieldsFrom)) {
305 4
            return [];
306
        }
307
308 2
        foreach ($this->definitionGroup->events() as $event) {
309 1
            if ($event->name() === $fieldsFrom) {
310 1
                return $event->fields();
311
            }
312
        }
313
314 2
        foreach ($this->definitionGroup->commands() as $command) {
315 2
            if ($command->name() === $fieldsFrom) {
316 2
                return $command->fields();
317
            }
318
        }
319
320 1
        throw new LogicException("Could not inherit fields from {$fieldsFrom}.");
321
    }
322
}