Completed
Push — master ( b2e536...a4e6dd )
by Arne
02:05
created

Storeman::getVaultsHavingRevision()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 7
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 3
nc 1
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
    public function synchronize(array $vaultTitles = null, SynchronizationProgressListenerInterface $progressListener = null): OperationResultList
105
    {
106
        $vaults = ($vaultTitles === null) ? $this->getVaults() : $this->getVaultsByTitle($vaultTitles);
107
108
        $this->acquireLocks($vaults, Vault::LOCK_SYNC);
109
110
        $newRevision = ($this->getLastRevision() ?: 0) + 1;
111
112
        $return = new OperationResultList();
113
        foreach ($vaults as $vault)
114
        {
115
            $return->append($vault->synchronize($newRevision, $progressListener));
116
        }
117
118
        $this->releaseLocks($vaults, Vault::LOCK_SYNC);
119
120
        return $return;
121
    }
122
123
    public function restore(int $toRevision = null, string $fromVault = null, SynchronizationProgressListenerInterface $progressListener = null): OperationResultList
124
    {
125
        $vault = $this->getVaultForDownload($toRevision, $fromVault);
126
127
        if ($vault === null)
128
        {
129
            return new OperationResultList();
130
        }
131
132
        $operationResultList = $vault->restore($toRevision, $progressListener);
133
134
        return $operationResultList;
135
    }
136
137
    public function dump(string $targetPath, int $revision = null, string $fromVault = null, SynchronizationProgressListenerInterface $progressListener = null): OperationResultList
138
    {
139
        $vault = $this->getVaultForDownload($revision, $fromVault);
140
141
        if ($vault === null)
142
        {
143
            return new OperationResultList();
144
        }
145
146
        $operationResultList = $vault->dump($targetPath, $revision, $progressListener);
147
148
        return $operationResultList;
149
    }
150
151
    /**
152
     * Returns the highest revision number across all vaults.
153
     *
154
     * @return int
155
     */
156
    public function getLastRevision(): ?int
157
    {
158
        $max = 0;
159
160
        foreach ($this->getVaults() as $vault)
161
        {
162
            if ($lastSynchronization = $vault->getVaultLayout()->getLastSynchronization())
163
            {
164
                $max = max($max, $lastSynchronization->getRevision());
165
            }
166
        }
167
168
        return $max ?: null;
169
    }
170
171
    /**
172
     * Builds and returns a history of all synchronizations on record for this archive.
173
     *
174
     * @return Synchronization[][]
175
     */
176
    public function buildSynchronizationHistory(): array
177
    {
178
        $return = [];
179
180
        foreach ($this->getVaults() as $vault)
181
        {
182
            $vaultConfig = $vault->getVaultConfiguration();
183
            $list = $vault->loadSynchronizationList();
184
185
            foreach ($list as $synchronization)
186
            {
187
                /** @var Synchronization $synchronization */
188
189
                $return[$synchronization->getRevision()][$vaultConfig->getTitle()] = $synchronization;
190
            }
191
        }
192
193
        ksort($return);
194
195
        return $return;
196
    }
197
198
    /**
199
     * @param Vault[] $vaults
200
     * @param string $lockName
201
     */
202
    protected function acquireLocks(array $vaults, string $lockName)
203
    {
204
        foreach ($vaults as $vault)
205
        {
206
            if (!$vault->getLockAdapter()->acquireLock($lockName))
207
            {
208
                throw new Exception("Failed to acquire lock for vault {$vault->getVaultConfiguration()->getTitle()}");
209
            }
210
        }
211
    }
212
213
    /**
214
     * @param Vault[] $vaults
215
     * @param string $lockName
216
     */
217
    protected function releaseLocks(array $vaults, string $lockName)
218
    {
219
        foreach ($vaults as $vault)
220
        {
221
            if (!$vault->getLockAdapter()->releaseLock($lockName))
222
            {
223
                throw new Exception("Failed to release lock for vault {$vault->getVaultConfiguration()->getTitle()}");
224
            }
225
        }
226
    }
227
228
    protected function getVaultForDownload(?int $revision, ?string $vaultTitle): ?Vault
229
    {
230
        $revision = $revision ?: $this->getLastRevision();
231
        if ($revision === null)
232
        {
233
            return null;
234
        }
235
236
        if ($vaultTitle)
237
        {
238
            $vault = $this->getVault($vaultTitle);
239
        }
240
        else
241
        {
242
            $vaults = $this->getVaultsHavingRevision($revision);
243
            $vault = reset($vaults) ?: null;
244
        }
245
246
        if ($vault === null)
247
        {
248
            throw new Exception("Cannot find requested revision #{$revision} in any configured vault.");
249
        }
250
251
        return $vault;
252
    }
253
}
254