Completed
Push — master ( e0ffea...b38ea1 )
by Francis
02:07
created

SwaggerOperationsGenerator::processOperations()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 11
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 4
c 0
b 0
f 0
dl 0
loc 11
rs 10
cc 3
nc 3
nop 3
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Prometee\SwaggerClientGenerator\Swagger;
6
7
use Exception;
8
use Prometee\SwaggerClientGenerator\Base\Factory\ClassGeneratorFactoryInterface;
9
use Prometee\SwaggerClientGenerator\Base\Generator\ClassGenerator;
10
use Prometee\SwaggerClientGenerator\Base\Generator\ClassGeneratorInterface;
11
use Prometee\SwaggerClientGenerator\Base\Generator\Method\MethodGeneratorInterface;
12
use Prometee\SwaggerClientGenerator\Base\Generator\Method\MethodParameterGeneratorInterface;
13
use Prometee\SwaggerClientGenerator\Swagger\Helper\SwaggerOperationsHelperInterface;
14
use Prometee\SwaggerClientGenerator\Swagger\Factory\OperationsMethodGeneratorFactoryInterface;
15
16
class SwaggerOperationsGenerator implements SwaggerOperationsGeneratorInterface
17
{
18
    /** @var ClassGeneratorFactoryInterface */
19
    protected $classGeneratorFactory;
20
    /** @var OperationsMethodGeneratorFactoryInterface */
21
    protected $methodFactory;
22
    /** @var SwaggerOperationsHelperInterface */
23
    protected $helper;
24
25
    /** @var string */
26
    protected $folder = '';
27
    /** @var string */
28
    protected $namespace = '';
29
    /** @var string */
30
    protected $modelNamespace = '';
31
    /** @var string */
32
    protected $indent = '    ';
33
    /** @var array */
34
    protected $paths = [];
35
    /** @var ClassGenerator[] */
36
    protected $classGenerators = [];
37
    /** @var string[] */
38
    protected $throwsClasses = [];
39
    /** @var string|null */
40
    protected $abstractOperationClass;
41
    /** @var bool */
42
    protected $overwrite = false;
43
44
    /**
45
     * @param ClassGeneratorFactoryInterface $classGeneratorFactory
46
     * @param OperationsMethodGeneratorFactoryInterface $methodFactory
47
     * @param SwaggerOperationsHelperInterface $helper
48
     */
49
    public function __construct(
50
        ClassGeneratorFactoryInterface $classGeneratorFactory,
51
        OperationsMethodGeneratorFactoryInterface $methodFactory,
52
        SwaggerOperationsHelperInterface $helper
53
    )
54
    {
55
        $this->classGeneratorFactory = $classGeneratorFactory;
56
        $this->methodFactory = $methodFactory;
57
        $this->helper = $helper;
58
    }
59
60
    /**
61
     * {@inheritDoc}
62
     */
63
    public function configure(string $folder, string $namespace, string $modelNamespace, string $indent = '    ')
64
    {
65
        $this->folder = $folder;
66
        $this->namespace = $namespace;
67
        $this->modelNamespace = $modelNamespace;
68
        $this->indent = $indent;
69
70
        $this->paths = [];
71
        $this->classGenerators = [];
72
        $this->throwsClasses = [];
73
    }
74
75
    /**
76
     * {@inheritDoc}
77
     *
78
     * @throws Exception
79
     */
80
    public function generate(): bool
81
    {
82
        foreach ($this->paths as $path => $operationConfigurations) {
83
            $this->generateClass($path, $operationConfigurations);
84
        }
85
86
        return true;
87
    }
88
89
    /**
90
     * {@inheritDoc}
91
     *
92
     * @throws Exception
93
     */
94
    public function processPaths(array $json): bool
95
    {
96
        if (!isset($json['paths'])) {
97
            return false;
98
        }
99
        foreach ($this->paths as $path => $operationConfigurations) {
100
            $this->generateClass($path, $operationConfigurations);
101
        }
102
103
        return true;
104
    }
105
106
    /**
107
     * {@inheritDoc}
108
     *
109
     * @throws Exception
110
     */
111
    public function generateClass(string $path, array $operationConfigurations): ?ClassGeneratorInterface
112
    {
113
        $filePath = $this->getFilePathFromPath(
114
            $path,
115
            '',
116
            static::CLASS_SUFFIX
117
        );
118
        if (!$this->overwrite && is_file($filePath)) {
119
            return null;
120
        }
121
122
        // Class
123
        $classGenerator = $this->getOrCreateClassGenerator($path, $filePath);
124
125
        // Methods
126
        $this->processOperations($classGenerator, $path, $operationConfigurations);
127
128
        // File creation
129
        $directory = dirname($filePath);
130
        if (!is_dir($directory)) {
131
            mkdir($directory, 0777, true);
132
        }
133
134
        if (false === file_put_contents($filePath, $classGenerator->generate($this->indent))) {
135
            throw new Exception(sprintf('Unable to generate the class : "%s" !', $filePath));
136
        }
137
138
        return $classGenerator;
139
    }
140
141
    /**
142
     * {@inheritDoc}
143
     */
144
    protected function getOrCreateClassGenerator(string $path, string $filePath): ClassGeneratorInterface
145
    {
146
        [$namespace, $className] = $this->getClassNameAndNamespaceFromPath(
147
            $path,
148
            '',
149
            static::CLASS_SUFFIX
150
        );
151
152
        if ($this->hasClassGenerator($filePath)) {
153
            return $this->classGenerators[$filePath];
154
        }
155
156
        $classGenerator = $this->classGeneratorFactory->createClassGenerator();
157
        $classGenerator->configure($namespace, $className);
158
        if ($this->abstractOperationClass !== null) {
159
            $classGenerator->setExtendClass($this->abstractOperationClass);
160
        }
161
162
        $this->classGenerators[$filePath] = $classGenerator;
163
164
        return $classGenerator;
165
    }
166
167
    /**
168
     * {@inheritDoc}
169
     */
170
    protected function hasClassGenerator(string $filePath): bool
171
    {
172
        return isset($this->classGenerators[$filePath]);
173
    }
174
175
    /**
176
     * {@inheritDoc}
177
     */
178
    public function processOperations(
179
        ClassGeneratorInterface $classGenerator,
180
        string $path,
181
        array $operationConfigurations
182
    ): void
183
    {
184
        foreach ($operationConfigurations as $operation => $operationConfiguration) {
185
            if (!is_array($operationConfiguration)) {
186
                continue;
187
            }
188
            $this->processOperation($classGenerator, $path, $operation, $operationConfiguration);
189
        }
190
    }
191
192
    /**
193
     * {@inheritDoc}
194
     */
195
    public function processOperation(
196
        ClassGeneratorInterface $classGenerator,
197
        string $path,
198
        string $operation,
199
        array $operationConfiguration
200
    ): void {
201
202
        $operation = strtolower($operation);
203
        if (!in_array($operation, ['get', 'post', 'put', 'patch', 'delete'])) {
204
            return;
205
        }
206
207
        $returnType = null;
208
        if (isset($operationConfiguration['responses'])) {
209
            $returnType = $this->helper::getReturnType($operationConfiguration['responses']);
210
        }
211
        if ($returnType !== null) {
212
            $returnType = $this->getPhpNameFromType($returnType);
213
        } else {
214
            $returnType = 'void';
215
        }
216
217
        $operationMethodGenerator = $this->methodFactory->createOperationMethodGenerator(
218
            $classGenerator->getUsesGenerator()
219
        );
220
        $operationMethodName = $this->helper::getOperationMethodName($path, $operation, $operationConfiguration);
221
        $operationMethodGenerator->setName($operationMethodName);
222
        $operationMethodGenerator->addReturnType($returnType);
223
224
        if (isset($operationConfiguration['description'])) {
225
            $operationMethodGenerator->getPhpDocGenerator()->addDescriptionLine($operationConfiguration['description']);
226
        }
227
        $operationMethodGenerator->getPhpDocGenerator()->addDescriptionLine(
228
            sprintf('path: %s', $path)
229
        );
230
        $operationMethodGenerator->getPhpDocGenerator()->addDescriptionLine(
231
            sprintf('method: %s', strtoupper($operation))
232
        );
233
234
        $operationParameters = [];
235
        if (isset($operationConfiguration['parameters'])) {
236
            $operationParameters = $operationConfiguration['parameters'];
237
            $this->processOperationParameters($classGenerator, $operationMethodGenerator, $operationParameters);
238
        }
239
240
        foreach ($this->throwsClasses as $throwsClass => $className) {
241
            $classGenerator->getUsesGenerator()->addUse($throwsClass);
242
            $operationMethodGenerator->getPhpDocGenerator()->addThrowsLine($className);
243
        }
244
245
        $operationMethodGenerator->addMethodBodyFromSwaggerConfiguration(
246
            $path,
247
            $operation,
248
            $operationParameters,
249
            $this->indent
250
        );
251
252
        $classGenerator->getMethodsGenerator()->addMethod($operationMethodGenerator);
253
    }
254
255
    /**
256
     * {@inheritDoc}
257
     */
258
    public function processOperationParameters(ClassGeneratorInterface $classGenerator, MethodGeneratorInterface $methodGenerator, array $operationParameters): void
259
    {
260
        foreach ($operationParameters as $parameterConfiguration) {
261
            $methodParameterGenerator = $this->createAnOperationParameter($classGenerator, $parameterConfiguration);
262
            if ($methodParameterGenerator === null) {
263
                continue;
264
            }
265
            $methodGenerator->addParameter($methodParameterGenerator);
266
        }
267
    }
268
269
    /**
270
     * {@inheritDoc}
271
     */
272
    public function createAnOperationParameter(ClassGeneratorInterface $classGenerator, array $parameterConfiguration): ?MethodParameterGeneratorInterface
273
    {
274
        if (!isset($parameterConfiguration['name'])) {
275
            return null;
276
        }
277
278
        $type = $this->helper::getPhpTypeFromSwaggerConfiguration($parameterConfiguration);
279
        if ($type !== null) {
280
            $type = $this->getPhpNameFromType($type);
281
        }
282
        $types = [$type];
283
284
        $name = lcfirst($this->helper::cleanStr($parameterConfiguration['name']));
285
286
        $value = $this->buildValueForOperationParameter($parameterConfiguration, $type);
287
288
        $description = '';
289
        if (isset($parameterConfiguration['description'])) {
290
            $description = $parameterConfiguration['description'];
291
        }
292
293
        $methodParameterGenerator = $this->methodFactory->createMethodParameterGenerator(
294
            $classGenerator->getUsesGenerator()
295
        );
296
        $methodParameterGenerator->configure(
297
            $types,
298
            $name,
299
            $value,
300
            false,
301
            $description
302
        );
303
304
        return $methodParameterGenerator;
305
    }
306
307
    /**
308
     * {@inheritDoc}
309
     */
310
    public function buildValueForOperationParameter(array $parameterConfiguration, ?string $type): ?string
311
    {
312
        $value = null;
313
        if (isset($parameterConfiguration['default'])) {
314
            $value = (string) $parameterConfiguration['default'];
315
            if ($type === 'string') {
316
                $value = "'" . addslashes($value) . "'";
317
            }
318
        }
319
320
        if (isset($parameterConfiguration['required'])) {
321
            if ((bool)$parameterConfiguration['required']) {
322
                $value = null;
323
            } else {
324
                $value = $value ?? 'null';
325
            }
326
        }
327
328
        return $value;
329
    }
330
331
    /**
332
     * {@inheritDoc}
333
     */
334
    public function getClassNameAndNamespaceFromPath(string $path, string $classPrefix = '', string $classSuffix = ''): array
335
    {
336
        $classPath = $this->helper::getClassPathFromPath($path);
337
        $classParts = explode('/', $classPath);
338
        $className = array_pop($classParts);
339
        $namespace = implode('\\', $classParts);
340
        $namespace = $this->namespace . ($namespace === '' ? '' : $namespace);
341
342
        return [
343
            $namespace,
344
            $classPrefix . $className . $classSuffix,
345
        ];
346
    }
347
348
    /**
349
     * {@inheritDoc}
350
     */
351
    public function getPhpNameFromType(string $type): string
352
    {
353
        if (preg_match('$^#/definitions/$', $type)) {
354
            $className = preg_replace('$#/definitions/$', '', $type);
355
            $className = $this->helper::camelize($className);
356
            return '\\' . $this->modelNamespace . '\\' . $className;
357
        }
358
359
        return $type;
360
    }
361
362
    /**
363
     * {@inheritDoc}
364
     */
365
    protected function getFilePathFromPath(string $path, string $classPrefix = '', string $classSuffix = ''): string
366
    {
367
        $filePath = sprintf('%s/%s%s%s.php', $this->folder, $classPrefix, $this->helper::getClassPathFromPath($path), $classSuffix);
368
369
        return preg_replace('#/' . $classPrefix . $classSuffix . '\.php$#', $classPrefix . $classSuffix . '.php', $filePath);
370
    }
371
372
    /**
373
     * {@inheritDoc}
374
     */
375
    public function getPaths(): array
376
    {
377
        return $this->paths;
378
    }
379
380
    /**
381
     * {@inheritDoc}
382
     */
383
    public function setPaths(array $paths): void
384
    {
385
        $this->paths = $paths;
386
    }
387
388
    /**
389
     * {@inheritDoc}
390
     */
391
    public function getHelper(): SwaggerOperationsHelperInterface
392
    {
393
        return $this->helper;
394
    }
395
396
    /**
397
     * {@inheritDoc}
398
     */
399
    public function setHelper(SwaggerOperationsHelperInterface $helper): void
400
    {
401
        $this->helper = $helper;
402
    }
403
404
    /**
405
     * {@inheritDoc}
406
     */
407
    public function getThrowsClasses(): array
408
    {
409
        return $this->throwsClasses;
410
    }
411
412
    /**
413
     * {@inheritDoc}
414
     */
415
    public function setThrowsClasses(array $throwsClasses): void
416
    {
417
        $this->throwsClasses = $throwsClasses;
418
    }
419
420
    /**
421
     * {@inheritDoc}
422
     */
423
    public function getAbstractOperationClass(): ?string
424
    {
425
        return $this->abstractOperationClass;
426
    }
427
428
    /**
429
     * {@inheritDoc}
430
     */
431
    public function setAbstractOperationClass(?string $abstractOperationClass): void
432
    {
433
        $this->abstractOperationClass = $abstractOperationClass;
434
    }
435
436
    /**
437
     * {@inheritDoc}
438
     */
439
    public function isOverwrite(): bool
440
    {
441
        return $this->overwrite;
442
    }
443
444
    /**
445
     * {@inheritDoc}
446
     */
447
    public function setOverwrite(bool $overwrite): void
448
    {
449
        $this->overwrite = $overwrite;
450
    }
451
}
452