Completed
Push — master ( a4e6dd...a851cc )
by Arne
02:45
created

Container::getIndexBuilder()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
nop 1
1
<?php
2
3
namespace Storeman;
4
5
use League\Container\Definition\DefinitionInterface;
6
use Psr\Container\ContainerInterface;
7
use Storeman\ConflictHandler\ConflictHandlerInterface;
8
use Storeman\ConflictHandler\PanickingConflictHandler;
9
use Storeman\ConflictHandler\PreferLocalConflictHandler;
10
use Storeman\ConflictHandler\PreferRemoteConflictHandler;
11
use Storeman\IndexBuilder\IndexBuilderInterface;
12
use Storeman\IndexBuilder\StandardIndexBuilder;
13
use Storeman\IndexMerger\IndexMergerInterface;
14
use Storeman\IndexMerger\StandardIndexMerger;
15
use Storeman\LockAdapter\DummyLockAdapter;
16
use Storeman\LockAdapter\LockAdapterInterface;
17
use Storeman\LockAdapter\StorageBasedLockAdapter;
18
use Storeman\OperationListBuilder\OperationListBuilderInterface;
19
use Storeman\OperationListBuilder\StandardOperationListBuilder;
20
use Storeman\StorageAdapter\LocalStorageAdapter;
21
use Storeman\StorageAdapter\StorageAdapterInterface;
22
use Storeman\Validation\Constraints\ExistingServiceValidator;
23
use Storeman\VaultLayout\Amberjack\AmberjackVaultLayout;
24
use Storeman\VaultLayout\VaultLayoutInterface;
25
26
/**
27
 * Dependency injection container for modularized parts.
28
 * It is basically a facade for a PSR container implementation with some additional accessors for type safety.
29
 */
30
final class Container implements ContainerInterface
31
{
32
    protected const PREFIX_CONFLICT_HANDLER = 'conflictHandler.';
33
    protected const PREFIX_INDEX_BUILDER = 'indexBuilder.';
34
    protected const PREFIX_INDEX_MERGER = 'indexMerger.';
35
    protected const PREFIX_LOCK_ADAPTER = 'lockAdapter.';
36
    protected const PREFIX_OPERATION_LIST_BUILDER = 'operationListBuilder.';
37
    protected const PREFIX_STORAGE_ADAPTER = 'storageAdapter.';
38
    protected const PREFIX_VAULT_LAYOUT = 'vaultLayout.';
39
40
41
    /**
42
     * @var InspectableContainer
43
     */
44
    protected $delegate;
45
46
    /**
47
     * Sets up the container with all bundled services.
48
     *
49
     * Note: Be very careful with shared services as the container is used shared by all vaults of a storeman instance.
50
     */
51
    public function __construct()
52
    {
53
        $this->delegate = new InspectableContainer();
54
55
        $this->delegate->add('vaults', new VaultContainer(), true);
56
        $this->delegate->add('vaultConfiguration', function(Vault $vault) { return $vault->getVaultConfiguration(); })->withArgument('vault');
57
58
        $this->registerServiceFactory('indexBuilder');
59
        $this->addIndexBuilder('standard', StandardIndexBuilder::class, true);
60
61
        $this->registerVaultServiceFactory('conflictHandler');
62
        $this->addConflictHandler('panicking', PanickingConflictHandler::class, true);
63
        $this->addConflictHandler('preferLocal', PreferLocalConflictHandler::class, true);
64
        $this->addConflictHandler('preferRemote', PreferRemoteConflictHandler::class, true);
65
66
        $this->registerVaultServiceFactory('indexMerger');
67
        $this->addIndexMerger('standard', StandardIndexMerger::class, true);
68
69
        $this->registerVaultServiceFactory('lockAdapter');
70
        $this->addLockAdapter('dummy', DummyLockAdapter::class);
71
        $this->addLockAdapter('storage', StorageBasedLockAdapter::class)->withArgument('storageAdapter');
72
73
        $this->registerVaultServiceFactory('operationListBuilder');
74
        $this->addOperationListBuilder('standard', StandardOperationListBuilder::class, true);
75
76
        $this->registerVaultServiceFactory('storageAdapter', 'adapter');
77
        $this->addStorageAdapter('local', LocalStorageAdapter::class)->withArgument('vaultConfiguration');
78
79
        $this->registerVaultServiceFactory('vaultLayout');
80
        $this->addVaultLayout('amberjack', AmberjackVaultLayout::class)->withArguments(['storageAdapter', 'vaultConfiguration']);
81
82
        $this->delegate->add(ExistingServiceValidator::class)->withArgument($this);
83
    }
84
85
    public function injectConfiguration(Configuration $configuration): Container
86
    {
87
        if ($this->has('configuration'))
88
        {
89
            throw new \RuntimeException();
90
        }
91
92
        $this->delegate->add('configuration', $configuration, true);
93
94
        return $this;
95
    }
96
97
    public function injectStoreman(Storeman $storeman): Container
98
    {
99
        if ($this->has('storeman'))
100
        {
101
            throw new \RuntimeException();
102
        }
103
104
        $this->delegate->add('storeman', $storeman, true);
105
106
        return $this;
107
    }
108
109
    /**
110
     * Selects the given vault (or no vault) as the current one.
111
     *
112
     * @param Vault $vault
113
     * @return Container
114
     */
115
    public function selectVault(Vault $vault = null): Container
116
    {
117
        $this->delegate->add('vault', $vault, true);
118
119
        return $this;
120
    }
121
122
    /**
123
     * {@inheritdoc}
124
     */
125
    public function get($id)
126
    {
127
        return $this->delegate->get($id);
128
    }
129
130
    /**
131
     * {@inheritdoc}
132
     */
133
    public function has($id)
134
    {
135
        return $this->delegate->has($id);
136
    }
137
138
139
    public function getStoreman(): Storeman
140
    {
141
        return $this->get('storeman');
142
    }
143
144
    public function getConfiguration(): Configuration
145
    {
146
        return $this->get('configuration');
147
    }
148
149
    public function getVaults(): VaultContainer
150
    {
151
        return $this->get('vaults');
152
    }
153
154
    public function getSelectedVault(): ?Vault
155
    {
156
        return $this->get('vault');
157
    }
158
159
160
    public function getConflictHandler(string $name): ConflictHandlerInterface
161
    {
162
        return $this->delegate->get($this->getConflictHandlerServiceName($name));
163
    }
164
165
    public function addConflictHandler(string $name, $concrete, bool $shared = false): DefinitionInterface
166
    {
167
        return $this->delegate->add($this->getConflictHandlerServiceName($name), $concrete, $shared);
168
    }
169
170
    public function getConflictHandlerNames(): array
171
    {
172
        return $this->getServiceNamesWithPrefix(static::PREFIX_CONFLICT_HANDLER);
173
    }
174
175
176
    public function getIndexMerger(string $name): IndexMergerInterface
177
    {
178
        return $this->delegate->get($this->getIndexMergerServiceName($name));
179
    }
180
181
    public function addIndexMerger(string $name, $concrete, bool $shared = false): DefinitionInterface
182
    {
183
        return $this->delegate->add($this->getIndexMergerServiceName($name), $concrete, $shared);
184
    }
185
186
    public function getIndexMergerNames(): array
187
    {
188
        return $this->getServiceNamesWithPrefix(static::PREFIX_INDEX_MERGER);
189
    }
190
191
192
    public function getIndexBuilder(string $name): IndexBuilderInterface
193
    {
194
        return $this->delegate->get($this->getIndexBuilderServiceName($name));
195
    }
196
197
    public function addIndexBuilder(string $name, $concrete, bool $shared = false): DefinitionInterface
198
    {
199
        return $this->delegate->add($this->getIndexBuilderServiceName($name), $concrete, $shared);
200
    }
201
202
    public function getIndexBuilderNames(): array
203
    {
204
        return $this->getServiceNamesWithPrefix(static::PREFIX_INDEX_BUILDER);
205
    }
206
207
208
    public function getLockAdapter(string $name): LockAdapterInterface
209
    {
210
        return $this->delegate->get($this->getLockAdapterServiceName($name));
211
    }
212
213
    public function addLockAdapter(string $name, $concrete, bool $shared = false): DefinitionInterface
214
    {
215
        return $this->delegate->add($this->getLockAdapterServiceName($name), $concrete, $shared);
216
    }
217
218
    public function getLockAdapterNames(): array
219
    {
220
        return $this->getServiceNamesWithPrefix(static::PREFIX_LOCK_ADAPTER);
221
    }
222
223
    
224
    public function getOperationListBuilder(string $name): OperationListBuilderInterface
225
    {
226
        return $this->delegate->get($this->getOperationListBuilderServiceName($name));
227
    }
228
229
    public function addOperationListBuilder(string $name, $concrete, bool $shared = false): DefinitionInterface
230
    {
231
        return $this->delegate->add($this->getOperationListBuilderServiceName($name), $concrete, $shared);
232
    }
233
234
    public function getOperationListBuilderNames(): array
235
    {
236
        return $this->getServiceNamesWithPrefix(static::PREFIX_OPERATION_LIST_BUILDER);
237
    }
238
239
240
    public function getStorageAdapter(string $name): StorageAdapterInterface
241
    {
242
        return $this->delegate->get($this->getStorageAdapterServiceName($name));
243
    }
244
245
    public function addStorageAdapter(string $name, $concrete, bool $shared = false): DefinitionInterface
246
    {
247
        return $this->delegate->add($this->getStorageAdapterServiceName($name), $concrete, $shared);
248
    }
249
250
    public function getStorageAdapterNames(): array
251
    {
252
        return $this->getServiceNamesWithPrefix(static::PREFIX_STORAGE_ADAPTER);
253
    }
254
255
256
    public function getVaultLayout(string $name): VaultLayoutInterface
257
    {
258
        return $this->delegate->get($this->getVaultLayoutServiceName($name));
259
    }
260
261
    public function addVaultLayout(string $name, $concrete, bool $shared = false): DefinitionInterface
262
    {
263
        return $this->delegate->add($this->getVaultLayoutServiceName($name), $concrete, $shared);
264
    }
265
266
    public function getVaultLayoutNames(): array
267
    {
268
        return $this->getServiceNamesWithPrefix(static::PREFIX_VAULT_LAYOUT);
269
    }
270
271
272
    protected function getConflictHandlerServiceName(string $name): string
273
    {
274
        return static::PREFIX_CONFLICT_HANDLER . $name;
275
    }
276
277
    protected function getIndexBuilderServiceName(string $name): string
278
    {
279
        return static::PREFIX_INDEX_BUILDER . $name;
280
    }
281
282
    protected function getIndexMergerServiceName(string $name): string
283
    {
284
        return static::PREFIX_INDEX_MERGER . $name;
285
    }
286
287
    protected function getLockAdapterServiceName(string $name): string
288
    {
289
        return static::PREFIX_LOCK_ADAPTER . $name;
290
    }
291
292
    protected function getOperationListBuilderServiceName(string $name): string
293
    {
294
        return static::PREFIX_OPERATION_LIST_BUILDER . $name;
295
    }
296
297
    protected function getStorageAdapterServiceName(string $name): string
298
    {
299
        return static::PREFIX_STORAGE_ADAPTER . $name;
300
    }
301
302
    protected function getVaultLayoutServiceName(string $name): string
303
    {
304
        return static::PREFIX_VAULT_LAYOUT . $name;
305
    }
306
307
    /**
308
     * Registers a factory for a type of service.
309
     *
310
     * @param string $type
311
     * @param string $configurationKey
312
     */
313
    protected function registerServiceFactory(string $type, string $configurationKey = null): void
314
    {
315
        $this->delegate->add($type, function(Configuration $configuration) use ($configurationKey, $type) {
316
317
            $array = $configuration->getArrayCopy();
318
            $configurationKey = $configurationKey ?: $type;
0 ignored issues
show
Bug introduced by
Consider using a different name than the imported variable $configurationKey, or did you forget to import by reference?

It seems like you are assigning to a variable which was imported through a use statement which was not imported by reference.

For clarity, we suggest to use a different name or import by reference depending on whether you would like to have the change visibile in outer-scope.

Change not visible in outer-scope

$x = 1;
$callable = function() use ($x) {
    $x = 2; // Not visible in outer scope. If you would like this, how
            // about using a different variable name than $x?
};

$callable();
var_dump($x); // integer(1)

Change visible in outer-scope

$x = 1;
$callable = function() use (&$x) {
    $x = 2;
};

$callable();
var_dump($x); // integer(2)
Loading history...
319
320
            if (!array_key_exists($configurationKey, $array))
321
            {
322
                throw new \LogicException("Unknown config key: {$configuration}");
323
            }
324
325
            return $this->delegate->get("{$type}.{$array[$configurationKey]}");
326
327
        })->withArgument('configuration');
328
    }
329
330
    /**
331
     * Registers a factory for a type of vault-specific module.
332
     *
333
     * @param string $type
334
     * @param string $vaultConfigurationKey
335
     */
336
    protected function registerVaultServiceFactory(string $type, string $vaultConfigurationKey = null): void
337
    {
338
        $this->delegate->add($type, function(Vault $vault) use ($type, $vaultConfigurationKey) {
339
340
            $array = $vault->getVaultConfiguration()->getArrayCopy();
341
            $vaultConfigurationKey = $vaultConfigurationKey ?: $type;
0 ignored issues
show
Bug introduced by
Consider using a different name than the imported variable $vaultConfigurationKey, or did you forget to import by reference?

It seems like you are assigning to a variable which was imported through a use statement which was not imported by reference.

For clarity, we suggest to use a different name or import by reference depending on whether you would like to have the change visibile in outer-scope.

Change not visible in outer-scope

$x = 1;
$callable = function() use ($x) {
    $x = 2; // Not visible in outer scope. If you would like this, how
            // about using a different variable name than $x?
};

$callable();
var_dump($x); // integer(1)

Change visible in outer-scope

$x = 1;
$callable = function() use (&$x) {
    $x = 2;
};

$callable();
var_dump($x); // integer(2)
Loading history...
342
343
            if (!array_key_exists($vaultConfigurationKey, $array))
344
            {
345
                throw new \LogicException("Unknown vault config key: {$vaultConfigurationKey}");
346
            }
347
348
            return $this->delegate->get("{$type}.{$array[$vaultConfigurationKey]}");
349
350
        })->withArgument('vault');
351
    }
352
353
    /**
354
     * Builds and returns list of added service names starting with a given prefix.
355
     *
356
     * @param string $prefix
357
     * @return string[]
358
     */
359
    protected function getServiceNamesWithPrefix(string $prefix): array
360
    {
361
        return array_map(
362
363
            // remove prefix
364
            function(string $alias) use ($prefix) {
365
                return substr($alias, strlen($prefix));
366
            },
367
368
            array_filter(
369
370
                // search in all registered services
371
                $this->delegate->getProvidedServiceNames(),
372
373
                // only service names with given prefix
374
                function(string $alias) use ($prefix) {
375
                    return substr($alias, 0, strlen($prefix)) === $prefix;
376
                }
377
            )
378
        );
379
    }
380
}
381