Completed
Push — master ( a8c5d7...b64b96 )
by Arne
02:19
created

Storeman::getLogger()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
3
namespace Storeman;
4
5
use Psr\Log\LoggerInterface;
6
use Storeman\Config\Configuration;
7
use Storeman\Index\Index;
8
use Storeman\IndexBuilder\IndexBuilderInterface;
9
use Storeman\SynchronizationProgressListener\SynchronizationProgressListenerInterface;
10
11
/**
12
 * This class coordinates executions with multiple vaults involved.
13
 */
14
class Storeman
15
{
16
    public const CONFIG_FILE_NAME = 'storeman.json';
17
    public const METADATA_DIRECTORY_NAME = '.storeman';
18
19
    /**
20
     * @var Container
21
     */
22
    protected $container;
23
24
    public function __construct(Container $container = null)
25
    {
26
        $this->container = $container ?: new Container();
27
        $this->container->injectStoreman($this);
28
    }
29
30
    /**
31
     * Returns the DI container of this storeman instance.
32
     * Some service names resolve to different implementations depending on the current vault which can be set as a context.
33
     * E.g. "storageAdapter" resolves to the storage adapter implementation configured for the current vault.
34
     *
35
     * @param Vault $vault
36
     * @return Container
37
     */
38
    public function getContainer(Vault $vault = null): Container
39
    {
40
        return $this->container->selectVault($vault);
41
    }
42
43
    /**
44
     * Returns the configuration.
45
     *
46
     * @return Configuration
47
     */
48
    public function getConfiguration(): Configuration
49
    {
50
        return $this->container->get('configuration');
51
    }
52
53
    /**
54
     * Returns a container of the configured vaults.
55
     *
56
     * @return VaultContainer
57
     */
58
    public function getVaultContainer(): VaultContainer
59
    {
60
        return $this->container->getVaultContainer();
61
    }
62
63
    /**
64
     * Returns configured index builder.
65
     *
66
     * @return IndexBuilderInterface
67
     */
68
    public function getIndexBuilder(): IndexBuilderInterface
69
    {
70
        return $this->container->get('indexBuilder');
71
    }
72
73
    /**
74
     * @return FileReader
75
     */
76
    public function getFileReader(): FileReader
77
    {
78
        return $this->container->get('fileReader');
79
    }
80
81
    /**
82
     * Builds and returns an index representing the current local state.
83
     *
84
     * @param string $path
85
     * @return Index
86
     */
87
    public function getLocalIndex(string $path = null): Index
88
    {
89
        return $this->getIndexBuilder()->buildIndex(
90
            $path ?: $this->getConfiguration()->getPath(),
91
            $this->getLocalIndexExclusionPatterns()
92
        );
93
    }
94
95
    public function synchronize(array $vaultTitles = null, SynchronizationProgressListenerInterface $progressListener = null): OperationResultList
96
    {
97
        $this->getLogger()->notice(sprintf("Synchronizing to these vaults: %s", $vaultTitles ? implode(', ', $vaultTitles) : '-all-'));
98
99
        /** @var Vault[] $vaults */
100
        $vaults = ($vaultTitles === null) ? $this->getVaultContainer() : $this->getVaultContainer()->getVaultsByTitles($vaultTitles);
101
102
        $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...
103
104
        $newRevision = ($this->getLastRevision() ?: 0) + 1;
105
106
        $return = new OperationResultList();
107
        foreach ($vaults as $vault)
108
        {
109
            $return->append($vault->synchronize($newRevision, $progressListener));
110
        }
111
112
        $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...
113
114
        return $return;
115
    }
116
117
    public function restore(int $toRevision = null, string $fromVault = null, SynchronizationProgressListenerInterface $progressListener = null): OperationResultList
118
    {
119
        $this->getLogger()->notice(sprintf("Restoring from %s...", $toRevision ? "r{$toRevision}": 'latest revision'));
120
121
        $vault = $this->getVaultForDownload($toRevision, $fromVault);
122
123
        if ($vault === null)
124
        {
125
            return new OperationResultList();
126
        }
127
128
        $operationResultList = $vault->restore($toRevision, $progressListener);
129
130
        return $operationResultList;
131
    }
132
133
    public function dump(string $targetPath, int $revision = null, string $fromVault = null, SynchronizationProgressListenerInterface $progressListener = null): OperationResultList
134
    {
135
        $this->getLogger()->notice(sprintf("Dumping from %s to {$targetPath}...", $revision ? "r{$revision}": 'latest revision'));
136
137
        $vault = $this->getVaultForDownload($revision, $fromVault);
138
139
        if ($vault === null)
140
        {
141
            return new OperationResultList();
142
        }
143
144
        $operationResultList = $vault->dump($targetPath, $revision, $progressListener);
145
146
        return $operationResultList;
147
    }
148
149
    /**
150
     * Returns the highest revision number across all vaults.
151
     *
152
     * @return int
153
     */
154
    public function getLastRevision(): ?int
155
    {
156
        $this->getLogger()->debug("Determining max revision...");
157
158
        $max = 0;
159
160
        foreach ($this->getVaultContainer() as $vault)
161
        {
162
            /** @var Vault $vault */
163
164
            if ($lastSynchronization = $vault->getVaultLayout()->getLastSynchronization())
165
            {
166
                $this->getLogger()->debug("Vault {$vault->getIdentifier()} is at r{$lastSynchronization->getRevision()}");
167
168
                $max = max($max, $lastSynchronization->getRevision());
169
            }
170
            else
171
            {
172
                $this->getLogger()->debug("Vault {$vault->getIdentifier()} has no synchronizations yet");
173
            }
174
        }
175
176
        $this->getLogger()->info("Found max revision to be " . ($max ?: '-'));
177
178
        return $max ?: null;
179
    }
180
181
    public function getMetadataDirectoryPath(): string
182
    {
183
        return $this->initMetadataDirectory();
184
    }
185
186
    /**
187
     * @param Vault[] $vaults
188
     * @param string $lockName
189
     */
190
    protected function acquireLocks(\Traversable $vaults, string $lockName)
191
    {
192
        foreach ($vaults as $vault)
193
        {
194
            if (!$vault->getLockAdapter()->acquireLock($lockName))
195
            {
196
                throw new Exception("Failed to acquire lock for vault {$vault->getIdentifier()}");
197
            }
198
        }
199
    }
200
201
    /**
202
     * @param Vault[] $vaults
203
     * @param string $lockName
204
     */
205
    protected function releaseLocks(\Traversable $vaults, string $lockName)
206
    {
207
        foreach ($vaults as $vault)
208
        {
209
            if (!$vault->getLockAdapter()->releaseLock($lockName))
210
            {
211
                throw new Exception("Failed to release lock for vault {$vault->getIdentifier()}");
212
            }
213
        }
214
    }
215
216
    protected function getVaultForDownload(?int $revision, ?string $vaultTitle): ?Vault
217
    {
218
        $revision = $revision ?: $this->getLastRevision();
219
        if ($revision === null)
220
        {
221
            return null;
222
        }
223
224
        $vaultContainer = $this->getVaultContainer();
225
226
        if ($vaultTitle)
227
        {
228
            $vault = $vaultContainer->getVaultByTitle($vaultTitle);
229
        }
230
        else
231
        {
232
            $vaults = $vaultContainer->getVaultsHavingRevision($revision);
233
            $vault = $vaultContainer->getPrioritizedVault($vaults);
234
        }
235
236
        if ($vault === null)
237
        {
238
            throw new Exception("Cannot find requested revision #{$revision} in any configured vault.");
239
        }
240
241
        return $vault;
242
    }
243
244
    protected function initMetadataDirectory(): string
245
    {
246
        $path = $this->getConfiguration()->getPath() . static::METADATA_DIRECTORY_NAME;
247
248
        if (!is_dir($path))
249
        {
250
            if (!mkdir($path))
251
            {
252
                throw new Exception("mkdir() failed for {$path}");
253
            }
254
        }
255
256
        return "{$path}/";
257
    }
258
259
    /**
260
     * @return string[]
261
     */
262
    protected function getLocalIndexExclusionPatterns(): array
263
    {
264
        return array_merge($this->getConfiguration()->getExclude(), [
265
            static::CONFIG_FILE_NAME,
266
            static::METADATA_DIRECTORY_NAME,
267
        ]);
268
    }
269
270
    protected function getLogger(): LoggerInterface
271
    {
272
        return $this->container->getLogger();
273
    }
274
}
275