Passed
Push — main ( cee6e7...fbef6d )
by Chema
02:55
created

SetupGacela::getGacelaConfigsToExtend()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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