DistributionConfig::createS3Resolver()   C
last analyzed

Complexity

Conditions 13
Paths 9

Size

Total Lines 53
Code Lines 28

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 182

Importance

Changes 0
Metric Value
eloc 28
dl 0
loc 53
ccs 0
cts 31
cp 0
rs 6.6166
c 0
b 0
f 0
cc 13
nc 9
nop 2
crap 182

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Spiral\Distribution\Config;
6
7
use Aws\Credentials\Credentials;
8
use Aws\S3\S3Client;
9
use GuzzleHttp\Psr7\Uri as GuzzleUri;
10
use Nyholm\Psr7\Uri as NyholmUri;
11
use Psr\Http\Message\UriInterface;
12
use Spiral\Core\InjectableConfig;
13
use Spiral\Distribution\Manager;
14
use Spiral\Distribution\Resolver\CloudFrontResolver;
15
use Spiral\Distribution\Resolver\S3SignedResolver;
16
use Spiral\Distribution\Resolver\StaticResolver;
17
use Spiral\Distribution\UriResolverInterface;
18
use Spiral\Config\Exception\InvalidArgumentException;
19
20
final class DistributionConfig extends InjectableConfig
21
{
22
    public const CONFIG = 'distribution';
23
24
    private string $default = Manager::DEFAULT_RESOLVER;
25
26
    /**
27
     * @var array<string, UriResolverInterface>
28
     */
29
    private array $resolvers = [];
30
31 3
    public function __construct(array $config = [])
32
    {
33 3
        parent::__construct($config);
34
35 3
        $this->bootDefaultDriver($config);
36 3
        $this->bootResolvers($config);
37
    }
38
39 3
    public function getDefaultDriver(): string
40
    {
41 3
        return $this->default;
42
    }
43
44
    /**
45
     * @return iterable<string, UriResolverInterface>
46
     */
47 3
    public function getResolvers(): iterable
48
    {
49 3
        return $this->resolvers;
50
    }
51
52 3
    private function bootResolvers(array $config): void
53
    {
54 3
        foreach ($config['resolvers'] ?? [] as $name => $child) {
55
            if (!\is_string($name)) {
56
                throw new InvalidArgumentException(
57
                    \vsprintf('Distribution driver config key must be a string, but %s given', [
58
                        \get_debug_type($child),
59
                    ]),
60
                );
61
            }
62
63
            if (!\is_array($child)) {
64
                throw new InvalidArgumentException(
65
                    \vsprintf('Distribution driver config `%s` must be an array, but %s given', [
66
                        $name,
67
                        \get_debug_type($child),
68
                    ]),
69
                );
70
            }
71
72
            $this->resolvers[$name] = $this->createResolver($name, $child);
73
        }
74
    }
75
76 3
    private function bootDefaultDriver(array $config): void
77
    {
78 3
        $default = $config['default'] ?? null;
79
80 3
        if ($default !== null) {
81
            // Validate config
82 3
            if (!\is_string($default)) {
83
                throw new InvalidArgumentException(
84
                    \vsprintf('Distribution config default driver must be a string, but %s given', [
85
                        \get_debug_type($default),
86
                    ]),
87
                );
88
            }
89
90 3
            $this->default = $default;
91
        }
92
    }
93
94
    private function createResolver(string $name, array $config): UriResolverInterface
95
    {
96
        if (!isset($config['type']) || !\is_string($config['type'])) {
97
            throw $this->invalidConfigKey($name, 'type', 'string');
98
        }
99
100
        return match ($config['type']) {
101
            'static' => $this->createStaticResolver($name, $config),
102
            's3' => $this->createS3Resolver($name, $config),
103
            'cloudfront' => $this->createCloudFrontResolver($name, $config),
104
            default => $this->createCustomResolver($config['type'], $name, $config),
105
        };
106
    }
107
108
    private function createCustomResolver(string $type, string $name, array $config): UriResolverInterface
109
    {
110
        if (!\is_subclass_of($type, UriResolverInterface::class, true)) {
111
            throw $this->invalidConfigKey($name, 'type', UriResolverInterface::class);
112
        }
113
114
        if (isset($config['options']) && !\is_array($config['options'])) {
115
            throw $this->invalidConfigKey($name, 'options', 'array');
116
        }
117
118
        try {
119
            return new $type(...\array_values($config['options'] ?? []));
120
        } catch (\Throwable $e) {
121
            $message = 'An error occurred while resolver `%s` initializing: %s';
122
            throw new InvalidArgumentException(\sprintf($message, $name, $e->getMessage()), 0, $e);
123
        }
124
    }
125
126
    private function createS3Resolver(string $name, array $config): UriResolverInterface
127
    {
128
        // Required config options
129
        if (!isset($config['region']) || !\is_string($config['region'])) {
130
            throw $this->invalidConfigKey($name, 'region', 'string');
131
        }
132
133
        if (!isset($config['key']) || !\is_string($config['key'])) {
134
            throw $this->invalidConfigKey($name, 'key', 'string');
135
        }
136
137
        if (!isset($config['secret']) || !\is_string($config['secret'])) {
138
            throw $this->invalidConfigKey($name, 'secret', 'string');
139
        }
140
141
        if (!isset($config['bucket']) || !\is_string($config['bucket'])) {
142
            throw $this->invalidConfigKey($name, 'bucket', 'string');
143
        }
144
145
        // Optional config options
146
        if (!\is_string($config['prefix'] ?? '')) {
147
            throw $this->invalidConfigKey($name, 'prefix', 'string or null');
148
        }
149
150
        if (!\is_string($config['version'] ?? '')) {
151
            throw $this->invalidConfigKey($name, 'version', 'string or null');
152
        }
153
154
        if (!\is_string($config['token'] ?? '')) {
155
            throw $this->invalidConfigKey($name, 'token', 'string or null');
156
        }
157
158
        if (!\is_int($config['expires'] ?? 0)) {
159
            throw $this->invalidConfigKey($name, 'expires', 'positive int (unix timestamp)');
160
        }
161
162
        $s3Options = [
163
            'version'     => $config['version'] ?? 'latest',
164
            'region'      => $config['region'],
165
            'endpoint'    => $config['endpoint'] ?? null,
166
            'credentials' => new Credentials(
167
                $config['key'],
168
                $config['secret'],
169
                $config['token'] ?? null,
170
                $config['expires'] ?? null,
171
            ),
172
        ];
173
174
        $s3Options += ($config['options'] ?? []);
175
176
        $client = new S3Client($s3Options);
177
178
        return new S3SignedResolver($client, $config['bucket'], $config['prefix'] ?? null);
179
    }
180
181
    private function createCloudFrontResolver(string $name, array $config): UriResolverInterface
182
    {
183
        if (!isset($config['key']) || !\is_string($config['key'])) {
184
            throw $this->invalidConfigKey($name, 'key', 'string');
185
        }
186
187
        if (!isset($config['private']) || !\is_string($config['private'])) {
188
            throw $this->invalidConfigKey($name, 'private', 'string value or path to key file');
189
        }
190
191
        if (!isset($config['domain']) || !\is_string($config['domain'])) {
192
            throw $this->invalidConfigKey($name, 'domain', 'string');
193
        }
194
195
        if (!\is_string($config['prefix'] ?? '')) {
196
            throw $this->invalidConfigKey($name, 'prefix', 'string or null');
197
        }
198
199
        return new CloudFrontResolver(
200
            $config['key'],
201
            $config['private'],
202
            $config['domain'],
203
            $config['prefix'] ?? null,
204
        );
205
    }
206
207
    private function createStaticResolver(string $name, array $config): UriResolverInterface
208
    {
209
        if (!isset($config['uri']) || !\is_string($config['uri'])) {
210
            throw $this->invalidConfigKey($name, 'uri', 'string');
211
        }
212
213
        return new StaticResolver($this->createUri($name, $config['uri'], $config));
214
    }
215
216
    private function createUri(string $name, string $uri, array $config): UriInterface
217
    {
218
        if (!\is_string($config['factory'] ?? '')) {
219
            throw $this->invalidConfigKey($name, 'factory', 'string (PSR-7 uri factory implementation)');
220
        }
221
222
        return match (true) {
223
            isset($config['factory']) => (new $config['factory']())->createUri($uri),
224
            \class_exists(NyholmUri::class) => new NyholmUri($uri),
225
            \class_exists(GuzzleUri::class) => new GuzzleUri($uri),
226
            default => throw new InvalidArgumentException(
227
                \sprintf('Can not resolve available PSR-7 UriFactory implementation; 
228
                    Please define `factory` config section in `%s` distribution driver config', $name),
229
            ),
230
        };
231
    }
232
233
    private function invalidConfigKey(string $name, string $key, string $type): InvalidArgumentException
234
    {
235
        $message = 'Distribution config of `%s` driver must contain key `%s` that must be a type of %s';
236
237
        return new InvalidArgumentException(\sprintf($message, $name, $key, $type));
238
    }
239
}
240