Passed
Push — main ( 1a17f8...8155a4 )
by Chema
10:50 queued 09:35
created

SetupGacela::getGacelaConfigsToExtend()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
ccs 0
cts 0
cp 0
crap 2
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\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 133
76
    /** @var ?list<class-string|callable> */
77 133
    private ?array $plugins = null;
78 74
79
    public function __construct()
80 133
    {
81 133
        $emptyFn = static function (): void {};
82 133
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 74
    /**
107
     * @param callable(GacelaConfig):void $setupGacelaFileFn
108 74
     */
109 74
    public static function fromCallable(callable $setupGacelaFileFn): self
110
    {
111 74
        $gacelaConfig = new GacelaConfig();
112
        $setupGacelaFileFn($gacelaConfig);
113
114 93
        return self::fromGacelaConfig($gacelaConfig);
115
    }
116 93
117
    public static function fromGacelaConfig(GacelaConfig $gacelaConfig): self
118 93
    {
119
        (new GacelaConfigExtender())->extend($gacelaConfig);
120 93
121 93
        $dto = $gacelaConfig->toTransfer();
122 93
123 93
        return (new self())
124 93
            ->setExternalServices($dto->externalServices)
125 93
            ->setAppConfigBuilder($dto->appConfigBuilder)
126 93
            ->setSuffixTypesBuilder($dto->suffixTypesBuilder)
127 93
            ->setBindingsBuilder($dto->bindingsBuilder)
128 93
            ->setBindings($dto->bindings)
129 93
            ->setShouldResetInMemoryCache($dto->shouldResetInMemoryCache)
130 93
            ->setFileCacheEnabled($dto->fileCacheEnabled)
131 93
            ->setFileCacheDirectory($dto->fileCacheDirectory)
132 93
            ->setProjectNamespaces($dto->projectNamespaces)
133 93
            ->setConfigKeyValues($dto->configKeyValues)
134 93
            ->setAreEventListenersEnabled($dto->areEventListenersEnabled)
135 93
            ->setGenericListeners($dto->genericListeners)
136
            ->setSpecificListeners($dto->specificListeners)
137
            ->setGacelaConfigsToExtend($dto->gacelaConfigsToExtend)
138
            ->setPlugins($dto->plugins)
139
            ->setServicesToExtend($dto->servicesToExtend);
140
    }
141 96
142
    /**
143 96
     * @param array<string,class-string|object|callable> $array
144 96
     */
145
    public function setExternalServices(?array $array): self
146 96
    {
147
        $this->markPropertyChanged(self::externalServices, true);
148
        $this->externalServices = $array;
149 93
150
        return $this;
151 93
    }
152
153 93
    /**
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 93
    public function setBindings(?array $array): self
157
    {
158 93
        $this->markPropertyChanged(self::bindings, true);
159
        $this->bindings = $array;
160 93
161
        return $this;
162
    }
163 93
164
    public function setAppConfigBuilder(AppConfigBuilder $builder): self
165 93
    {
166
        $this->appConfigBuilder = $builder;
167 93
168
        return $this;
169
    }
170
171
    public function setSuffixTypesBuilder(SuffixTypesBuilder $builder): self
172
    {
173 3
        $this->suffixTypesBuilder = $builder;
174
175 3
        return $this;
176
    }
177 3
178
    public function setBindingsBuilder(BindingsBuilder $builder): self
179
    {
180 75
        $this->bindingsBuilder = $builder;
181
182 75
        return $this;
183
    }
184 75
185 67
    /**
186
     * @param callable(AppConfigBuilder):void $callable
187
     */
188 75
    public function setAppConfigFn(callable $callable): self
189
    {
190 75
        $this->appConfigFn = $callable;
191
192
        return $this;
193
    }
194
195
    public function buildAppConfig(AppConfigBuilder $builder): AppConfigBuilder
196 3
    {
197
        $builder = parent::buildAppConfig($builder);
198 3
199
        if ($this->appConfigBuilder instanceof AppConfigBuilder) {
200 3
            $builder = $this->appConfigBuilder;
201
        }
202
203
        ($this->appConfigFn)($builder);
204
205
        return $builder;
206
    }
207
208 75
    /**
209
     * @param callable(BindingsBuilder,array<string,mixed>):void $callable
210
     */
211
    public function setBindingsFn(callable $callable): self
212 75
    {
213
        $this->bindingsFn = $callable;
214 75
215 67
        return $this;
216
    }
217
218 75
    /**
219 75
     * Define the mapping between interfaces and concretions, so Gacela services will auto-resolve them automatically.
220 75
     *
221 75
     * @param array<string,class-string|object|callable> $externalServices
222
     */
223 75
    public function buildBindings(
224
        BindingsBuilder $builder,
225
        array $externalServices,
226
    ): BindingsBuilder {
227
        $builder = parent::buildBindings($builder, $externalServices);
228
229 3
        if ($this->bindingsBuilder instanceof BindingsBuilder) {
230
            $builder = $this->bindingsBuilder;
231 3
        }
232
233 3
        foreach ($this->bindings ?? [] as $k => $v) {
234
            $builder->bind($k, $v);
235
        }
236
237
        ($this->bindingsFn)(
238
            $builder,
239 75
            array_merge($this->externalServices ?? [], $externalServices)
240
        );
241 75
242
        return $builder;
243 75
    }
244 67
245
    /**
246
     * @param callable(SuffixTypesBuilder):void $callable
247 75
     */
248
    public function setSuffixTypesFn(callable $callable): self
249 75
    {
250
        $this->suffixTypesFn = $callable;
251
252
        return $this;
253
    }
254
255 29
    /**
256
     * Allow overriding gacela resolvable types.
257 29
     */
258 29
    public function buildSuffixTypes(SuffixTypesBuilder $builder): SuffixTypesBuilder
259 29
    {
260 29
        $builder = parent::buildSuffixTypes($builder);
261
262
        if ($this->suffixTypesBuilder instanceof SuffixTypesBuilder) {
263 93
            $builder = $this->suffixTypesBuilder;
264
        }
265 93
266 93
        ($this->suffixTypesFn)($builder);
267
268 93
        return $builder;
269
    }
270
271 107
    /**
272
     * @return array<string, class-string|object|callable>
273 107
     */
274
    public function externalServices(): array
275
    {
276 49
        return array_merge(
277
            parent::externalServices(),
278 49
            $this->externalServices ?? [],
279
        );
280
    }
281 7
282
    public function setShouldResetInMemoryCache(?bool $flag): self
283 7
    {
284
        $this->markPropertyChanged(self::shouldResetInMemoryCache, $flag);
285
        $this->shouldResetInMemoryCache = $flag ?? self::DEFAULT_SHOULD_RESET_IN_MEMORY_CACHE;
286 93
287
        return $this;
288 93
    }
289 93
290
    public function shouldResetInMemoryCache(): bool
291 93
    {
292
        return (bool)$this->shouldResetInMemoryCache;
293
    }
294
295
    public function isFileCacheEnabled(): bool
296
    {
297 93
        return (bool)$this->fileCacheEnabled;
298
    }
299 93
300 93
    public function getFileCacheDirectory(): string
301
    {
302 93
        return (string)$this->fileCacheDirectory;
303
    }
304
305
    public function setFileCacheDirectory(?string $dir): self
306
    {
307
        $this->markPropertyChanged(self::fileCacheDirectory, $dir);
308 45
        $this->fileCacheDirectory = $dir ?? self::DEFAULT_FILE_CACHE_DIRECTORY;
309
310 45
        return $this;
311
    }
312
313
    /**
314
     * @param ?list<string> $list
315
     */
316 107
    public function setProjectNamespaces(?array $list): self
317
    {
318 107
        $this->markPropertyChanged(self::projectNamespaces, $list);
319
        $this->projectNamespaces = $list ?? self::DEFAULT_PROJECT_NAMESPACES;
320
321 70
        return $this;
322
    }
323 70
324
    /**
325
     * @return list<string>
326
     */
327
    public function getProjectNamespaces(): array
328
    {
329 120
        return (array)$this->projectNamespaces;
330
    }
331 120
332
    /**
333
     * @return array<string,mixed>
334 93
     */
335
    public function getConfigKeyValues(): array
336 93
    {
337 93
        return (array)$this->configKeyValues;
338
    }
339 93
340
    public function getEventDispatcher(): EventDispatcherInterface
341
    {
342 70
        return $this->eventDispatcher ??= SetupEventDispatcher::getDispatcher($this);
343
    }
344 70
345 70
    /**
346
     * @return array<string,list<Closure>>
347
     */
348
    public function getServicesToExtend(): array
349
    {
350
        return (array)$this->servicesToExtend;
351 93
    }
352
353 93
    public function setFileCacheEnabled(?bool $flag): self
354 93
    {
355
        $this->markPropertyChanged(self::fileCacheEnabled, $flag);
356 93
        $this->fileCacheEnabled = $flag ?? self::DEFAULT_FILE_CACHE_ENABLED;
357
358
        return $this;
359
    }
360
361
    public function canCreateEventDispatcher(): bool
362 16
    {
363
        return $this->areEventListenersEnabled === true
364 16
            && $this->hasEventListeners();
365
    }
366
367
    /**
368
     * @param ?array<string,mixed> $configKeyValues
369
     */
370 16
    public function setConfigKeyValues(?array $configKeyValues): self
371
    {
372 16
        $this->markPropertyChanged(self::configKeyValues, $configKeyValues);
373
        $this->configKeyValues = $configKeyValues ?? self::DEFAULT_CONFIG_KEY_VALUES;
374
375 29
        return $this;
376
    }
377 29
378
    /**
379
     * @return array<class-string,list<callable>>|null
380 29
     */
381
    public function getSpecificListeners(): ?array
382 29
    {
383
        return $this->specificListeners;
384 29
    }
385
386
    /**
387 29
     * @return list<callable>|null
388
     */
389 29
    public function getGenericListeners(): ?array
390
    {
391
        return $this->genericListeners;
392
    }
393
394
    public function isPropertyChanged(string $name): bool
395 1
    {
396
        return $this->changedProperties[$name] ?? false;
397 1
    }
398 1
399 1
    public function setEventDispatcher(EventDispatcherInterface $eventDispatcher): self
400 1
    {
401 1
        $this->eventDispatcher = $eventDispatcher;
402
403 1
        return $this;
404
    }
405
406 29
    public function combine(self $other): self
407
    {
408 29
        return (new SetupCombinator($this))->combine($other);
409
    }
410
411 1
    /**
412
     * @param list<Closure> $servicesToExtend
413 1
     */
414
    public function addServicesToExtend(string $serviceId, array $servicesToExtend): self
415
    {
416 1
        $this->servicesToExtend[$serviceId] ??= [];
417
        $this->servicesToExtend[$serviceId] = [...$this->servicesToExtend[$serviceId], ...$servicesToExtend];
418 1
419
        return $this;
420
    }
421
422
    /**
423
     * @param array<string,class-string|object|callable> $list
424 1
     */
425
    public function combineExternalServices(array $list): void
426 1
    {
427 1
        $this->setExternalServices(array_merge($this->externalServices ?? [], $list));
428 1
    }
429
430
    /**
431
     * @param list<string> $list
432
     */
433
    public function combineProjectNamespaces(array $list): void
434 1
    {
435
        $this->setProjectNamespaces(array_merge($this->projectNamespaces ?? [], $list));
436 1
    }
437
438
    /**
439
     * @param array<string,mixed> $list
440
     */
441
    public function combineConfigKeyValues(array $list): void
442 1
    {
443
        $this->setConfigKeyValues(array_merge($this->configKeyValues ?? [], $list));
444 1
    }
445
446
    /**
447
     * @param list<class-string> $list
448
     */
449
    public function combineGacelaConfigsToExtend(array $list): void
450 107
    {
451
        $this->setGacelaConfigsToExtend(
452 107
            // @phpstan-ignore-next-line
453
            array_unique(array_merge($this->gacelaConfigsToExtend ?? [], $list)),
454
        );
455 93
    }
456
457 93
    /**
458
     * @param list<class-string|callable> $list
459 93
     */
460
    public function combinePlugins(array $list): void
461
    {
462 70
        $this->setPlugins(array_merge($this->plugins ?? [], $list));
463
    }
464 70
465 70
    /**
466
     * @return list<class-string>
467
     */
468
    public function getGacelaConfigsToExtend(): array
469
    {
470
        return (array)$this->gacelaConfigsToExtend;
471 93
    }
472
473 93
    /**
474
     * @return list<class-string|callable>
475 93
     */
476
    public function getPlugins(): array
477
    {
478
        return (array)$this->plugins;
479
    }
480
481 93
    /**
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 93
     */
484 93
    public function getBindings(): array
485
    {
486 93
        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 93
    public function combineBindings(array $list): void
493
    {
494 93
        $this->setBindings(array_merge($this->bindings ?? [], $list));
495 93
    }
496
497 93
    private function setAreEventListenersEnabled(?bool $flag): self
498
    {
499
        $this->areEventListenersEnabled = $flag ?? self::DEFAULT_ARE_EVENT_LISTENERS_ENABLED;
500
501
        return $this;
502
    }
503 93
504
    private function hasEventListeners(): bool
505 93
    {
506 93
        return ($this->genericListeners !== null && $this->genericListeners !== [])
507
            || ($this->specificListeners !== null && $this->specificListeners !== []);
508 93
    }
509
510
    /**
511
     * @param ?list<callable> $listeners
512
     */
513
    private function setGenericListeners(?array $listeners): self
514 93
    {
515
        $this->genericListeners = $listeners ?? self::DEFAULT_GENERIC_LISTENERS;
516 93
517
        return $this;
518 93
    }
519
520
    /**
521 96
     * @param ?array<string,list<Closure>> $list
522
     */
523 96
    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