Completed
Pull Request — master (#29)
by Frank
06:52 queued 43s
created

CodeDumper::dumpTestHelpers()   B

Complexity

Conditions 5
Paths 8

Size

Total Lines 57
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 25
CRAP Score 5

Importance

Changes 0
Metric Value
dl 0
loc 57
c 0
b 0
f 0
ccs 25
cts 25
cp 1
rs 8.7433
cc 5
eloc 30
nc 8
nop 1
crap 5

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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