SetupGacela::setGacelaConfigsToExtend()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

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