Passed
Pull Request — master (#19)
by
unknown
04:05 queued 01:18
created

HubBootstrapper::bootstrap()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 13
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 8
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 13
ccs 8
cts 8
cp 1
crap 1
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Yii\Sentry;
6
7
use Yiisoft\Yii\Sentry\Http\YiiRequestFetcher;
8
use Psr\Container\ContainerInterface;
9
use Psr\Log\LoggerInterface;
10
use RuntimeException;
11
use Sentry\ClientBuilder;
12
use Sentry\Integration as SdkIntegration;
13
use Sentry\Integration\IntegrationInterface;
14
use Sentry\Options;
15
use Sentry\SentrySdk;
16
use Sentry\State\HubInterface;
17
use Sentry\Transport\TransportFactoryInterface;
18
19
use function is_string;
20
21
final class HubBootstrapper
22
{
23
    public const DEFAULT_INTEGRATIONS = [];
24
25 13
    public function __construct(
26
        private Options $options,
27
        private YiiSentryConfig $configuration,
28
        private TransportFactoryInterface $transportFactory,
29
        private LoggerInterface $logger,
30
        private HubInterface $hub,
31
        private ContainerInterface $container,
32
    ) {
33
    }
34
35 13
    public function bootstrap(): void
36
    {
37 13
        $this->options->setIntegrations(fn (array $integrations) => $this->prepareIntegrations($integrations));
38
39 13
        $clientBuilder = new ClientBuilder($this->options);
40
        $clientBuilder
41 13
            ->setTransportFactory($this->transportFactory)
42 13
            ->setLogger($this->logger);
43
44 13
        $client = $clientBuilder->getClient();
45
46 13
        $this->hub->bindClient($client);
47 13
        SentrySdk::setCurrentHub($this->hub);
48
    }
49
50
    /**
51
     * @param IntegrationInterface[] $integrations
52
     *
53
     * @return IntegrationInterface[]
54
     */
55 13
    public function prepareIntegrations(array $integrations)
56
    {
57 13
        $userIntegrations = $this->resolveIntegrationsFromUserConfig();
58 13
        if ($this->options->hasDefaultIntegrations()) {
59 13
            $integrations = array_filter(
60
                $integrations,
61 13
                static function (
62
                    SdkIntegration\IntegrationInterface $integration
63
                ): bool {
64
                    if (
65
                        $integration instanceof
66
                        SdkIntegration\ErrorListenerIntegration
67
                    ) {
68 1
                        return false;
69
                    }
70
71
                    if (
72
                        $integration instanceof
73
                        SdkIntegration\ExceptionListenerIntegration
74
                    ) {
75 1
                        return false;
76
                    }
77
78
                    if (
79
                        $integration instanceof
80
                        SdkIntegration\FatalErrorListenerIntegration
81
                    ) {
82 1
                        return false;
83
                    }
84
85
                    // We also remove the default request integration so it can be readded
86
                    // after with a Yii3 specific request fetcher. This way we can resolve
87
                    // the request from Yii3 instead of constructing it from the global state
88 13
                    return ! ($integration instanceof
89
                        SdkIntegration\RequestIntegration);
90
                }
91
            );
92
93 13
            $integrations[] = new SdkIntegration\RequestIntegration(
94 13
                new YiiRequestFetcher($this->container)
95
            );
96
        }
97
98 13
        return array_merge($integrations, $userIntegrations);
99
    }
100
101
    /**
102
     * Resolve the integrations from the user configuration with the container.
103
     *
104
     * @return SdkIntegration\IntegrationInterface[]
105
     */
106 13
    private function resolveIntegrationsFromUserConfig(): array
107
    {
108
        // Default Sentry SDK integrations
109 13
        $integrations = [
110 13
            new Integration(),
111 13
            new Integration\ExceptionContextIntegration(),
112
        ];
113
114 13
        $integrationsToResolve = $this->configuration->getIntegrations();
115
116 13
        $enableDefaultTracingIntegrations = !isset($this->configuration->getTracing()['default_integrations']) ||
117 11
            (bool)$this->configuration->getTracing()['default_integrations'];
118
119
        if (
120 13
            $enableDefaultTracingIntegrations
121 13
            && $this->configuration->couldHavePerformanceTracingEnabled()
122
        ) {
123 1
            $integrationsToResolve = array_merge(
124
                $integrationsToResolve,
125
                self::DEFAULT_INTEGRATIONS
126
            );
127
        }
128
        /** @psalm-suppress MixedAssignment */
129 13
        foreach ($integrationsToResolve as $userIntegration) {
130
            if (
131
                $userIntegration instanceof
132
                SdkIntegration\IntegrationInterface
133
            ) {
134
                $integrations[] = $userIntegration;
135
            } elseif (is_string($userIntegration)) {
136
                /** @psalm-suppress MixedAssignment */
137
                $resolvedIntegration = $this->container->get($userIntegration);
138
139
                if (
140
                    !$resolvedIntegration instanceof
141
                        SdkIntegration\IntegrationInterface
142
                ) {
143
                    if (is_array($resolvedIntegration)) {
144
                        $value = 'array';
145
                    } elseif (is_object($resolvedIntegration)) {
146
                        $value = $resolvedIntegration::class;
147
                    } elseif (null === $resolvedIntegration) {
148
                        $value = 'null';
149
                    } else {
150
                        $value = (string)$resolvedIntegration;
151
                    }
152
153
                    throw new RuntimeException(
154
                        sprintf(
155
                            'Sentry integration must be an instance of `%s` got `%s`.',
156
                            SdkIntegration\IntegrationInterface::class,
157
                            $value
158
                        )
159
                    );
160
                }
161
162
                $integrations[] = $resolvedIntegration;
163
            } else {
164
                throw new RuntimeException(
165
                    sprintf(
166
                        'Sentry integrations must either be a valid container reference or an instance of `%s`.',
167
                        SdkIntegration\IntegrationInterface::class
168
                    )
169
                );
170
            }
171
        }
172
173 13
        return $integrations;
174
    }
175
}
176