Passed
Push — main ( fbef6d...0b56a2 )
by Chema
01:16 queued 35s
created

SetupGacela   C

Complexity

Total Complexity 56

Size/Duplication

Total Lines 503
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 14
Bugs 1 Features 0
Metric Value
eloc 147
c 14
b 1
f 0
dl 0
loc 503
ccs 174
cts 174
cp 1
rs 5.5199
wmc 56

49 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 2
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 9 1
A combineConfigKeyValues() 0 3 1
A setSuffixTypesFn() 0 5 1
A getPlugins() 0 3 1
A setAppConfigFn() 0 5 1
A combineProjectNamespaces() 0 3 1
A getSpecificListeners() 0 3 1
A __construct() 0 8 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 4 1
A fromGacelaConfig() 0 22 1
A fromCallable() 0 6 1
A buildBindings() 0 16 2
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 getServicesToExtend() 0 3 1
A setPlugins() 0 6 1
A setEventDispatcher() 0 5 1
A combineExternalServices() 0 3 1
A buildSuffixTypes() 0 11 2
A setProjectNamespaces() 0 6 1

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