Passed
Push — bugfix/support-windows ( 0e99e7...b5ecf8 )
by Jesús
06:33 queued 02:55
created

SetupGacela   D

Complexity

Total Complexity 59

Size/Duplication

Total Lines 536
Duplicated Lines 0 %

Test Coverage

Coverage 94.12%

Importance

Changes 13
Bugs 1 Features 0
Metric Value
eloc 173
dl 0
loc 536
ccs 176
cts 187
cp 0.9412
rs 4.08
c 13
b 1
f 0
wmc 59

50 Methods

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