Passed
Push — master ( 870a57...334d92 )
by Mikaël
13:03 queued 09:40
created

Generator   F

Complexity

Total Complexity 77

Size/Duplication

Total Lines 390
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 3
Bugs 0 Features 0
Metric Value
eloc 141
c 3
b 0
f 0
dl 0
loc 390
ccs 90
cts 90
cp 1
rs 2.24
wmc 77

44 Methods

Rating   Name   Duplication   Size   Complexity  
A getOptionSuffix() 0 3 2
A parse() 0 3 1
A initDirectory() 0 8 2
A getServiceName() 0 3 1
A generatePackage() 0 7 1
A getSoapClient() 0 3 1
A initialize() 0 8 1
A initWsdl() 0 5 1
A getStructByNameAndType() 0 3 1
A getServices() 0 16 5
A getStructs() 0 3 1
A setOptions() 0 5 1
A setOptionOrigin() 0 6 1
A setOptionNamespacePrefix() 0 5 1
A getOptionDestination() 0 8 2
A getOptionPrefix() 0 3 2
A doParse() 0 5 1
A __call() 0 15 5
A getWsdl() 0 3 1
A setWsdl() 0 5 1
A getServiceMethod() 0 3 2
A setOptionDestination() 0 9 2
A getStructByName() 0 5 2
A getContainers() 0 3 1
A getService() 0 3 1
A __construct() 0 5 1
A getModelInstanceFromJsonArrayEntry() 0 3 1
A doGenerate() 0 5 1
A setOptionSoapOptions() 0 9 2
A getParsers() 0 3 1
A initSoapClient() 0 7 2
A getGather() 0 3 1
A getOptions() 0 3 1
A instanceFromSerializedJson() 0 24 5
A doSanityChecks() 0 13 4
A getUrlContent() 0 15 4
A initParsers() 0 7 2
A jsonSerialize() 0 5 1
A initContainers() 0 7 2
A addSchemaToWsdl() 0 7 3
A initFiles() 0 7 2
A setOptionComposerName() 0 9 2
A getFiles() 0 3 1
A getOptionNamespacePrefix() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like Generator often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Generator, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
declare(strict_types=1);
4
5
namespace WsdlToPhp\PackageGenerator\Generator;
6
7
use InvalidArgumentException;
8
use JsonSerializable;
9
use WsdlToPhp\PackageGenerator\ConfigurationReader\GeneratorOptions;
10
use WsdlToPhp\PackageGenerator\Container\Model\Service as ServiceContainer;
11
use WsdlToPhp\PackageGenerator\Container\Model\Struct as StructContainer;
12
use WsdlToPhp\PackageGenerator\Model\AbstractModel;
13
use WsdlToPhp\PackageGenerator\Model\EmptyModel;
14
use WsdlToPhp\PackageGenerator\Model\Method;
15
use WsdlToPhp\PackageGenerator\Model\Schema;
16
use WsdlToPhp\PackageGenerator\Model\Service;
17
use WsdlToPhp\PackageGenerator\Model\Struct;
18
use WsdlToPhp\PackageGenerator\Model\Wsdl;
19
20
/**
21
 * @method string getOptionCategory()
22
 * @method self   setOptionCategory(string $category)
23
 * @method string getOptionGatherMethods()
24
 * @method self   setOptionGatherMethods(string $gatherMethods)
25
 * @method bool   getOptionGenericConstantsNames()
26
 * @method self   setOptionGenericConstantsNames(bool $genericConstantsNames)
27
 * @method bool   getOptionGenerateTutorialFile()
28
 * @method self   setOptionGenerateTutorialFile(bool $generateTutorialFile)
29
 * @method string getOptionNamespace()
30
 * @method self   setOptionNamespace(string $namespace)
31
 * @method bool   getOptionNamespaceDictatesDirectories()
32
 * @method self   setOptionNamespaceDictatesDirectories(bool $namespaceDictatesDirectories)
33
 * @method array  getOptionAddComments()
34 358
 * @method self   setOptionAddComments(array $addComments)
35
 * @method bool   getOptionStandalone()
36
 * @method self   setOptionStandalone(bool $standalone)
37 358
 * @method bool   getOptionValidation()
38 358
 * @method self   setOptionValidation(bool $validation)
39
 * @method string getOptionStructClass()
40 354
 * @method self   setOptionStructClass(string $structClass)
41
 * @method string getOptionStructArrayClass()
42 66
 * @method self   setOptionStructArrayClass(string $structArrayClass)
43
 * @method string getOptionStructEnumClass()
44 66
 * @method self   setOptionStructEnumClass(string $structEnumClass)
45
 * @method string getOptionSoapClientClass()
46
 * @method self   setOptionSoapClientClass(string $soapClientClass)
47 26
 * @method self   setOptionPrefix(string $optionPrefix)
48
 * @method self   setOptionSuffix(string $optionSuffix)
49 26
 * @method string getOptionBasicLogin()
50
 * @method self   setOptionBasicLogin(string $optionBasicLogin)
51
 * @method string getOptionBasicPassword()
52 18
 * @method self   setOptionBasicPassword(string $optionBasicPassword)
53
 * @method string getOptionProxyHost()
54
 * @method self   setOptionProxyHost(string $optionProxyHost)
55 18
 * @method string getOptionProxyPort()
56 14
 * @method self   setOptionProxyPort(string $optionProxyPort)
57 14
 * @method string getOptionProxyLogin()
58 12
 * @method self   setOptionProxyLogin(string $optionProxyLogin)
59
 * @method string getOptionProxyPassword()
60
 * @method self   setOptionProxyPassword(string $optionProxyPassword)
61
 * @method string getOptionOrigin()
62 16
 * @method string getOptionSrcDirname()
63
 * @method self   setOptionSrcDirname(string $optionSrcDirname)
64 16
 * @method array  getOptionSoapOptions()
65
 * @method string getOptionComposerName()
66
 * @method array  getOptionComposerSettings()
67
 * @method self   setOptionComposerSettings(array $optionComposerSettings)
68
 * @method string getOptionStructsFolder()
69
 * @method self   setOptionStructsFolder(string $optionStructsFolder)
70
 * @method string getOptionArraysFolder()
71
 * @method self   setOptionArraysFolder(string $optionArraysFolder)
72
 * @method string getOptionEnumsFolder()
73
 * @method self   setOptionEnumsFolder(string $optionEnumsFolder)
74
 * @method string getOptionServicesFolder()
75 444
 * @method self   setOptionServicesFolder(string $optionServicesFolder)
76
 * @method bool   getOptionSchemasSave()
77 444
 * @method self   setOptionSchemasSave(bool $optionSchemasSave)
78
 * @method string getOptionSchemasFolder()
79 444
 * @method self   setOptionSchemasFolder(string $optionSchemasFolder)
80
 * @method string getOptionXsdTypesPath()
81
 * @method self   setOptionXsdTypesPath(string $xsdTypesPath)
82 166
 */
83
class Generator implements JsonSerializable
84 166
{
85
    protected Wsdl $wsdl;
86
87 76
    protected GeneratorOptions $options;
88
89 76
    protected GeneratorParsers $parsers;
90
91
    protected GeneratorFiles $files;
92 72
93
    protected GeneratorContainers $containers;
94 72
95
    protected ?GeneratorSoapClient $soapClient = null;
96
97 178
    public function __construct(GeneratorOptions $options)
98
    {
99 178
        $this
100 178
            ->setOptions($options)
101 4
            ->initialize()
102 4
        ;
103 4
    }
104 4
105 4
    public function __call($name, $arguments)
106
    {
107
        if (($prefix = 'getOption') === substr($name, 0, $length = strlen($prefix)) && empty($arguments)) {
108 4
            $getMethod = 'get'.substr($name, $length);
109 4
110
            return $this->options->{$getMethod}();
111
        }
112 178
        if (($prefix = 'setOption') === substr($name, 0, $length = strlen($prefix)) && 1 === count($arguments)) {
113
            $setMethod = 'set'.substr($name, $length);
114
            $this->options->{$setMethod}(array_shift($arguments));
115 490
116
            return $this;
117 490
        }
118
119
        throw new \BadMethodCallException(sprintf('Method %s is undefined', $name));
120
    }
121
122
    public function getParsers(): GeneratorParsers
123
    {
124
        return $this->parsers;
125 232
    }
126
127 232
    public function getFiles(): GeneratorFiles
128
    {
129
        return $this->files;
130
    }
131
132
    public function generatePackage(): self
133
    {
134
        return $this
135
            ->doSanityChecks()
136
            ->parse()
137 4
            ->initDirectory()
138
            ->doGenerate()
139 4
        ;
140
    }
141 4
142
    public function parse(): self
143
    {
144
        return $this->doParse();
145
    }
146
147
    /**
148
     * Gets the struct by its name
149 178
     * Starting from issue #157, we know call getVirtual secondly as structs are now betterly parsed and so is their inheritance/type is detected.
150
     *
151 178
     * @param string $structName the original struct name
152
     *
153
     * @uses Generator::getStructs()
154
     */
155
    public function getStructByName(string $structName): ?Struct
156
    {
157
        $struct = $this->getStructs()->getStructByName($structName);
158
159
        return $struct ?: $this->getStructs()->getVirtual($structName);
160
    }
161 2
162
    public function getStructByNameAndType(string $structName, string $type): ?Struct
163 2
    {
164
        return $this->getStructs()->getStructByNameAndType($structName, $type);
165 2
    }
166
167
    public function getService(string $serviceName): ?Service
168
    {
169
        return $this->getServices()->getServiceByName($serviceName);
170
    }
171
172
    public function getServiceMethod(string $methodName): ?Method
173 48
    {
174
        return $this->getService($this->getServiceName($methodName)) instanceof Service ? $this->getService($this->getServiceName($methodName))->getMethod($methodName) : null;
175 48
    }
176
177
    public function getServices(bool $usingGatherMethods = false): ServiceContainer
178
    {
179
        $services = $this->containers->getServices();
180
        if ($usingGatherMethods && GeneratorOptions::VALUE_NONE === $this->getOptionGatherMethods()) {
181
            $serviceContainer = new ServiceContainer($this);
182
            $serviceModel = new Service($this, Service::DEFAULT_SERVICE_CLASS_NAME);
183
            foreach ($services as $service) {
184
                foreach ($service->getMethods() as $method) {
185 4
                    $serviceModel->getMethods()->add($method);
186
                }
187 4
            }
188
            $serviceContainer->add($serviceModel);
189 4
            $services = $serviceContainer;
190
        }
191
192
        return $services;
193
    }
194
195
    public function getStructs(): StructContainer
196
    {
197 16
        return $this->containers->getStructs();
198
    }
199 16
200
    public function getOptionNamespacePrefix(): string
201
    {
202
        return $this->options->getNamespace();
203
    }
204
205
    public function setOptionNamespacePrefix(string $namespace): self
206
    {
207
        $this->options->setNamespace($namespace);
208
209 2
        return $this;
210
    }
211 2
212
    public function getOptionPrefix(bool $ucFirst = true): string
213 2
    {
214
        return $ucFirst ? ucfirst($this->getOptions()->getPrefix()) : $this->getOptions()->getPrefix();
215
    }
216
217
    public function getOptionSuffix(bool $ucFirst = true): string
218
    {
219
        return $ucFirst ? ucfirst($this->getOptions()->getSuffix()) : $this->getOptions()->getSuffix();
220
    }
221 232
222
    public function setOptionOrigin(string $optionOrigin): self
223 232
    {
224
        $this->options->setOrigin($optionOrigin);
225
        $this->initWsdl();
226
227
        return $this;
228
    }
229
230
    public function getOptionDestination(): string
231
    {
232
        $destination = $this->options->getDestination();
233 12
        if (!empty($destination)) {
234
            $destination = rtrim($destination, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR;
235 12
        }
236
237 12
        return $destination;
238
    }
239
240
    public function setOptionDestination(string $optionDestination): self
241
    {
242
        if (!empty($optionDestination)) {
243
            $this->options->setDestination(rtrim($optionDestination, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR);
244
        } else {
245 160
            throw new InvalidArgumentException('Package\'s destination can\'t be empty', __LINE__);
246
        }
247 160
248
        return $this;
249
    }
250
251
    public function setOptionSoapOptions(array $optionSoapOptions): self
252
    {
253
        $this->options->setSoapOptions($optionSoapOptions);
254
255
        if ($this->soapClient) {
256
            $this->soapClient->initSoapClient();
257 2
        }
258
259 2
        return $this;
260
    }
261 2
262
    public function setOptionComposerName(string $optionComposerName): self
263
    {
264
        if (!empty($optionComposerName)) {
265
            $this->options->setComposerName($optionComposerName);
266
        } else {
267
            throw new InvalidArgumentException('Package\'s composer name can\'t be empty', __LINE__);
268
        }
269 34
270
        return $this;
271 34
    }
272
273
    public function getWsdl(): ?Wsdl
274
    {
275
        return $this->wsdl;
276
    }
277
278
    public function addSchemaToWsdl(Wsdl $wsdl, string $schemaLocation): self
279
    {
280
        if (!empty($schemaLocation) && !$wsdl->hasSchema($schemaLocation)) {
281 6
            $wsdl->addSchema(new Schema($wsdl->getGenerator(), $schemaLocation, $this->getUrlContent($schemaLocation)));
0 ignored issues
show
Bug introduced by
It seems like $this->getUrlContent($schemaLocation) can also be of type null; however, parameter $content of WsdlToPhp\PackageGenerat...l\Schema::__construct() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

281
            $wsdl->addSchema(new Schema($wsdl->getGenerator(), $schemaLocation, /** @scrutinizer ignore-type */ $this->getUrlContent($schemaLocation)));
Loading history...
282
        }
283 6
284
        return $this;
285 6
    }
286
287
    public function getServiceName(string $methodName): string
288
    {
289
        return ucfirst($this->getGather(new EmptyModel($this, $methodName)));
290
    }
291
292
    public function getOptions(): ?GeneratorOptions
293 110
    {
294
        return $this->options;
295 110
    }
296
297
    public function getSoapClient(): GeneratorSoapClient
298
    {
299
        return $this->soapClient;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->soapClient could return the type null which is incompatible with the type-hinted return WsdlToPhp\PackageGenerat...tor\GeneratorSoapClient. Consider adding an additional type-check to rule them out.
Loading history...
300
    }
301
302
    public function getUrlContent(string $url): ?string
303
    {
304
        if (false !== mb_strpos($url, '://')) {
305 20
            $content = Utils::getContentFromUrl($url, $this->getOptionBasicLogin(), $this->getOptionBasicPassword(), $this->getOptionProxyHost(), $this->getOptionProxyPort(), $this->getOptionProxyLogin(), $this->getOptionProxyPassword(), $this->getSoapClient()->getSoapClientStreamContextOptions());
306
            if ($this->getOptionSchemasSave()) {
307 20
                Utils::saveSchemas($this->getOptionDestination(), $this->getOptionSchemasFolder(), $url, $content);
308
            }
309 20
310
            return $content;
311
        }
312
        if (is_file($url)) {
313
            return file_get_contents($url);
314
        }
315
316
        return null;
317 100
    }
318
319 100
    public function getContainers(): GeneratorContainers
320
    {
321
        return $this->containers;
322
    }
323
324
    public function jsonSerialize(): array
325
    {
326
        return [
327
            'containers' => $this->containers,
328
            'options' => $this->options,
329 6
        ];
330
    }
331 6
332
    public static function instanceFromSerializedJson(string $json): Generator
333 6
    {
334
        $decodedJson = json_decode($json, true);
335
        if (JSON_ERROR_NONE === json_last_error()) {
336
            // load options first
337
            $options = GeneratorOptions::instance();
338
            foreach ($decodedJson['options'] as $name => $value) {
339
                $options->setOptionValue($name, $value);
0 ignored issues
show
Bug introduced by
The method setOptionValue() does not exist on WsdlToPhp\PackageGenerat...ader\AbstractYamlReader. It seems like you code against a sub-type of WsdlToPhp\PackageGenerat...ader\AbstractYamlReader such as WsdlToPhp\PackageGenerat...Reader\GeneratorOptions. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

339
                $options->/** @scrutinizer ignore-call */ 
340
                          setOptionValue($name, $value);
Loading history...
340
            }
341 20
            // create generator instance with options
342
            $instance = new self($options);
343 20
            // load services
344
            foreach ($decodedJson['containers']['services'] as $service) {
345
                $instance->getContainers()->getServices()->add(self::getModelInstanceFromJsonArrayEntry($instance, $service));
346
            }
347
            // load structs
348
            foreach ($decodedJson['containers']['structs'] as $struct) {
349
                $instance->getContainers()->getStructs()->add(self::getModelInstanceFromJsonArrayEntry($instance, $struct));
350
            }
351
        } else {
352
            throw new InvalidArgumentException(sprintf('Json is invalid, please check error %s', json_last_error()));
353 2
        }
354
355 2
        return $instance;
356
    }
357 2
358
    protected function initialize(): self
359
    {
360
        return $this
361
            ->initContainers()
362
            ->initParsers()
363
            ->initFiles()
364
            ->initSoapClient()
365 46
            ->initWsdl()
366
        ;
367 46
    }
368
369
    protected function initSoapClient(): self
370
    {
371
        if (!isset($this->soapClient)) {
372
            $this->soapClient = new GeneratorSoapClient($this);
373
        }
374
375
        return $this;
376
    }
377 2
378
    protected function initContainers(): self
379 2
    {
380
        if (!isset($this->containers)) {
381 2
            $this->containers = new GeneratorContainers($this);
382
        }
383
384
        return $this;
385
    }
386
387
    protected function initParsers(): self
388
    {
389 50
        if (!isset($this->parsers)) {
390
            $this->parsers = new GeneratorParsers($this);
391 50
        }
392
393
        return $this;
394
    }
395
396
    protected function initFiles(): self
397
    {
398
        if (!isset($this->files)) {
399
            $this->files = new GeneratorFiles($this);
400
        }
401 2
402
        return $this;
403 2
    }
404
405 2
    protected function initDirectory(): self
406
    {
407
        Utils::createDirectory($this->getOptions()->getDestination());
408
        if (!is_writable($this->getOptionDestination())) {
409
            throw new InvalidArgumentException(sprintf('Unable to use dir "%s" as dir does not exists, its creation has been impossible or it\'s not writable', $this->getOptionDestination()), __LINE__);
410
        }
411
412
        return $this;
413
    }
414
415 414
    protected function initWsdl(): self
416
    {
417 414
        $this->setWsdl(new Wsdl($this, $this->getOptionOrigin(), $this->getUrlContent($this->getOptionOrigin())));
0 ignored issues
show
Bug introduced by
It seems like $this->getUrlContent($this->getOptionOrigin()) can also be of type null; however, parameter $content of WsdlToPhp\PackageGenerat...del\Wsdl::__construct() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

417
        $this->setWsdl(new Wsdl($this, $this->getOptionOrigin(), /** @scrutinizer ignore-type */ $this->getUrlContent($this->getOptionOrigin())));
Loading history...
418
419
        return $this;
420
    }
421
422
    protected function doSanityChecks(): self
423
    {
424
        $destination = $this->getOptionDestination();
425
        if (empty($destination)) {
426
            throw new InvalidArgumentException('Package\'s destination must be defined', __LINE__);
427 44
        }
428
429 44
        $composerName = $this->getOptionComposerName();
430
        if ($this->getOptionStandalone() && empty($composerName)) {
431 44
            throw new InvalidArgumentException('Package\'s composer name must be defined', __LINE__);
432
        }
433
434
        return $this;
435
    }
436
437
    protected function doParse(): self
438
    {
439
        $this->parsers->doParse();
440
441 410
        return $this;
442
    }
443 410
444
    protected function doGenerate(): self
445
    {
446
        $this->files->doGenerate();
447
448
        return $this;
449
    }
450
451
    protected function setWsdl(Wsdl $wsdl): self
452
    {
453 16
        $this->wsdl = $wsdl;
454
455 16
        return $this;
456
    }
457 16
458
    protected function getGather(AbstractModel $model): string
459
    {
460
        return Utils::getPart($this->getOptionGatherMethods(), $model->getCleanName());
461
    }
462
463
    protected function setOptions(GeneratorOptions $options): self
464
    {
465 366
        $this->options = $options;
466
467 366
        return $this;
468
    }
469
470
    protected static function getModelInstanceFromJsonArrayEntry(Generator $generator, array $jsonArrayEntry): AbstractModel
471
    {
472
        return AbstractModel::instanceFromSerializedJson($generator, $jsonArrayEntry);
473
    }
474
}
475