Completed
Pull Request — master (#21)
by Frank
06:36 queued 40s
created

CodeDumper   A

Complexity

Total Complexity 35

Size/Duplication

Total Lines 302
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Test Coverage

Coverage 99.22%

Importance

Changes 0
Metric Value
wmc 35
lcom 1
cbo 4
dl 0
loc 302
ccs 128
cts 129
cp 0.9922
rs 9
c 0
b 0
f 0

11 Methods

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