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

Storeman::getPrioritizedVault()   B

Complexity

Conditions 5
Paths 6

Size

Total Lines 17
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 17
rs 8.8571
cc 5
eloc 7
nc 6
nop 1
1
<?php
2
3
namespace Storeman;
4
5
use Storeman\Exception\Exception;
6
use Storeman\SynchronizationProgressListener\SynchronizationProgressListenerInterface;
7
8
/**
9
 * This class coordinates executions with multiple vaults involved.
10
 */
11
class Storeman
12
{
13
    /**
14
     * @var Container
15
     */
16
    protected $container;
17
18
    public function __construct(Container $container = null)
19
    {
20
        $this->container = $container ?: new Container();
21
        $this->container->injectStoreman($this);
22
    }
23
24
    public function getConfiguration(): Configuration
25
    {
26
        return $this->container->get('configuration');
27
    }
28
29
    /**
30
     * Returns the DI container of this storeman instance.
31
     * Some service names resolve to different implementations depending on the current vault which can be set as a context.
32
     * E.g. "storageAdapter" resolves to the storage adapter implementation configured for the current vault.
33
     *
34
     * @param Vault $vault
35
     * @return Container
36
     */
37
    public function getContainer(Vault $vault = null): Container
38
    {
39
        return $this->container->selectVault($vault);
40
    }
41
42
    /**
43
     * Returns a specific vault by title.
44
     *
45
     * @param string $vaultTitle
46
     * @return Vault
47
     */
48
    public function getVault(string $vaultTitle): Vault
49
    {
50
        $vaults = $this->container->getVaults();
51
52
        if (!$vaults->has($vaultTitle))
53
        {
54
            $vaultConfiguration = $this->getConfiguration()->getVault($vaultTitle);
55
56
            $vaults->register(new Vault($this, $vaultConfiguration));
57
        }
58
59
        return $vaults->get($vaultTitle);
60
    }
61
62
    /**
63
     * Returns all configured vaults.
64
     *
65
     * @return Vault[]
66
     */
67
    public function getVaults(): array
68
    {
69
        return array_values(array_map(function(VaultConfiguration $vaultConfiguration) {
70
71
            return $this->getVault($vaultConfiguration->getTitle());
72
73
        }, $this->getConfiguration()->getVaults()));
74
    }
75
76
    /**
77
     * Returns a subset of the configured vaults identified by the given set of titles.
78
     *
79
     * @param array $titles
80
     * @return Vault[]
81
     */
82
    public function getVaultsByTitle(array $titles): array
83
    {
84
        return array_filter($this->getVaults(), function(Vault $vault) use ($titles) {
85
86
            return in_array($vault->getVaultConfiguration()->getTitle(), $titles);
87
        });
88
    }
89
90
    /**
91
     * Returns all those vaults that have a given revision.
92
     *
93
     * @param int $revision
94
     * @return Vault[]
95
     */
96
    public function getVaultsHavingRevision(int $revision): array
97
    {
98
        return array_filter($this->getVaults(), function(Vault $vault) use ($revision) {
99
100
            return $vault->getVaultLayout()->getSynchronizations()->getSynchronization($revision) !== null;
101
        });
102
    }
103
104
    /**
105
     * Returns the vault with the highest priority.
106
     *
107
     * @param Vault[] $vaults Vaults to consider. Defaults to all configured vaults.
108
     * @return Vault
109
     */
110
    public function getPrioritizedVault(array $vaults = null): ?Vault
111
    {
112
        $vaults = ($vaults === null) ? $this->getVaults() : $vaults;
113
114
        /** @var Vault $return */
115
        $return = null;
116
117
        foreach ($vaults as $vault)
118
        {
119
            if ($return === null || $return->getVaultConfiguration()->getPriority() < $vault->getVaultConfiguration()->getPriority())
120
            {
121
                $return = $vault;
122
            }
123
        }
124
125
        return $return;
126
    }
127
128
    public function synchronize(array $vaultTitles = null, SynchronizationProgressListenerInterface $progressListener = null): OperationResultList
129
    {
130
        $vaults = ($vaultTitles === null) ? $this->getVaults() : $this->getVaultsByTitle($vaultTitles);
131
132
        $this->acquireLocks($vaults, Vault::LOCK_SYNC);
133
134
        $newRevision = ($this->getLastRevision() ?: 0) + 1;
135
136
        $return = new OperationResultList();
137
        foreach ($vaults as $vault)
138
        {
139
            $return->append($vault->synchronize($newRevision, $progressListener));
140
        }
141
142
        $this->releaseLocks($vaults, Vault::LOCK_SYNC);
143
144
        return $return;
145
    }
146
147
    public function restore(int $toRevision = null, string $fromVault = null, SynchronizationProgressListenerInterface $progressListener = null): OperationResultList
148
    {
149
        $vault = $this->getVaultForDownload($toRevision, $fromVault);
150
151
        if ($vault === null)
152
        {
153
            return new OperationResultList();
154
        }
155
156
        $operationResultList = $vault->restore($toRevision, $progressListener);
157
158
        return $operationResultList;
159
    }
160
161
    public function dump(string $targetPath, int $revision = null, string $fromVault = null, SynchronizationProgressListenerInterface $progressListener = null): OperationResultList
162
    {
163
        $vault = $this->getVaultForDownload($revision, $fromVault);
164
165
        if ($vault === null)
166
        {
167
            return new OperationResultList();
168
        }
169
170
        $operationResultList = $vault->dump($targetPath, $revision, $progressListener);
171
172
        return $operationResultList;
173
    }
174
175
    /**
176
     * Returns the highest revision number across all vaults.
177
     *
178
     * @return int
179
     */
180
    public function getLastRevision(): ?int
181
    {
182
        $max = 0;
183
184
        foreach ($this->getVaults() as $vault)
185
        {
186
            if ($lastSynchronization = $vault->getVaultLayout()->getLastSynchronization())
187
            {
188
                $max = max($max, $lastSynchronization->getRevision());
189
            }
190
        }
191
192
        return $max ?: null;
193
    }
194
195
    /**
196
     * Builds and returns a history of all synchronizations on record for this archive.
197
     *
198
     * @return Synchronization[][]
199
     */
200
    public function buildSynchronizationHistory(): array
201
    {
202
        $return = [];
203
204
        foreach ($this->getVaults() as $vault)
205
        {
206
            $vaultConfig = $vault->getVaultConfiguration();
207
            $list = $vault->loadSynchronizationList();
208
209
            foreach ($list as $synchronization)
210
            {
211
                /** @var Synchronization $synchronization */
212
213
                $return[$synchronization->getRevision()][$vaultConfig->getTitle()] = $synchronization;
214
            }
215
        }
216
217
        ksort($return);
218
219
        return $return;
220
    }
221
222
    /**
223
     * @param Vault[] $vaults
224
     * @param string $lockName
225
     */
226
    protected function acquireLocks(array $vaults, string $lockName)
227
    {
228
        foreach ($vaults as $vault)
229
        {
230
            if (!$vault->getLockAdapter()->acquireLock($lockName))
231
            {
232
                throw new Exception("Failed to acquire lock for vault {$vault->getVaultConfiguration()->getTitle()}");
233
            }
234
        }
235
    }
236
237
    /**
238
     * @param Vault[] $vaults
239
     * @param string $lockName
240
     */
241
    protected function releaseLocks(array $vaults, string $lockName)
242
    {
243
        foreach ($vaults as $vault)
244
        {
245
            if (!$vault->getLockAdapter()->releaseLock($lockName))
246
            {
247
                throw new Exception("Failed to release lock for vault {$vault->getVaultConfiguration()->getTitle()}");
248
            }
249
        }
250
    }
251
252
    protected function getVaultForDownload(?int $revision, ?string $vaultTitle): ?Vault
253
    {
254
        $revision = $revision ?: $this->getLastRevision();
255
        if ($revision === null)
256
        {
257
            return null;
258
        }
259
260
        if ($vaultTitle)
261
        {
262
            $vault = $this->getVault($vaultTitle);
263
        }
264
        else
265
        {
266
            $vaults = $this->getVaultsHavingRevision($revision);
267
            $vault = $this->getPrioritizedVault($vaults);
268
        }
269
270
        if ($vault === null)
271
        {
272
            throw new Exception("Cannot find requested revision #{$revision} in any configured vault.");
273
        }
274
275
        return $vault;
276
    }
277
}
278