GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Pull Request — master (#225)
by joseph
39:33
created

CreateDtoBodyProcess::setEntityFqn()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
cc 2
eloc 6
nc 2
nop 1
dl 0
loc 11
ccs 0
cts 10
cp 0
crap 6
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\Creation\Process\Src\Entity\DataTransferObjects;
6
7
use Doctrine\Common\Collections\Collection;
8
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\CodeHelper;
9
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\Creation\Process\ProcessInterface;
10
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\Filesystem\File;
11
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\NamespaceHelper;
12
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\ReflectionHelper;
13
use EdmondsCommerce\DoctrineStaticMeta\DoctrineStaticMeta;
14
use EdmondsCommerce\DoctrineStaticMeta\Entity\Fields\Interfaces\PrimaryKey\IdFieldInterface;
15
use ReflectionParameter;
16
use RuntimeException;
17
use ts\Reflection\ReflectionMethod;
18
19
use function defined;
20
use function in_array;
21
22
class CreateDtoBodyProcess implements ProcessInterface
23
{
24
    /**
25
     * @var DoctrineStaticMeta
26
     */
27
    private $dsm;
28
    /**
29
     * @var ReflectionHelper
30
     */
31
    private $reflectionHelper;
32
33
    /**
34
     * @var array
35
     */
36
    private $properties = [];
37
38
    /**
39
     * @var array
40
     */
41
    private $setters = [];
42
43
    /**
44
     * @var array
45
     */
46
    private $getters = [];
47
    /**
48
     * @var CodeHelper
49
     */
50
    private $codeHelper;
51
    /**
52
     * @var string
53
     */
54
    private $entityFqn;
55
    /**
56
     * @var NamespaceHelper
57
     */
58
    private $namespaceHelper;
59
60
    public function __construct(
61
        ReflectionHelper $reflectionHelper,
62
        CodeHelper $codeHelper,
63
        NamespaceHelper $namespaceHelper
64
    ) {
65
        $this->reflectionHelper = $reflectionHelper;
66
        $this->codeHelper       = $codeHelper;
67
        $this->namespaceHelper  = $namespaceHelper;
68
    }
69
70
    public function setEntityFqn(string $entityFqn)
71
    {
72
        $this->entityFqn = $entityFqn;
73
        if (false === \ts\stringContains($entityFqn, '\\Entities\\')) {
0 ignored issues
show
introduced by
The condition false === stringContains...ntityFqn, '\Entities\') is always false.
Loading history...
74
            throw new RuntimeException(
75
                'This does not look like an Entity FQN: ' . $entityFqn
76
            );
77
        }
78
        $this->dsm = new DoctrineStaticMeta($entityFqn);
79
80
        return $this;
81
    }
82
83
    public function run(File\FindReplace $findReplace): void
84
    {
85
        $this->buildArraysOfCode();
86
        $this->updateFileContents($findReplace);
87
    }
88
89
    private function buildArraysOfCode(): void
90
    {
91
        foreach ($this->dsm->getSetters() as $getterName => $setterName) {
92
            if ('getId' === $getterName) {
93
                continue;
94
            }
95
            $reflectionClassWithMethod = $this->reflectionHelper->getTraitImplementingMethod(
96
                $this->dsm->getReflectionClass(),
97
                $setterName
98
            );
99
            if (null === $reflectionClassWithMethod) {
100
                $reflectionClassWithMethod = $this->dsm->getReflectionClass();
101
            }
102
            $setter   = $reflectionClassWithMethod->getMethod($setterName);
103
            $property = $this->dsm->getPropertyNameFromSetterName($setterName);
104
            $type     = $this->getPropertyTypeFromSetter($setter);
105
            $this->setProperty($property, $type);
106
            $this->setGetterFromPropertyAndType($getterName, $property, $type);
107
            $this->setSetterFromPropertyAndType($setterName, $property, $type);
108
            $this->addIssetMethodsForProperty($property, $type);
109
        }
110
    }
111
112
    private function getPropertyTypeFromSetter(ReflectionMethod $setter): string
113
    {
114
        /**
115
         * @var ReflectionParameter $param
116
         */
117
        $param = current($setter->getParameters());
118
        $type  = $param->getType();
119
        if (null !== $type) {
120
            $type = $type->getName();
121
            if (!in_array($type, ['string', 'bool', 'int', 'float', 'object', 'array'], true)) {
122
                $type = "\\$type";
123
            }
124
            if ($param->allowsNull()) {
125
                $type = "?$type";
126
            }
127
        }
128
129
        return (string)$type;
130
    }
131
132
    private function setProperty(string $property, string $type): void
133
    {
134
        $defaultValue = $this->getDefaultValueCodeForProperty($property);
135
        $type         = $this->getPropertyVarType($type);
136
        $type         = $this->makeIdTypesNullable($property, $type);
137
138
        $code = '';
139
        $code .= "\n" . '    /**';
140
        $code .= ('' !== $type) ? "\n" . '     * @var ' . $type : '';
141
        $code .= "\n" . '     */';
142
        $code .= "\n" . '    private $' . $property . ' = ' . $defaultValue . ';';
143
144
        $this->properties[] = $code;
145
    }
146
147
    private function getDefaultValueCodeForProperty(
148
        string $property
149
    ): string {
150
        $defaultValueConst = 'DEFAULT_' . $this->codeHelper->consty($property);
151
        $fullValueString   = $this->entityFqn . '::' . $defaultValueConst;
152
        if (defined($fullValueString)) {
153
            return $this->dsm->getShortName() . '::' . $defaultValueConst;
154
        }
155
156
        return 'null';
157
    }
158
159
    private function getPropertyVarType(string $type): string
160
    {
161
        if (false === \ts\stringContains($type, '\\Entity\\Interfaces\\')) {
0 ignored issues
show
introduced by
The condition false === stringContains... '\Entity\Interfaces\') is always false.
Loading history...
162
            return $type;
163
        }
164
        $types = [];
165
        if (0 === strpos($type, '?')) {
166
            $types[] = 'null';
167
            $type    = substr($type, 1);
168
        }
169
        $types[] = $type;
170
        $types[] = $this->namespaceHelper->getEntityDtoFqnFromEntityFqn(
171
            $this->namespaceHelper->getEntityFqnFromEntityInterfaceFqn($type)
172
        );
173
174
        return implode('|', $types);
175
    }
176
177
    private function makeIdTypesNullable(string $property, string $type): string
178
    {
179
        if (IdFieldInterface::PROP_ID === $property && 0 !== strpos($type, '?')) {
180
            $type = "?$type";
181
        }
182
183
        return $type;
184
    }
185
186
    /**
187
     * @SuppressWarnings(PHPMD.ElseExpression)
188
     * @param string $getterName
189
     * @param string $property
190
     * @param string $type
191
     */
192
    private function setGetterFromPropertyAndType(
193
        string $getterName,
194
        string $property,
195
        string $type
196
    ): void {
197
        $type            = $this->makeIdTypesNullable($property, $type);
198
        $code            = '';
199
        $code            .= "\n    public function $getterName()" . (('' !== $type) ? ": $type" : '');
200
        $code            .= "\n    {";
201
        $code            .= $this->getGetterBody($property, $type);
202
        $code            .= "\n    }\n";
203
        $this->getters[] = $code;
204
    }
205
206
    private function getGetterBody(
207
        string $property,
208
        string $type
209
    ): string {
210
        if ('\\' . Collection::class === $type) {
211
            return "\n        return \$this->$property ?? \$this->$property = new ArrayCollection();";
212
        }
213
        if (\ts\stringContains($type, '\\Entity\\Interfaces\\')) {
214
            $getterCode = '';
215
            $getterCode .= "\n        if(null === \$this->$property){";
216
            $getterCode .= "\n            return \$this->$property;";
217
            $getterCode .= "\n        }";
218
            if (0 === strpos($type, '?')) {
219
                $type = substr($type, 1);
220
            }
221
            $getterCode .= "\n        if(\$this->$property instanceof $type){";
222
            $getterCode .= "\n            return \$this->$property;";
223
            $getterCode .= "\n        }";
224
            $getterCode .= "\n        throw new \RuntimeException(";
225
            $getterCode .= "\n            '\$this->$property is not an Entity, but is '. \get_class(\$this->$property)";
226
            $getterCode .= "\n        );";
227
228
            return $getterCode;
229
        }
230
231
        return "\n        return \$this->$property;";
232
    }
233
234
    private function setSetterFromPropertyAndType(
235
        string $setterName,
236
        string $property,
237
        string $type
238
    ): void {
239
        $code            = '';
240
        $code            .= "\n    public function $setterName($type \$$property): self ";
241
        $code            .= "\n    {";
242
        $code            .= "\n        \$this->$property = \$$property;";
243
        $code            .= "\n        return \$this;";
244
        $code            .= "\n    }\n";
245
        $this->setters[] = $code;
246
        if (\ts\stringContains($type, '\\Entity\\Interfaces\\')) {
247
            $this->setDtoGetterAndSetterForEntityProperty($setterName, $property, $type);
248
        }
249
    }
250
251
    private function setDtoGetterAndSetterForEntityProperty(
252
        string $setterName,
253
        string $property,
254
        string $entityInterfaceFqn
255
    ): void {
256
        $dtoFqn          = $this->namespaceHelper->getEntityDtoFqnFromEntityFqn(
257
            $this->namespaceHelper->getEntityFqnFromEntityInterfaceFqn($entityInterfaceFqn)
258
        );
259
        $setterCode      = '';
260
        $setterCode      .= "\n    public function ${setterName}Dto($dtoFqn \$$property): self ";
261
        $setterCode      .= "\n    {";
262
        $setterCode      .= "\n        \$this->$property = \$$property;";
263
        $setterCode      .= "\n        return \$this;";
264
        $setterCode      .= "\n    }\n";
265
        $this->setters[] = $setterCode;
266
267
        $getterName = 'get' . substr($setterName, 3);
268
        $getterCode = '';
269
        $getterCode .= "\n    public function $getterName" . "Dto(): $dtoFqn";
270
        $getterCode .= "\n    {";
271
        $getterCode .= "\n        if(null === \$this->$property){";
272
        $getterCode .= "\n            return \$this->$property;";
273
        $getterCode .= "\n        }";
274
        if (0 === strpos($dtoFqn, '?')) {
275
            $dtoFqn = substr($dtoFqn, 1);
276
        }
277
        $getterCode      .= "\n        if(\$this->$property instanceof $dtoFqn){";
278
        $getterCode      .= "\n            return \$this->$property;";
279
        $getterCode      .= "\n        }";
280
        $getterCode      .= "\n        throw new \RuntimeException(";
281
        $getterCode      .= "\n            '\$this->$property is not a DTO, but is '. \get_class(\$this->$property)";
282
        $getterCode      .= "\n        );";
283
        $getterCode      .= "\n    }\n";
284
        $this->getters[] = $getterCode;
285
    }
286
287
    private function addIssetMethodsForProperty(string $property, string $type): void
288
    {
289
        if (false === \ts\stringContains($type, '\\Entity\\Interfaces\\')) {
0 ignored issues
show
introduced by
The condition false === stringContains... '\Entity\Interfaces\') is always false.
Loading history...
290
            return;
291
        }
292
        $methodName      = 'isset' . ucfirst($property) . 'AsDto';
293
        $getterCodeDto   = '';
294
        $getterCodeDto   .= "\n    public function $methodName(): bool";
295
        $getterCodeDto   .= "\n    {";
296
        $getterCodeDto   .= "\n        return \$this->$property instanceof DataTransferObjectInterface;";
297
        $getterCodeDto   .= "\n    }\n";
298
        $this->getters[] = $getterCodeDto;
299
300
        $methodName       = 'isset' . ucfirst($property) . 'AsEntity';
301
        $getterCodeEntity = '';
302
        $getterCodeEntity .= "\n    public function $methodName(): bool";
303
        $getterCodeEntity .= "\n    {";
304
        $getterCodeEntity .= "\n        return \$this->$property instanceof EntityInterface;";
305
        $getterCodeEntity .= "\n    }\n";
306
        $this->getters[]  = $getterCodeEntity;
307
    }
308
309
    private function updateFileContents(
310
        File\FindReplace $findReplace
311
    ): void {
312
        sort($this->properties, SORT_STRING);
313
        sort($this->getters, SORT_STRING);
314
        sort($this->setters, SORT_STRING);
315
316
        $body = implode("\n", $this->properties) .
317
                "\n\n" .
318
                implode("\n", $this->getters) .
319
                "\n" .
320
                implode("\n", $this->setters);
321
322
        $findReplace->findReplaceRegex('%{(.+)}%s', "{\n\$1\n$body\n}");
323
    }
324
}
325