Test Failed
Pull Request — master (#1149)
by Aleksei
21:24
created

DebugBootloader   A

Complexity

Total Complexity 18

Size/Duplication

Total Lines 107
Duplicated Lines 0 %

Test Coverage

Coverage 85%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 18
eloc 43
c 2
b 0
f 0
dl 0
loc 107
ccs 34
cts 40
cp 0.85
rs 10

6 Methods

Rating   Name   Duplication   Size   Complexity  
A addStateCollector() 0 3 1
A addTag() 0 3 1
A init() 0 4 1
C state() 0 54 13
A initDefaultConfig() 0 5 1
A __construct() 0 4 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Spiral\Bootloader;
6
7
use Spiral\Boot\Bootloader\Bootloader;
8
use Spiral\Config\ConfiguratorInterface;
9
use Spiral\Config\Patch\Append;
10
use Spiral\Core\Attribute\Proxy;
11
use Spiral\Core\Attribute\Singleton;
12
use Spiral\Core\Container\Autowire;
13
use Spiral\Core\FactoryInterface;
14
use Spiral\Core\InvokerInterface;
15
use Spiral\Debug\Config\DebugConfig;
16
use Spiral\Debug\Exception\StateException;
17
use Spiral\Debug\State;
18
use Spiral\Debug\StateCollector\EnvironmentCollector;
19
use Spiral\Debug\StateCollectorInterface;
20
use Spiral\Debug\StateInterface;
21
22
/**
23
 * @psalm-import-type TCollector from DebugConfig
24
 * @psalm-import-type TTag from DebugConfig
25
 */
26
#[Singleton]
27
final class DebugBootloader extends Bootloader
28
{
29
    protected const SINGLETONS = [
30
        EnvironmentCollector::class => EnvironmentCollector::class,
31
    ];
32
33
    protected const BINDINGS = [
34
        StateInterface::class => [self::class, 'state'],
35
    ];
36 357
37
    public function __construct(
38
        private readonly InvokerInterface $invoker,
39
        private readonly ConfiguratorInterface $config,
40
    ) {
41 357
    }
42
43
    /**
44
     * Boot default state collector.
45
     */
46 357
    public function init(): void
47
    {
48 357
        $this->initDefaultConfig();
49 357
        $this->addStateCollector(EnvironmentCollector::class);
50
    }
51
52
    /**
53
     * @param non-empty-string $key
0 ignored issues
show
Documentation Bug introduced by
The doc comment non-empty-string at position 0 could not be parsed: Unknown type name 'non-empty-string' at position 0 in non-empty-string.
Loading history...
54
     * @param TTag $value
55
     */
56 1
    public function addTag(string $key, string|\Stringable|\Closure $value): void
57
    {
58 1
        $this->config->modify(DebugConfig::CONFIG, new Append('tags', $key, $value));
59
    }
60
61
    /**
62
     * @psalm-param TCollector $collector
63
     */
64 357
    public function addStateCollector(string|StateCollectorInterface|Autowire $collector): void
65
    {
66 357
        $this->config->modify(DebugConfig::CONFIG, new Append('collectors', null, $collector));
67
    }
68
69
    /**
70
     * Create state and populate it with collectors.
71
     */
72 5
    private function state(
73
        #[Proxy] FactoryInterface $factory,
74 5
        DebugConfig $config,
75
    ): StateInterface {
76 5
        $state = new State();
77 3
78 1
        foreach ($config->getTags() as $key => $value) {
79
            if ($value instanceof \Closure) {
80
                $value = $this->invoker->invoke($value);
81 3
            }
82 1
83 1
            if (!\is_string($value) && !$value instanceof \Stringable) {
84 1
                throw new StateException(
85 1
                    \sprintf(
86
                        'Invalid tag value, `string` expected got `%s`',
87
                        \is_object($value) ? $value::class : \gettype($value),
88 2
                    ),
89
                );
90
            }
91 4
92 3
            $state->setTag((string)$key, (string)$value);
93 3
        }
94 2
95 2
        $errors = [];
96 3
97
        foreach ($config->getCollectors() as $collector) {
98 3
            try {
99
                $collector = match (true) {
100
                    \is_string($collector) => $factory->make($collector),
101
                    $collector instanceof Autowire => $collector->resolve($factory),
102
                    default => $collector,
103
                };
104
            } catch (\Throwable) {
105
                $errors[] = \is_string($collector) || $collector instanceof \Stringable
106
                    ? (string) $collector
107 3
                    : \get_debug_type($collector);
108
                continue;
109
            }
110 4
111
            if (!$collector instanceof StateCollectorInterface) {
112
                throw new StateException(
113 357
                    \sprintf(
114
                        'Unable to populate state, invalid state collector %s',
115 357
                        \is_object($collector) ? $collector::class : \gettype($collector),
116 357
                    ),
117 357
                );
118 357
            }
119
120
            $collector->populate($state);
121
        }
122
123
        $errors === [] or $state->setTags(['unresolved-collectors' => \implode(', ', $errors)]);
124
125
        return $state;
126
    }
127
128
    private function initDefaultConfig(): void
129
    {
130
        $this->config->setDefaults(DebugConfig::CONFIG, [
131
            'collectors' => [],
132
            'tags' => [],
133
        ]);
134
    }
135
}
136