Passed
Push — refactor/setup-gacela ( 3ef164 )
by Chema
04:59
created

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