Completed
Branch feature/pre-split (afd44c)
by Anton
07:02
created

StorageManager   A

Complexity

Total Complexity 29

Size/Duplication

Total Lines 235
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 6

Importance

Changes 0
Metric Value
dl 0
loc 235
c 0
b 0
f 0
rs 10
wmc 29
lcom 1
cbo 6

11 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 11 2
A setBucket() 0 11 2
B registerBucket() 0 26 3
A getBucket() 0 14 3
A setServer() 0 12 2
A getServer() 0 15 3
A open() 0 4 1
B locateBucket() 0 17 5
A put() 0 6 2
A createInjection() 0 8 2
B createBucket() 0 24 4
1
<?php
2
/**
3
 * Spiral Framework.
4
 *
5
 * @license   MIT
6
 * @author    Anton Titov (Wolfy-J)
7
 */
8
namespace Spiral\Storage;
9
10
use Spiral\Core\Component;
11
use Spiral\Core\Container\InjectorInterface;
12
use Spiral\Core\FactoryInterface;
13
use Spiral\Storage\Configs\StorageConfig;
14
use Spiral\Storage\Entities\StorageBucket;
15
use Spiral\Storage\Entities\StorageObject;
16
use Spiral\Storage\Exceptions\StorageException;
17
18
/**
19
 * Default implementation of StorageInterface.
20
 */
21
class StorageManager extends Component implements StorageInterface, InjectorInterface
22
{
23
    /**
24
     * @var BucketInterface[]
25
     */
26
    private $buckets = [];
27
28
    /**
29
     * @var ServerInterface[]
30
     */
31
    private $servers = [];
32
33
    /**
34
     * @var StorageConfig
35
     */
36
    protected $config;
37
38
    /**
39
     * @invisible
40
     *
41
     * @var FactoryInterface
42
     */
43
    protected $factory;
44
45
    /**
46
     * @param StorageConfig    $config
47
     * @param FactoryInterface $factory
48
     */
49
    public function __construct(StorageConfig $config, FactoryInterface $factory)
50
    {
51
        $this->config = $config;
52
        $this->factory = $factory;
53
54
        //Loading buckets
55
        foreach ($this->config->getBuckets() as $name => $bucket) {
56
            //Using default implementation
57
            $this->buckets[$name] = $this->createBucket($name, $bucket);
58
        }
59
    }
60
61
    /**
62
     * @param BucketInterface $bucket
63
     *
64
     * @return self
65
     *
66
     * @throws StorageException
67
     */
68
    public function setBucket(BucketInterface $bucket): StorageManager
69
    {
70
        if (isset($this->buckets[$bucket->getName()])) {
71
            throw new StorageException("Unable to create bucket '{$bucket->getName()}', already exists");
72
        }
73
74
        $this->buckets[$bucket->getName()] = $bucket;
75
76
        return $this;
77
78
    }
79
80
    /**
81
     * {@inheritdoc}
82
     */
83
    public function registerBucket(
84
        string $name,
85
        string $prefix,
86
        $server,
87
        array $options = []
88
    ): BucketInterface {
89
        if (isset($this->buckets[$name])) {
90
            throw new StorageException("Unable to create bucket '{$name}', already exists");
91
        }
92
93
        //One of default implementation options
94
        $storage = $this;
95
96
        if (!$server instanceof ServerInterface) {
97
            $server = $this->getServer($server);
98
        }
99
100
        $bucket = $this->factory->make(
101
            StorageBucket::class,
102
            compact('storage', 'prefix', 'options', 'server')
103
        );
104
105
        $this->setBucket($bucket);
106
107
        return $bucket;
108
    }
109
110
    /**
111
     * {@inheritdoc}
112
     */
113
    public function getBucket(string $bucket): BucketInterface
114
    {
115
        if (empty($bucket)) {
116
            throw new StorageException("Unable to fetch bucket, name can not be empty");
117
        }
118
119
        $bucket = $this->config->resolveAlias($bucket);
120
121
        if (isset($this->buckets[$bucket])) {
122
            return $this->buckets[$bucket];
123
        }
124
125
        throw new StorageException("Unable to fetch bucket '{$bucket}', no presets found");
126
    }
127
128
    /**
129
     * Add server.
130
     *
131
     * @param string          $name
132
     * @param ServerInterface $server
133
     *
134
     * @return $this
135
     *
136
     * @throws StorageException
137
     */
138
    public function setServer($name, ServerInterface $server)
139
    {
140
        if (isset($this->servers[$name])) {
141
            throw new StorageException(
142
                "Unable to set storage server '{$server}', name is already taken"
143
            );
144
        }
145
146
        $this->servers[$name] = $server;
147
148
        return $this;
149
    }
150
151
    /**
152
     * {@inheritdoc}
153
     */
154
    public function getServer(string $server): ServerInterface
155
    {
156
        if (isset($this->servers[$server])) {
157
            return $this->servers[$server];
158
        }
159
160
        if (!$this->config->hasServer($server)) {
161
            throw new StorageException("Undefined storage server '{$server}'");
162
        }
163
164
        return $this->servers[$server] = $this->factory->make(
165
            $this->config->serverClass($server),
166
            $this->config->serverOptions($server)
167
        );
168
    }
169
170
    /**
171
     * {@inheritdoc}
172
     */
173
    public function open(string $address): ObjectInterface
174
    {
175
        return new StorageObject($address, $this);
176
    }
177
178
    /**
179
     * {@inheritdoc}
180
     */
181
    public function locateBucket(string $address, string &$name = null): BucketInterface
182
    {
183
        /**
184
         * @var BucketInterface $bestBucket
185
         */
186
        $bestBucket = null;
187
        foreach ($this->buckets as $bucket) {
188
            if (!empty($prefixLength = $bucket->hasAddress($address))) {
189
                if (empty($bestBucket) || strlen($bestBucket->getPrefix()) < $prefixLength) {
190
                    $bestBucket = $bucket;
191
                    $name = substr($address, $prefixLength);
192
                }
193
            }
194
        }
195
196
        return $bestBucket;
197
    }
198
199
    /**
200
     * {@inheritdoc}
201
     */
202
    public function put(string $bucket, string $name, $source = ''): ObjectInterface
203
    {
204
        $bucket = is_string($bucket) ? $this->getBucket($bucket) : $bucket;
205
206
        return $bucket->put($name, $source);
207
    }
208
209
    /**
210
     * {@inheritdoc}
211
     */
212
    public function createInjection(\ReflectionClass $class, string $context = null)
213
    {
214
        if (empty($context)) {
215
            throw new StorageException("Storage bucket can be requested without specified context");
216
        }
217
218
        return $this->getBucket($context);
219
    }
220
221
    /**
222
     * Create bucket based configuration settings.
223
     *
224
     * @param string $name
225
     * @param array  $bucket
226
     *
227
     * @return StorageBucket
228
     *
229
     * @throws StorageException
230
     */
231
    private function createBucket(string $name, array $bucket): StorageBucket
232
    {
233
        $parameters = $bucket + compact('name');
234
235
        if (!array_key_exists('options', $bucket)) {
236
            throw new StorageException("Bucket configuration must include options");
237
        }
238
239
        if (!array_key_exists('prefix', $bucket)) {
240
            throw new StorageException("Bucket configuration must include prefix");
241
        }
242
243
        if (!array_key_exists('server', $bucket)) {
244
            throw new StorageException("Bucket configuration must include server id");
245
        }
246
247
        return $this->factory->make(
248
            StorageBucket::class,
249
            $parameters + [
250
                'server'  => $this->getServer($bucket['server']),
251
                'storage' => $this
252
            ]
253
        );
254
    }
255
}
256