Completed
Push — master ( 2cde75...6c52d6 )
by Arne
02:04
created

Storeman::synchronize()   C

Complexity

Conditions 7
Paths 40

Size

Total Lines 42
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 42
rs 6.7272
c 0
b 0
f 0
cc 7
eloc 16
nc 40
nop 2
1
<?php
2
3
namespace Storeman;
4
5
use Storeman\Exception\ConfigurationException;
6
use Storeman\SynchronizationProgressListener\SynchronizationProgressListenerInterface;
7
8
/**
9
 * This class coordinates executions with multiple vaults involved.
10
 */
11
class Storeman
12
{
13
    /**
14
     * @var Configuration
15
     */
16
    protected $configuration;
17
18
    /**
19
     * @var Container
20
     */
21
    protected $container;
22
23
    /**
24
     * @var Vault[]
25
     */
26
    protected $vaults = [];
27
28
    public function __construct(Configuration $configuration, Container $container = null)
29
    {
30
        $this->configuration = $configuration;
31
        $this->container = $container ?: new Container();
32
    }
33
34
    public function getConfiguration(): Configuration
35
    {
36
        return $this->configuration;
37
    }
38
39
    /**
40
     * Returns the DI container of this storeman instance.
41
     * Some service names resolve to different implementations depending on the current vault which can be set as a context.
42
     * E.g. "storageAdapter" resolves to the storage adapter implementation configured for the current vault.
43
     *
44
     * @param Vault $vault
45
     * @return Container
46
     */
47
    public function getContainer(Vault $vault = null): Container
48
    {
49
        return $this->container->selectVault($vault);
50
    }
51
52
    /**
53
     * Returns a specific vault by title.
54
     *
55
     * @param string $vaultTitle
56
     * @return Vault
57
     */
58
    public function getVault(string $vaultTitle): Vault
59
    {
60
        if (!isset($this->vaults[$vaultTitle]))
61
        {
62
            $vaultConfiguration = $this->getConfiguration()->getVaultByTitle($vaultTitle);
63
64
            $this->vaults[$vaultTitle] = new Vault($this, $vaultConfiguration);
65
        }
66
67
        return $this->vaults[$vaultTitle];
68
    }
69
70
    /**
71
     * Returns all vaults.
72
     *
73
     * @return Vault[]
74
     */
75
    public function getVaults(): array
76
    {
77
        return array_values(array_map(function(VaultConfiguration $vaultConfiguration) {
78
79
            return $this->getVault($vaultConfiguration->getTitle());
80
81
        }, $this->configuration->getVaults()));
82
    }
83
84
    /**
85
     * Builds and returns an OperationList instance containing all operations required to a full synchronization.
86
     *
87
     * @return OperationList
88
     */
89
    public function buildOperationList(): OperationList
90
    {
91
        $return = new OperationList();
92
93
        foreach ($this->getVaults() as $vault)
94
        {
95
            $return->append($vault->getOperationList());
96
        }
97
98
        return $return;
99
    }
100
101
    public function synchronize(array $vaultTitles = [], SynchronizationProgressListenerInterface $progressListener = null): OperationResultList
102
    {
103
        $lastRevision = 0;
104
105
        // fallback to all vaults
106
        $vaultTitles = $vaultTitles ?: array_map(function(Vault $vault) { return $vault->getVaultConfiguration()->getTitle(); }, $this->getVaults());
107
108
        // acquire all locks and retrieve highest existing revision
109
        foreach ($this->getVaults() as $vault)
110
        {
111
            // lock is only required for vaults that we want to synchronize with
112
            if (in_array($vault->getVaultConfiguration()->getTitle(), $vaultTitles))
113
            {
114
                $vault->getLockAdapter()->acquireLock(Vault::LOCK_SYNC);
115
            }
116
117
            // highest revision should be build across all vaults
118
            $synchronizationList = $vault->loadSynchronizationList();
119
            if ($synchronizationList->getLastSynchronization())
120
            {
121
                $lastRevision = max($lastRevision, $synchronizationList->getLastSynchronization()->getRevision());
122
            }
123
        }
124
125
        // new revision is one plus the highest existing revision across all vaults
126
        $newRevision = $lastRevision + 1;
127
128
        // actual synchronization
129
        $return = new OperationResultList();
130
        foreach ($vaultTitles as $vaultTitle)
131
        {
132
            $return->append($this->getVault($vaultTitle)->synchronize($newRevision, $progressListener));
133
        }
134
135
        // release lock at the last moment to further reduce change of deadlocks
136
        foreach ($vaultTitles as $vaultTitle)
137
        {
138
            $this->getVault($vaultTitle)->getLockAdapter()->releaseLock(Vault::LOCK_SYNC);
139
        }
140
141
        return $return;
142
    }
143
144 View Code Duplication
    public function restore(int $toRevision = null, string $fromVault = null, SynchronizationProgressListenerInterface $progressListener = null): OperationResultList
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
145
    {
146
        $vault = $fromVault ? $this->getVault($fromVault) : $this->getAnyVault();
147
        $lockAdapter = $vault->getLockAdapter();
148
149
        $lockAdapter->acquireLock(Vault::LOCK_SYNC);
150
151
        $operationResultList = $vault->restore($toRevision, $progressListener);
152
153
        $lockAdapter->releaseLock(Vault::LOCK_SYNC);
154
155
        return $operationResultList;
156
    }
157
158 View Code Duplication
    public function dump(string $targetPath, int $revision = null, string $fromVault = null, SynchronizationProgressListenerInterface $progressListener = null): OperationResultList
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
159
    {
160
        $vault = $fromVault ? $this->getVault($fromVault) : $this->getAnyVault();
161
        $lockAdapter = $vault->getLockAdapter();
162
163
        $lockAdapter->acquireLock(Vault::LOCK_SYNC);
164
165
        $operationResultList = $vault->dump($targetPath, $revision, $progressListener);
166
167
        $lockAdapter->releaseLock(Vault::LOCK_SYNC);
168
169
        return $operationResultList;
170
    }
171
172
    /**
173
     * Builds and returns a history of all synchronizations on record for this archive.
174
     *
175
     * @return Synchronization[][]
176
     */
177
    public function buildSynchronizationHistory(): array
178
    {
179
        $return = [];
180
181
        foreach ($this->getVaults() as $vault)
182
        {
183
            $vaultConfig = $vault->getVaultConfiguration();
184
            $list = $vault->loadSynchronizationList();
185
186
            foreach ($list as $synchronization)
187
            {
188
                /** @var Synchronization $synchronization */
189
190
                $return[$synchronization->getRevision()][$vaultConfig->getTitle()] = $synchronization;
191
            }
192
        }
193
194
        ksort($return);
195
196
        return $return;
197
    }
198
199
    protected function getAnyVault(): Vault
200
    {
201
        $vaults = array_values($this->getVaults());
202
203
        if (empty($vaults))
204
        {
205
            throw new ConfigurationException('No vaults defined.');
206
        }
207
208
        return $vaults[0];
209
    }
210
}
211