Passed
Push — main ( 7d3811...971eef )
by Chema
01:28
created

SetupGacela::markPropertyAsChanged()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

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