Completed
Push — master ( 32e422...65570c )
by Arne
04:03
created

Storeman::synchronize()   A

Complexity

Conditions 4
Paths 8

Size

Total Lines 19
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 19
rs 9.2
c 0
b 0
f 0
cc 4
eloc 9
nc 8
nop 2
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
    /**
25
     * Returns the DI container of this storeman instance.
26
     * Some service names resolve to different implementations depending on the current vault which can be set as a context.
27
     * E.g. "storageAdapter" resolves to the storage adapter implementation configured for the current vault.
28
     *
29
     * @param Vault $vault
30
     * @return Container
31
     */
32
    public function getContainer(Vault $vault = null): Container
33
    {
34
        return $this->container->selectVault($vault);
35
    }
36
37
    /**
38
     * Returns the configuration.
39
     *
40
     * @return Configuration
41
     */
42
    public function getConfiguration(): Configuration
43
    {
44
        return $this->container->get('configuration');
45
    }
46
47
    /**
48
     * Returns a container of the configured vaults.
49
     *
50
     * @return VaultContainer
51
     */
52
    public function getVaultContainer(): VaultContainer
53
    {
54
        return $this->container->getVaultContainer();
55
    }
56
57
    public function synchronize(array $vaultTitles = null, SynchronizationProgressListenerInterface $progressListener = null): OperationResultList
58
    {
59
        /** @var Vault[] $vaults */
60
        $vaults = ($vaultTitles === null) ? $this->getVaultContainer() : $this->getVaultContainer()->getVaultsByTitles($vaultTitles);
61
62
        $this->acquireLocks($vaults, Vault::LOCK_SYNC);
0 ignored issues
show
Documentation introduced by
$vaults is of type array<integer,object<Storeman\Vault>>, but the function expects a object<Traversable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
63
64
        $newRevision = ($this->getLastRevision() ?: 0) + 1;
65
66
        $return = new OperationResultList();
67
        foreach ($vaults as $vault)
68
        {
69
            $return->append($vault->synchronize($newRevision, $progressListener));
70
        }
71
72
        $this->releaseLocks($vaults, Vault::LOCK_SYNC);
0 ignored issues
show
Documentation introduced by
$vaults is of type array<integer,object<Storeman\Vault>>, but the function expects a object<Traversable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
73
74
        return $return;
75
    }
76
77
    public function restore(int $toRevision = null, string $fromVault = null, SynchronizationProgressListenerInterface $progressListener = null): OperationResultList
78
    {
79
        $vault = $this->getVaultForDownload($toRevision, $fromVault);
80
81
        if ($vault === null)
82
        {
83
            return new OperationResultList();
84
        }
85
86
        $operationResultList = $vault->restore($toRevision, $progressListener);
87
88
        return $operationResultList;
89
    }
90
91
    public function dump(string $targetPath, int $revision = null, string $fromVault = null, SynchronizationProgressListenerInterface $progressListener = null): OperationResultList
92
    {
93
        $vault = $this->getVaultForDownload($revision, $fromVault);
94
95
        if ($vault === null)
96
        {
97
            return new OperationResultList();
98
        }
99
100
        $operationResultList = $vault->dump($targetPath, $revision, $progressListener);
101
102
        return $operationResultList;
103
    }
104
105
    /**
106
     * Returns the highest revision number across all vaults.
107
     *
108
     * @return int
109
     */
110
    public function getLastRevision(): ?int
111
    {
112
        $max = 0;
113
114
        foreach ($this->getVaultContainer() as $vault)
115
        {
116
            /** @var Vault $vault */
117
118
            if ($lastSynchronization = $vault->getVaultLayout()->getLastSynchronization())
119
            {
120
                $max = max($max, $lastSynchronization->getRevision());
121
            }
122
        }
123
124
        return $max ?: null;
125
    }
126
127
    /**
128
     * Builds and returns a history of all synchronizations on record for this archive.
129
     *
130
     * @return Synchronization[][]
131
     */
132
    public function buildSynchronizationHistory(): array
133
    {
134
        $return = [];
135
136
        foreach ($this->getVaultContainer() as $vault)
137
        {
138
            /** @var Vault $vault */
139
140
            $vaultConfig = $vault->getVaultConfiguration();
141
            $list = $vault->loadSynchronizationList();
142
143
            foreach ($list as $synchronization)
144
            {
145
                /** @var Synchronization $synchronization */
146
147
                $return[$synchronization->getRevision()][$vaultConfig->getTitle()] = $synchronization;
148
            }
149
        }
150
151
        ksort($return);
152
153
        return $return;
154
    }
155
156
    /**
157
     * @param Vault[] $vaults
158
     * @param string $lockName
159
     */
160
    protected function acquireLocks(\Traversable $vaults, string $lockName)
161
    {
162
        foreach ($vaults as $vault)
163
        {
164
            if (!$vault->getLockAdapter()->acquireLock($lockName))
165
            {
166
                throw new Exception("Failed to acquire lock for vault {$vault->getVaultConfiguration()->getTitle()}");
167
            }
168
        }
169
    }
170
171
    /**
172
     * @param Vault[] $vaults
173
     * @param string $lockName
174
     */
175
    protected function releaseLocks(\Traversable $vaults, string $lockName)
176
    {
177
        foreach ($vaults as $vault)
178
        {
179
            if (!$vault->getLockAdapter()->releaseLock($lockName))
180
            {
181
                throw new Exception("Failed to release lock for vault {$vault->getVaultConfiguration()->getTitle()}");
182
            }
183
        }
184
    }
185
186
    protected function getVaultForDownload(?int $revision, ?string $vaultTitle): ?Vault
187
    {
188
        $revision = $revision ?: $this->getLastRevision();
189
        if ($revision === null)
190
        {
191
            return null;
192
        }
193
194
        $vaultContainer = $this->getVaultContainer();
195
196
        if ($vaultTitle)
197
        {
198
            $vault = $vaultContainer->getVaultByTitle($vaultTitle);
199
        }
200
        else
201
        {
202
            $vaults = $vaultContainer->getVaultsHavingRevision($revision);
203
            $vault = $vaultContainer->getPrioritizedVault($vaults);
204
        }
205
206
        if ($vault === null)
207
        {
208
            throw new Exception("Cannot find requested revision #{$revision} in any configured vault.");
209
        }
210
211
        return $vault;
212
    }
213
}
214