|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
namespace Archivr; |
|
4
|
|
|
|
|
5
|
|
|
use Archivr\StorageDriver\StorageDriverFactory; |
|
6
|
|
|
use Archivr\Exception\ConfigurationException; |
|
7
|
|
|
use Archivr\LockAdapter\LockAdapterFactory; |
|
8
|
|
|
use Archivr\SynchronizationProgressListener\SynchronizationProgressListenerInterface; |
|
9
|
|
|
|
|
10
|
|
|
class ArchivR |
|
11
|
|
|
{ |
|
12
|
|
|
/** |
|
13
|
|
|
* @var Configuration |
|
14
|
|
|
*/ |
|
15
|
|
|
protected $configuration; |
|
16
|
|
|
|
|
17
|
|
|
/** |
|
18
|
|
|
* @var StorageDriverFactory |
|
19
|
|
|
*/ |
|
20
|
|
|
protected $storageDriverFactory; |
|
21
|
|
|
|
|
22
|
|
|
/** |
|
23
|
|
|
* @var LockAdapterFactory |
|
24
|
|
|
*/ |
|
25
|
|
|
protected $lockAdapterFactory; |
|
26
|
|
|
|
|
27
|
|
|
/** |
|
28
|
|
|
* @var Vault[] |
|
29
|
|
|
*/ |
|
30
|
|
|
protected $vaults = []; |
|
31
|
|
|
|
|
32
|
|
|
public function __construct(Configuration $configuration) |
|
33
|
|
|
{ |
|
34
|
|
|
$this->configuration = $configuration; |
|
35
|
|
|
} |
|
36
|
|
|
|
|
37
|
|
|
public function getConfiguration(): Configuration |
|
38
|
|
|
{ |
|
39
|
|
|
return $this->configuration; |
|
40
|
|
|
} |
|
41
|
|
|
|
|
42
|
|
|
public function getStorageDriverFactory(): StorageDriverFactory |
|
43
|
|
|
{ |
|
44
|
|
|
if ($this->storageDriverFactory === null) |
|
45
|
|
|
{ |
|
46
|
|
|
$this->setStorageDriverFactory(new StorageDriverFactory()); |
|
47
|
|
|
} |
|
48
|
|
|
|
|
49
|
|
|
return $this->storageDriverFactory; |
|
50
|
|
|
} |
|
51
|
|
|
|
|
52
|
|
|
public function setStorageDriverFactory(StorageDriverFactory $storageDriverFactory) |
|
53
|
|
|
{ |
|
54
|
|
|
$this->storageDriverFactory = $storageDriverFactory; |
|
55
|
|
|
} |
|
56
|
|
|
|
|
57
|
|
|
public function getLockAdapterFactory(): LockAdapterFactory |
|
58
|
|
|
{ |
|
59
|
|
|
if ($this->lockAdapterFactory === null) |
|
60
|
|
|
{ |
|
61
|
|
|
$this->setLockAdapterFactory(new LockAdapterFactory()); |
|
62
|
|
|
} |
|
63
|
|
|
|
|
64
|
|
|
return $this->lockAdapterFactory; |
|
65
|
|
|
} |
|
66
|
|
|
|
|
67
|
|
|
public function setLockAdapterFactory(LockAdapterFactory $lockAdapterFactory) |
|
68
|
|
|
{ |
|
69
|
|
|
$this->lockAdapterFactory = $lockAdapterFactory; |
|
70
|
|
|
} |
|
71
|
|
|
|
|
72
|
|
|
public function buildOperationList(): OperationList |
|
73
|
|
|
{ |
|
74
|
|
|
$return = new OperationList(); |
|
75
|
|
|
|
|
76
|
|
|
foreach ($this->getVaults() as $vault) |
|
77
|
|
|
{ |
|
78
|
|
|
$return->append($vault->getOperationList()); |
|
79
|
|
|
} |
|
80
|
|
|
|
|
81
|
|
|
return $return; |
|
82
|
|
|
} |
|
83
|
|
|
|
|
84
|
|
|
/** |
|
85
|
|
|
* @return Vault[] |
|
86
|
|
|
*/ |
|
87
|
|
|
public function getVaults(): array |
|
88
|
|
|
{ |
|
89
|
|
|
return array_map(function(VaultConfiguration $vaultConfiguration) { |
|
90
|
|
|
|
|
91
|
|
|
return $this->getVault($vaultConfiguration->getTitle()); |
|
92
|
|
|
|
|
93
|
|
|
}, $this->configuration->getVaultConfigurations()); |
|
94
|
|
|
} |
|
95
|
|
|
|
|
96
|
|
|
public function getVault(string $vaultTitle): Vault |
|
97
|
|
|
{ |
|
98
|
|
|
if (!isset($this->vaults[$vaultTitle])) |
|
99
|
|
|
{ |
|
100
|
|
|
$vaultConfiguration = $this->getConfiguration()->getVaultConfigurationByTitle($vaultTitle); |
|
101
|
|
|
|
|
102
|
|
|
$storageDriver = $this->getStorageDriverFactory()->create($vaultConfiguration->getVaultAdapter(), $vaultConfiguration); |
|
103
|
|
|
$lockAdapter = $this->getLockAdapterFactory()->create($vaultConfiguration->getLockAdapter(), $vaultConfiguration, $storageDriver); |
|
104
|
|
|
|
|
105
|
|
|
$vault = new Vault($vaultTitle, $this->configuration->getLocalPath(), $storageDriver); |
|
106
|
|
|
$vault->setLockAdapter($lockAdapter); |
|
107
|
|
|
$vault->setExclusions($this->configuration->getExclusions()); |
|
108
|
|
|
$vault->setIdentity($this->configuration->getIdentity()); |
|
109
|
|
|
|
|
110
|
|
|
$this->vaults[$vaultTitle] = $vault; |
|
111
|
|
|
} |
|
112
|
|
|
|
|
113
|
|
|
return $this->vaults[$vaultTitle]; |
|
114
|
|
|
} |
|
115
|
|
|
|
|
116
|
|
|
public function synchronize(array $vaultTitles = [], bool $preferLocal = false, SynchronizationProgressListenerInterface $progressListener = null): OperationResultList |
|
117
|
|
|
{ |
|
118
|
|
|
$lastRevision = 0; |
|
119
|
|
|
|
|
120
|
|
|
// fallback to all vaults |
|
121
|
|
|
$vaultTitles = $vaultTitles ?: array_map(function(Vault $vault) { return $vault->getTitle(); }, $this->getVaults()); |
|
122
|
|
|
|
|
123
|
|
|
// acquire all locks and retrieve highest existing revision |
|
124
|
|
|
foreach ($this->getVaults() as $vault) |
|
125
|
|
|
{ |
|
126
|
|
|
// lock is only required for vaults that we want to synchronize with |
|
127
|
|
|
if (in_array($vault->getTitle(), $vaultTitles)) |
|
128
|
|
|
{ |
|
129
|
|
|
$this->waitForLock($vault, Vault::LOCK_SYNC); |
|
130
|
|
|
} |
|
131
|
|
|
|
|
132
|
|
|
// highest revision should be build across all vaults |
|
133
|
|
|
$synchronizationList = $vault->loadSynchronizationList(); |
|
134
|
|
|
if ($synchronizationList->getLastSynchronization()) |
|
135
|
|
|
{ |
|
136
|
|
|
$lastRevision = max($lastRevision, $synchronizationList->getLastSynchronization()->getRevision()); |
|
137
|
|
|
} |
|
138
|
|
|
} |
|
139
|
|
|
|
|
140
|
|
|
// new revision is one plus the highest existing revision across all vaults |
|
141
|
|
|
$newRevision = $lastRevision + 1; |
|
142
|
|
|
|
|
143
|
|
|
// actual synchronization |
|
144
|
|
|
$return = new OperationResultList(); |
|
145
|
|
|
foreach ($vaultTitles as $vaultTitle) |
|
146
|
|
|
{ |
|
147
|
|
|
$return->append($this->getVault($vaultTitle)->synchronize($newRevision, $preferLocal, $progressListener)); |
|
148
|
|
|
} |
|
149
|
|
|
|
|
150
|
|
|
// release lock at the last moment to further reduce change of deadlocks |
|
151
|
|
|
foreach ($vaultTitles as $vaultTitle) |
|
152
|
|
|
{ |
|
153
|
|
|
$this->getVault($vaultTitle)->getLockAdapter()->releaseLock(Vault::LOCK_SYNC); |
|
154
|
|
|
} |
|
155
|
|
|
|
|
156
|
|
|
return $return; |
|
157
|
|
|
} |
|
158
|
|
|
|
|
159
|
|
|
/** |
|
160
|
|
|
* @return Synchronization[][] |
|
161
|
|
|
*/ |
|
162
|
|
|
public function buildSynchronizationHistory(): array |
|
163
|
|
|
{ |
|
164
|
|
|
$return = []; |
|
165
|
|
|
|
|
166
|
|
|
foreach ($this->getVaults() as $vault) |
|
167
|
|
|
{ |
|
168
|
|
|
$list = $vault->loadSynchronizationList(); |
|
169
|
|
|
|
|
170
|
|
|
foreach ($list as $synchronization) |
|
171
|
|
|
{ |
|
172
|
|
|
/** @var Synchronization $synchronization */ |
|
173
|
|
|
|
|
174
|
|
|
$return[$synchronization->getRevision()][$vault->getTitle()] = $synchronization; |
|
175
|
|
|
} |
|
176
|
|
|
} |
|
177
|
|
|
|
|
178
|
|
|
ksort($return); |
|
179
|
|
|
|
|
180
|
|
|
return $return; |
|
181
|
|
|
} |
|
182
|
|
|
|
|
183
|
|
View Code Duplication |
public function restore(int $toRevision = null, string $fromVault = null, SynchronizationProgressListenerInterface $progressListener = null): OperationResultList |
|
|
|
|
|
|
184
|
|
|
{ |
|
185
|
|
|
$vault = $fromVault ? $this->getVault($fromVault) : $this->getAnyVault(); |
|
186
|
|
|
|
|
187
|
|
|
$this->waitForLock($vault, Vault::LOCK_SYNC); |
|
188
|
|
|
|
|
189
|
|
|
$operationResultList = $vault->restore($toRevision, $progressListener); |
|
190
|
|
|
|
|
191
|
|
|
$vault->getLockAdapter()->releaseLock(Vault::LOCK_SYNC); |
|
192
|
|
|
|
|
193
|
|
|
return $operationResultList; |
|
194
|
|
|
} |
|
195
|
|
|
|
|
196
|
|
View Code Duplication |
public function dump(string $targetPath, int $revision = null, string $fromVault = null, SynchronizationProgressListenerInterface $progressListener = null): OperationResultList |
|
|
|
|
|
|
197
|
|
|
{ |
|
198
|
|
|
$vault = $fromVault ? $this->getVault($fromVault) : $this->getAnyVault(); |
|
199
|
|
|
|
|
200
|
|
|
$this->waitForLock($vault, Vault::LOCK_SYNC); |
|
201
|
|
|
|
|
202
|
|
|
$operationResultList = $vault->dump($targetPath, $revision, $progressListener); |
|
203
|
|
|
|
|
204
|
|
|
$vault->getLockAdapter()->releaseLock(Vault::LOCK_SYNC); |
|
205
|
|
|
|
|
206
|
|
|
return $operationResultList; |
|
207
|
|
|
} |
|
208
|
|
|
|
|
209
|
|
|
protected function getAnyVault(): Vault |
|
210
|
|
|
{ |
|
211
|
|
|
$vaults = array_values($this->getVaults()); |
|
212
|
|
|
|
|
213
|
|
|
if (empty($vaults)) |
|
214
|
|
|
{ |
|
215
|
|
|
throw new ConfigurationException('No vaults defined.'); |
|
216
|
|
|
} |
|
217
|
|
|
|
|
218
|
|
|
return $vaults[0]; |
|
219
|
|
|
} |
|
220
|
|
|
|
|
221
|
|
|
protected function waitForLock(Vault $vault, string $name) |
|
222
|
|
|
{ |
|
223
|
|
|
while (!$vault->getLockAdapter()->acquireLock($name)) |
|
224
|
|
|
{ |
|
225
|
|
|
sleep(5); |
|
226
|
|
|
} |
|
227
|
|
|
} |
|
228
|
|
|
} |
|
229
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.