Passed
Push — feat/add-provider-bindings ( 36306e )
by Chema
04:36
created

SetupGacela   F

Complexity

Total Complexity 62

Size/Duplication

Total Lines 549
Duplicated Lines 0 %

Importance

Changes 15
Bugs 1 Features 0
Metric Value
wmc 62
eloc 154
c 15
b 1
f 0
dl 0
loc 549
rs 3.44

52 Methods

Rating   Name   Duplication   Size   Complexity  
A markPropertyChanged() 0 3 1
A setGenericListeners() 0 5 1
A getGacelaConfigsToExtend() 0 3 1
A setAreEventListenersEnabled() 0 5 1
A setSuffixTypesBuilder() 0 5 1
A setServicesToExtend() 0 6 1
A setBindingsFn() 0 5 1
A getConfigKeyValues() 0 3 1
A getFileCacheDirectory() 0 3 1
A hasEventListeners() 0 4 4
A setFileCacheEnabled() 0 6 1
A isFileCacheEnabled() 0 3 1
A isPropertyChanged() 0 3 1
A combinePlugins() 0 3 1
A setGacelaConfigsToExtend() 0 6 1
A addServicesToExtend() 0 6 1
A combineConfigKeyValues() 0 3 1
A setSuffixTypesFn() 0 5 1
A getPlugins() 0 3 1
A setBindings() 0 6 1
A combineBindings() 0 3 1
A setAppConfigFn() 0 5 1
A combineProjectNamespaces() 0 3 1
A getSpecificListeners() 0 3 1
A __construct() 0 7 1
A setAppConfigBuilder() 0 5 1
A setConfigKeyValues() 0 6 1
A getEventDispatcher() 0 3 1
A getProjectNamespaces() 0 3 1
A buildAppConfig() 0 11 2
A setSpecificListeners() 0 5 1
A canCreateEventDispatcher() 0 4 2
A setShouldResetInMemoryCache() 0 6 1
A shouldResetInMemoryCache() 0 3 1
A getGenericListeners() 0 3 1
A combineGacelaConfigsToExtend() 0 5 1
A fromGacelaConfig() 0 23 1
A fromCallable() 0 6 1
A buildBindings() 0 20 3
A setExternalServices() 0 6 1
A externalServices() 0 5 1
A setBindingsBuilder() 0 5 1
A setFileCacheDirectory() 0 6 1
A fromFile() 0 13 3
A combine() 0 3 1
A getBindings() 0 3 1
A getServicesToExtend() 0 3 1
A setPlugins() 0 6 1
A setEventDispatcher() 0 5 1
A combineExternalServices() 0 3 1
A setProjectNamespaces() 0 6 1
A buildSuffixTypes() 0 11 2

How to fix   Complexity   

Complex Class

Complex classes like SetupGacela 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 SetupGacela, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Gacela\Framework\Bootstrap;
6
7
use Closure;
8
use Gacela\Framework\Bootstrap\Setup\GacelaConfigExtender;
9
use Gacela\Framework\Config\GacelaConfigBuilder\AppConfigBuilder;
10
use Gacela\Framework\Config\GacelaConfigBuilder\BindingsBuilder;
11
use Gacela\Framework\Config\GacelaConfigBuilder\SuffixTypesBuilder;
12
use Gacela\Framework\Event\Dispatcher\EventDispatcherInterface;
13
use RuntimeException;
14
15
use function array_unique;
16
use function is_callable;
17
use function sprintf;
18
19
/**
20
 * @psalm-suppress ArgumentTypeCoercion,MixedArgumentTypeCoercion
21
 */
22
final class SetupGacela extends AbstractSetupGacela
23
{
24
    /** @var callable(AppConfigBuilder):void */
25
    private $appConfigFn;
26
27
    /** @var callable(BindingsBuilder,array<string,mixed>):void */
28
    private $bindingsFn;
29
30
    /** @var ?array<class-string,class-string|object|callable> */
0 ignored issues
show
Documentation Bug introduced by
The doc comment ?array<class-string,class-string|object|callable> at position 2 could not be parsed: Unknown type name 'class-string' at position 2 in ?array<class-string,class-string|object|callable>.
Loading history...
31
    private ?array $bindings = null;
32
33
    /** @var callable(SuffixTypesBuilder):void */
34
    private $suffixTypesFn;
35
36
    /** @var ?array<string,class-string|object|callable> */
37
    private ?array $externalServices = null;
38
39
    private ?AppConfigBuilder $appConfigBuilder = null;
40
41
    private ?SuffixTypesBuilder $suffixTypesBuilder = null;
42
43
    private ?BindingsBuilder $bindingsBuilder = null;
44
45
    private ?bool $shouldResetInMemoryCache = null;
46
47
    private ?bool $fileCacheEnabled = null;
48
49
    private ?string $fileCacheDirectory = null;
50
51
    /** @var ?list<string> */
52
    private ?array $projectNamespaces = null;
53
54
    /** @var ?array<string,mixed> */
55
    private ?array $configKeyValues = null;
56
57
    private ?bool $areEventListenersEnabled = null;
58
59
    /** @var ?list<callable> */
60
    private ?array $genericListeners = null;
61
62
    /** @var ?array<class-string,list<callable>> */
63
    private ?array $specificListeners = null;
64
65
    private ?EventDispatcherInterface $eventDispatcher = null;
66
67
    /** @var ?array<string,bool> */
68
    private ?array $changedProperties = null;
69
70
    /** @var ?array<string,list<Closure>> */
71
    private ?array $servicesToExtend = null;
72
73
    /** @var ?list<class-string> */
74
    private ?array $gacelaConfigsToExtend = null;
75
76
    /** @var ?list<class-string|callable> */
77
    private ?array $plugins = null;
78
79
    public function __construct()
80
    {
81
        $emptyFn = static function (): void {};
82
83
        $this->appConfigFn = $emptyFn;
84
        $this->bindingsFn = $emptyFn;
85
        $this->suffixTypesFn = $emptyFn;
86
    }
87
88
    /**
89
     * @codeCoverageIgnore
90
     */
91
    public static function fromFile(string $gacelaFilePath): self
92
    {
93
        if (!is_file($gacelaFilePath)) {
94
            throw new RuntimeException(sprintf("Invalid file path: '%s'", $gacelaFilePath));
95
        }
96
97
        /** @var callable(GacelaConfig):void|null $setupGacelaFileFn */
98
        $setupGacelaFileFn = include $gacelaFilePath;
99
        if (!is_callable($setupGacelaFileFn)) {
100
            return new self();
101
        }
102
103
        return self::fromCallable($setupGacelaFileFn);
104
    }
105
106
    /**
107
     * @param callable(GacelaConfig):void $setupGacelaFileFn
108
     */
109
    public static function fromCallable(callable $setupGacelaFileFn): self
110
    {
111
        $gacelaConfig = new GacelaConfig();
112
        $setupGacelaFileFn($gacelaConfig);
113
114
        return self::fromGacelaConfig($gacelaConfig);
115
    }
116
117
    public static function fromGacelaConfig(GacelaConfig $gacelaConfig): self
118
    {
119
        (new GacelaConfigExtender())->extend($gacelaConfig);
120
121
        $dto = $gacelaConfig->toTransfer();
122
123
        return (new self())
124
            ->setExternalServices($dto->externalServices)
125
            ->setAppConfigBuilder($dto->appConfigBuilder)
126
            ->setSuffixTypesBuilder($dto->suffixTypesBuilder)
127
            ->setBindingsBuilder($dto->bindingsBuilder)
128
            ->setBindings($dto->bindings)
129
            ->setShouldResetInMemoryCache($dto->shouldResetInMemoryCache)
130
            ->setFileCacheEnabled($dto->fileCacheEnabled)
131
            ->setFileCacheDirectory($dto->fileCacheDirectory)
132
            ->setProjectNamespaces($dto->projectNamespaces)
133
            ->setConfigKeyValues($dto->configKeyValues)
134
            ->setAreEventListenersEnabled($dto->areEventListenersEnabled)
135
            ->setGenericListeners($dto->genericListeners)
136
            ->setSpecificListeners($dto->specificListeners)
137
            ->setGacelaConfigsToExtend($dto->gacelaConfigsToExtend)
138
            ->setPlugins($dto->plugins)
139
            ->setServicesToExtend($dto->servicesToExtend);
140
    }
141
142
    /**
143
     * @param array<string,class-string|object|callable> $array
144
     */
145
    public function setExternalServices(?array $array): self
146
    {
147
        $this->markPropertyChanged(self::externalServices, true);
148
        $this->externalServices = $array;
149
150
        return $this;
151
    }
152
153
    /**
154
     * @param array<class-string, class-string|object|callable> $array
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<class-string, class-string|object|callable> at position 2 could not be parsed: Unknown type name 'class-string' at position 2 in array<class-string, class-string|object|callable>.
Loading history...
155
     */
156
    public function setBindings(?array $array): self
157
    {
158
        $this->markPropertyChanged(self::bindings, true);
159
        $this->bindings = $array;
160
161
        return $this;
162
    }
163
164
    public function setAppConfigBuilder(AppConfigBuilder $builder): self
165
    {
166
        $this->appConfigBuilder = $builder;
167
168
        return $this;
169
    }
170
171
    public function setSuffixTypesBuilder(SuffixTypesBuilder $builder): self
172
    {
173
        $this->suffixTypesBuilder = $builder;
174
175
        return $this;
176
    }
177
178
    public function setBindingsBuilder(BindingsBuilder $builder): self
179
    {
180
        $this->bindingsBuilder = $builder;
181
182
        return $this;
183
    }
184
185
    /**
186
     * @param callable(AppConfigBuilder):void $callable
187
     */
188
    public function setAppConfigFn(callable $callable): self
189
    {
190
        $this->appConfigFn = $callable;
191
192
        return $this;
193
    }
194
195
    public function buildAppConfig(AppConfigBuilder $builder): AppConfigBuilder
196
    {
197
        $builder = parent::buildAppConfig($builder);
198
199
        if ($this->appConfigBuilder instanceof AppConfigBuilder) {
200
            $builder = $this->appConfigBuilder;
201
        }
202
203
        ($this->appConfigFn)($builder);
204
205
        return $builder;
206
    }
207
208
    /**
209
     * @param callable(BindingsBuilder,array<string,mixed>):void $callable
210
     */
211
    public function setBindingsFn(callable $callable): self
212
    {
213
        $this->bindingsFn = $callable;
214
215
        return $this;
216
    }
217
218
    /**
219
     * Define the mapping between interfaces and concretions, so Gacela services will auto-resolve them automatically.
220
     *
221
     * @param array<string,class-string|object|callable> $externalServices
222
     */
223
    public function buildBindings(
224
        BindingsBuilder $builder,
225
        array $externalServices,
226
    ): BindingsBuilder {
227
        $builder = parent::buildBindings($builder, $externalServices);
228
229
        if ($this->bindingsBuilder instanceof BindingsBuilder) {
230
            $builder = $this->bindingsBuilder;
231
        }
232
233
        foreach ($this->bindings ?? [] as $k => $v) {
234
            $builder->bind($k, $v);
235
        }
236
237
        ($this->bindingsFn)(
238
            $builder,
239
            array_merge($this->externalServices ?? [], $externalServices)
240
        );
241
242
        return $builder;
243
    }
244
245
    /**
246
     * @param callable(SuffixTypesBuilder):void $callable
247
     */
248
    public function setSuffixTypesFn(callable $callable): self
249
    {
250
        $this->suffixTypesFn = $callable;
251
252
        return $this;
253
    }
254
255
    /**
256
     * Allow overriding gacela resolvable types.
257
     */
258
    public function buildSuffixTypes(SuffixTypesBuilder $builder): SuffixTypesBuilder
259
    {
260
        $builder = parent::buildSuffixTypes($builder);
261
262
        if ($this->suffixTypesBuilder instanceof SuffixTypesBuilder) {
263
            $builder = $this->suffixTypesBuilder;
264
        }
265
266
        ($this->suffixTypesFn)($builder);
267
268
        return $builder;
269
    }
270
271
    /**
272
     * @return array<string, class-string|object|callable>
273
     */
274
    public function externalServices(): array
275
    {
276
        return array_merge(
277
            parent::externalServices(),
278
            $this->externalServices ?? [],
279
        );
280
    }
281
282
    public function setShouldResetInMemoryCache(?bool $flag): self
283
    {
284
        $this->markPropertyChanged(self::shouldResetInMemoryCache, $flag);
285
        $this->shouldResetInMemoryCache = $flag ?? self::DEFAULT_SHOULD_RESET_IN_MEMORY_CACHE;
286
287
        return $this;
288
    }
289
290
    public function shouldResetInMemoryCache(): bool
291
    {
292
        return (bool)$this->shouldResetInMemoryCache;
293
    }
294
295
    public function isFileCacheEnabled(): bool
296
    {
297
        return (bool)$this->fileCacheEnabled;
298
    }
299
300
    public function getFileCacheDirectory(): string
301
    {
302
        return (string)$this->fileCacheDirectory;
303
    }
304
305
    public function setFileCacheDirectory(?string $dir): self
306
    {
307
        $this->markPropertyChanged(self::fileCacheDirectory, $dir);
308
        $this->fileCacheDirectory = $dir ?? self::DEFAULT_FILE_CACHE_DIRECTORY;
309
310
        return $this;
311
    }
312
313
    /**
314
     * @param ?list<string> $list
315
     */
316
    public function setProjectNamespaces(?array $list): self
317
    {
318
        $this->markPropertyChanged(self::projectNamespaces, $list);
319
        $this->projectNamespaces = $list ?? self::DEFAULT_PROJECT_NAMESPACES;
320
321
        return $this;
322
    }
323
324
    /**
325
     * @return list<string>
326
     */
327
    public function getProjectNamespaces(): array
328
    {
329
        return (array)$this->projectNamespaces;
330
    }
331
332
    /**
333
     * @return array<string,mixed>
334
     */
335
    public function getConfigKeyValues(): array
336
    {
337
        return (array)$this->configKeyValues;
338
    }
339
340
    public function getEventDispatcher(): EventDispatcherInterface
341
    {
342
        return $this->eventDispatcher ??= SetupEventDispatcher::getDispatcher($this);
343
    }
344
345
    /**
346
     * @return array<string,list<Closure>>
347
     */
348
    public function getServicesToExtend(): array
349
    {
350
        return (array)$this->servicesToExtend;
351
    }
352
353
    public function setFileCacheEnabled(?bool $flag): self
354
    {
355
        $this->markPropertyChanged(self::fileCacheEnabled, $flag);
356
        $this->fileCacheEnabled = $flag ?? self::DEFAULT_FILE_CACHE_ENABLED;
357
358
        return $this;
359
    }
360
361
    public function canCreateEventDispatcher(): bool
362
    {
363
        return $this->areEventListenersEnabled === true
364
            && $this->hasEventListeners();
365
    }
366
367
    /**
368
     * @param ?array<string,mixed> $configKeyValues
369
     */
370
    public function setConfigKeyValues(?array $configKeyValues): self
371
    {
372
        $this->markPropertyChanged(self::configKeyValues, $configKeyValues);
373
        $this->configKeyValues = $configKeyValues ?? self::DEFAULT_CONFIG_KEY_VALUES;
374
375
        return $this;
376
    }
377
378
    /**
379
     * @return array<class-string,list<callable>>|null
380
     */
381
    public function getSpecificListeners(): ?array
382
    {
383
        return $this->specificListeners;
384
    }
385
386
    /**
387
     * @return list<callable>|null
388
     */
389
    public function getGenericListeners(): ?array
390
    {
391
        return $this->genericListeners;
392
    }
393
394
    public function isPropertyChanged(string $name): bool
395
    {
396
        return $this->changedProperties[$name] ?? false;
397
    }
398
399
    public function setEventDispatcher(EventDispatcherInterface $eventDispatcher): self
400
    {
401
        $this->eventDispatcher = $eventDispatcher;
402
403
        return $this;
404
    }
405
406
    public function combine(self $other): self
407
    {
408
        return (new SetupCombinator($this))->combine($other);
409
    }
410
411
    /**
412
     * @param list<Closure> $servicesToExtend
413
     */
414
    public function addServicesToExtend(string $serviceId, array $servicesToExtend): self
415
    {
416
        $this->servicesToExtend[$serviceId] ??= [];
417
        $this->servicesToExtend[$serviceId] = [...$this->servicesToExtend[$serviceId], ...$servicesToExtend];
418
419
        return $this;
420
    }
421
422
    /**
423
     * @param array<string,class-string|object|callable> $list
424
     */
425
    public function combineExternalServices(array $list): void
426
    {
427
        $this->setExternalServices(array_merge($this->externalServices ?? [], $list));
428
    }
429
430
    /**
431
     * @param list<string> $list
432
     */
433
    public function combineProjectNamespaces(array $list): void
434
    {
435
        $this->setProjectNamespaces(array_merge($this->projectNamespaces ?? [], $list));
436
    }
437
438
    /**
439
     * @param array<string,mixed> $list
440
     */
441
    public function combineConfigKeyValues(array $list): void
442
    {
443
        $this->setConfigKeyValues(array_merge($this->configKeyValues ?? [], $list));
444
    }
445
446
    /**
447
     * @param list<class-string> $list
448
     */
449
    public function combineGacelaConfigsToExtend(array $list): void
450
    {
451
        $this->setGacelaConfigsToExtend(
452
            // @phpstan-ignore-next-line
453
            array_unique(array_merge($this->gacelaConfigsToExtend ?? [], $list)),
454
        );
455
    }
456
457
    /**
458
     * @param list<class-string|callable> $list
459
     */
460
    public function combinePlugins(array $list): void
461
    {
462
        $this->setPlugins(array_merge($this->plugins ?? [], $list));
463
    }
464
465
    /**
466
     * @return list<class-string>
467
     */
468
    public function getGacelaConfigsToExtend(): array
469
    {
470
        return (array)$this->gacelaConfigsToExtend;
471
    }
472
473
    /**
474
     * @return list<class-string|callable>
475
     */
476
    public function getPlugins(): array
477
    {
478
        return (array)$this->plugins;
479
    }
480
481
    /**
482
     * @return array<class-string,class-string|object|callable>
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<class-string,class-string|object|callable> at position 2 could not be parsed: Unknown type name 'class-string' at position 2 in array<class-string,class-string|object|callable>.
Loading history...
483
     */
484
    public function getBindings(): array
485
    {
486
        return $this->bindings ?? self::DEFAULT_BINDINGS;
487
    }
488
489
    /**
490
     * @param array<class-string,class-string|object|callable> $list
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<class-string,class-string|object|callable> at position 2 could not be parsed: Unknown type name 'class-string' at position 2 in array<class-string,class-string|object|callable>.
Loading history...
491
     */
492
    public function combineBindings(array $list): void
493
    {
494
        $this->setBindings(array_merge($this->bindings ?? [], $list));
495
    }
496
497
    private function setAreEventListenersEnabled(?bool $flag): self
498
    {
499
        $this->areEventListenersEnabled = $flag ?? self::DEFAULT_ARE_EVENT_LISTENERS_ENABLED;
500
501
        return $this;
502
    }
503
504
    private function hasEventListeners(): bool
505
    {
506
        return ($this->genericListeners !== null && $this->genericListeners !== [])
507
            || ($this->specificListeners !== null && $this->specificListeners !== []);
508
    }
509
510
    /**
511
     * @param ?list<callable> $listeners
512
     */
513
    private function setGenericListeners(?array $listeners): self
514
    {
515
        $this->genericListeners = $listeners ?? self::DEFAULT_GENERIC_LISTENERS;
516
517
        return $this;
518
    }
519
520
    /**
521
     * @param ?array<string,list<Closure>> $list
522
     */
523
    private function setServicesToExtend(?array $list): self
524
    {
525
        $this->markPropertyChanged(self::servicesToExtend, $list);
526
        $this->servicesToExtend = $list ?? self::DEFAULT_SERVICES_TO_EXTEND;
527
528
        return $this;
529
    }
530
531
    /**
532
     * @param ?list<class-string> $list
533
     */
534
    private function setGacelaConfigsToExtend(?array $list): self
535
    {
536
        $this->markPropertyChanged(self::gacelaConfigsToExtend, $list);
537
        $this->gacelaConfigsToExtend = $list ?? self::DEFAULT_GACELA_CONFIGS_TO_EXTEND;
538
539
        return $this;
540
    }
541
542
    /**
543
     * @param ?list<class-string|callable> $list
544
     */
545
    private function setPlugins(?array $list): self
546
    {
547
        $this->markPropertyChanged(self::plugins, $list);
548
        $this->plugins = $list ?? self::DEFAULT_PLUGINS;
549
550
        return $this;
551
    }
552
553
    /**
554
     * @param ?array<class-string,list<callable>> $listeners
555
     */
556
    private function setSpecificListeners(?array $listeners): self
557
    {
558
        $this->specificListeners = $listeners ?? self::DEFAULT_SPECIFIC_LISTENERS;
559
560
        return $this;
561
    }
562
563
    /**
564
     * @param (Closure[]|callable|class-string|mixed)[]|bool|null|string $value
0 ignored issues
show
Documentation Bug introduced by
The doc comment (Closure[]|callable|clas...xed)[]|bool|null|string at position 7 could not be parsed: Unknown type name 'class-string' at position 7 in (Closure[]|callable|class-string|mixed)[]|bool|null|string.
Loading history...
565
     *
566
     * @psalm-param array<int<0, max>|string, callable|class-string|list<Closure>|mixed>|bool|null|string $value
567
     */
568
    private function markPropertyChanged(string $name, array|bool|string|null $value): void
569
    {
570
        $this->changedProperties[$name] = ($value !== null);
571
    }
572
}
573