StorageService::adapter()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 13
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 3
eloc 6
c 2
b 0
f 0
nc 3
nop 1
dl 0
loc 13
rs 10
1
<?php
2
3
/**
4
 * Copyright (c) Florian Krämer (https://florian-kraemer.net)
5
 * Licensed under The MIT License
6
 * For full copyright and license information, please see the LICENSE.txt
7
 * Redistributions of files must retain the above copyright notice.
8
 *
9
 * @copyright Copyright (c) Florian Krämer (https://florian-kraemer.net)
10
 * @author    Florian Krämer
11
 * @link      https://github.com/Phauthentic
12
 * @license   https://opensource.org/licenses/MIT MIT License
13
 */
14
15
declare(strict_types=1);
16
17
namespace Phauthentic\Infrastructure\Storage;
18
19
use League\Flysystem\AdapterInterface;
20
use League\Flysystem\Config;
21
use Phauthentic\Infrastructure\Storage\Exception\StorageException;
22
use Phauthentic\Infrastructure\Storage\Factories\Exception\FactoryNotFoundException;
23
use RuntimeException;
24
25
/**
26
 * StorageFactory - Manages and instantiates storage engine adapters.
27
 *
28
 * @author Florian Krämer
29
 * @copyright 2012 - 2020 Florian Krämer
30
 * @license MIT
31
 */
32
class StorageService implements StorageServiceInterface
33
{
34
    /**
35
     * @var array<string, array{class: string, options: array<string, mixed>}>
36
     */
37
    protected array $adapterConfig = [];
38
39
    /**
40
     * @var \Phauthentic\Infrastructure\Storage\AdapterCollectionInterface
41
     */
42
    protected AdapterCollectionInterface $adapterCollection;
43
44
    /**
45
     * @var \Phauthentic\Infrastructure\Storage\StorageAdapterFactoryInterface
46
     */
47
    protected StorageAdapterFactoryInterface $adapterFactory;
48
49
    /**
50
     * Constructor
51
     *
52
     * @param \Phauthentic\Infrastructure\Storage\StorageAdapterFactoryInterface $adapterFactory Adapter Factory
53
     * @param \Phauthentic\Infrastructure\Storage\AdapterCollectionInterface|null $factoryCollection Factory Collection
54
     */
55
    public function __construct(
56
        StorageAdapterFactoryInterface $adapterFactory,
57
        ?AdapterCollectionInterface $factoryCollection = null
58
    ) {
59
        $this->adapterFactory = $adapterFactory;
60
        $this->adapterCollection = $factoryCollection ?? new AdapterCollection();
61
    }
62
63
    /**
64
     * Adapter Factory
65
     *
66
     * @return \Phauthentic\Infrastructure\Storage\StorageAdapterFactoryInterface
67
     */
68
    public function adapterFactory(): StorageAdapterFactoryInterface
69
    {
70
        return $this->adapterFactory;
71
    }
72
73
    /**
74
     * @inheritDoc
75
     */
76
    public function adapters(): AdapterCollectionInterface
77
    {
78
        return $this->adapterCollection;
79
    }
80
81
    /**
82
     * @inheritDoc
83
     */
84
    public function adapter(string $name): AdapterInterface
85
    {
86
        if ($this->adapterCollection->has($name)) {
87
            return $this->adapterCollection->get($name);
88
        }
89
90
        if (!isset($this->adapterConfig[$name])) {
91
            throw FactoryNotFoundException::withName($name);
92
        }
93
94
        $options = $this->adapterConfig[$name];
95
96
        return $this->loadAdapter($name, $options['class'], $options['options']);
97
    }
98
99
    /**
100
     * Loads an adapter instance using the factory
101
     *
102
     * @param string $name Name
103
     * @param string $adapter Adapter
104
     * @param array<string, mixed> $options
105
     * @return \League\Flysystem\AdapterInterface
106
     */
107
    public function loadAdapter(string $name, string $adapter, array $options): AdapterInterface
108
    {
109
        $adapter = $this->adapterFactory->buildStorageAdapter(
110
            $adapter,
111
            $options
112
        );
113
114
        $this->adapterCollection->add($name, $adapter);
115
116
        return $adapter;
117
    }
118
119
    /**
120
     * Adds an adapter config
121
     *
122
     * @param string $name
123
     * @param string $class
124
     * @param array<string, mixed> $options
125
     * @return void
126
     */
127
    public function addAdapterConfig(string $name, string $class, array $options): void
128
    {
129
        $this->adapterConfig[$name] = [
130
            'class' => $class,
131
            'options' => $options
132
        ];
133
    }
134
135
    /**
136
     * Sets the adapter configuration to lazy load them later
137
     *
138
     * @param array<string, array{class?: string, options?: mixed}> $config Config
139
     * @return void
140
     */
141
    public function setAdapterConfigFromArray(array $config): void
142
    {
143
        foreach ($config as $name => $options) {
144
            if (!isset($options['class'])) {
145
                throw new RuntimeException('Adapter class or name is missing');
146
            }
147
            $class = (string)$options['class'];
148
149
            if (!isset($options['options'])) {
150
                throw new RuntimeException('Adapter options must be an array');
151
            }
152
            $opts = $options['options'];
153
            if (!is_array($opts)) {
154
                throw new RuntimeException('Adapter options must be an array');
155
            }
156
157
            /** @var array{class: string, options: array<string, mixed>} $validatedOptions */
158
            $validatedOptions = [
159
                'class' => $class,
160
                'options' => $opts
161
            ];
162
            $this->adapterConfig[$name] = $validatedOptions;
163
        }
164
    }
165
166
    /**
167
     * @param \League\Flysystem\Config|null $config Config
168
     * @return \League\Flysystem\Config
169
     */
170
    protected function makeConfigIfNeeded(?Config $config): Config
171
    {
172
        if ($config === null) {
173
            $config = new Config();
174
        }
175
176
        return $config;
177
    }
178
179
    /**
180
     * @inheritDoc
181
     * @return array<string, mixed>
182
     */
183
    public function storeResource(string $adapter, string $path, $resource, ?Config $config = null): array
184
    {
185
        $config = $this->makeConfigIfNeeded($config);
186
        $result = $this->adapter($adapter)->writeStream($path, $resource, $config);
187
188
        if ($result === false) {
189
            throw new StorageException(sprintf(
190
                'Failed to store resource stream to in `%s` with path `%s`',
191
                $adapter,
192
                $path
193
            ));
194
        }
195
196
        return $result;
197
    }
198
199
    /**
200
     * @inheritDoc
201
     * @return array<string, mixed>
202
     */
203
    public function storeFile(string $adapter, string $path, string $file, ?Config $config = null): array
204
    {
205
        $config = $this->makeConfigIfNeeded($config);
206
        $contents = file_get_contents($file);
207
        if ($contents === false) {
208
            throw new StorageException(sprintf(
209
                'Failed to read file contents from `%s`',
210
                $file
211
            ));
212
        }
213
        $result = $this->adapter($adapter)->write($path, $contents, $config);
214
215
        if ($result === false) {
216
            throw new StorageException(sprintf(
217
                'Failed to store file `%s` in `%s` with path `%s`',
218
                $file,
219
                $adapter,
220
                $path
221
            ));
222
        }
223
224
        return $result;
225
    }
226
227
    /**
228
     * @param string $adapter Adapter
229
     * @param string $path Path
230
     * @return bool
231
     */
232
    public function fileExists(string $adapter, string $path): bool
233
    {
234
        $result = $this->adapter($adapter)->has($path);
235
236
        return (bool)$result;
237
    }
238
239
    /**
240
     * @param string $adapter Name
241
     * @param string $path File to delete
242
     * @return bool
243
     */
244
    public function removeFile(string $adapter, string $path): bool
245
    {
246
        return $this->adapter($adapter)->delete($path);
247
    }
248
}
249