Completed
Push — master ( b53e98...bc9dc6 )
by Tobias
06:07
created

StorageService::delete()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 6
ccs 0
cts 6
cp 0
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 3
nc 2
nop 3
crap 6
1
<?php
2
3
/*
4
 * This file is part of the PHP Translation package.
5
 *
6
 * (c) PHP Translation team <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Translation\Bundle\Service;
13
14
use Symfony\Component\Translation\MessageCatalogue;
15
use Translation\Bundle\Catalogue\CatalogueFetcher;
16
use Translation\Bundle\Catalogue\CatalogueWriter;
17
use Translation\Bundle\Model\Configuration;
18
use Translation\Common\Exception\LogicException;
19
use Translation\Common\Model\Message;
20
use Translation\Common\Storage;
21
use Translation\Common\TransferableStorage;
22
23
/**
24
 * A service that you use to handle the storages.
25
 *
26
 * @author Tobias Nyholm <[email protected]>
27
 */
28
final class StorageService implements Storage
29
{
30
    const DIRECTION_UP = 'up';
31
    const DIRECTION_DOWN = 'down';
32
33
    /**
34
     * @var Storage[]
35
     */
36
    private $localStorages = [];
37
38
    /**
39
     * @var Storage[]
40
     */
41
    private $remoteStorages = [];
42
43
    /**
44
     * @var CatalogueFetcher
45
     */
46
    private $catalogueFetcher;
47
48
    /**
49
     * @var CatalogueWriter
50
     */
51
    private $catalogueWriter;
52
53
    /**
54
     * @var Configuration
55
     */
56
    private $config;
57
58
    /**
59
     * @param CatalogueFetcher $catalogueFetcher
60
     * @param CatalogueWriter  $catalogueWriter
61
     * @param Configuration    $config
62
     */
63
    public function __construct(
64
        CatalogueFetcher $catalogueFetcher,
65
        CatalogueWriter $catalogueWriter,
66
        Configuration $config
67
    ) {
68
        $this->catalogueFetcher = $catalogueFetcher;
69
        $this->catalogueWriter = $catalogueWriter;
70
        $this->config = $config;
71
    }
72
73
    /**
74
     * Download all remote storages into all local storages.
75
     * This will overwrite your local copy.
76
     */
77
    public function download()
78
    {
79
        $catalogues = $this->doDownload();
80
81
        $this->catalogueWriter->writeCatalogues($this->config, $catalogues);
82
    }
83
84
    /**
85
     * Synchronize translations with remote.
86
     */
87
    public function sync($direction = self::DIRECTION_DOWN)
88
    {
89
        switch ($direction) {
90
            case self::DIRECTION_DOWN:
91
                $this->mergeDown();
92
                $this->mergeUp();
93
                break;
94
            case self::DIRECTION_UP:
95
                $this->mergeUp();
96
                $this->mergeDown();
97
                break;
98
            default:
99
                throw new LogicException(sprintf('Direction must be either "up" or "down". Value "%s" was provided', $direction));
100
        }
101
    }
102
103
    /**
104
     * Download and merge all translations from remote storages down to your local storages.
105
     * Only the local storages will be changed.
106
     */
107
    public function mergeDown()
108
    {
109
        $catalogues = $this->doDownload();
110
111
        foreach ($catalogues as $locale => $catalogue) {
112
            foreach ($catalogue->all() as $domain => $messages) {
113
                foreach ($messages as $key => $translation) {
114
                    $message = new Message($key, $domain, $locale, $translation);
115
                    $this->updateStorages($this->localStorages, $message);
116
                }
117
            }
118
        }
119
    }
120
121
    /**
122
     * Upload and merge all translations from local storages up to your remote storages.
123
     * Only the remote storages will be changed.
124
     *
125
     * This will overwrite your remote copy.
126
     */
127
    public function mergeUp()
128
    {
129
        $catalogues = $this->catalogueFetcher->getCatalogues($this->config);
130
        foreach ($catalogues as $catalogue) {
131
            foreach ($this->remoteStorages as $storage) {
132
                if ($storage instanceof TransferableStorage) {
0 ignored issues
show
Bug introduced by
The class Translation\Common\TransferableStorage does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
133
                    $storage->import($catalogue);
134
                }
135
            }
136
        }
137
    }
138
139
    /**
140
     * Get the very latest version we know of a message. First look at the remote storage
141
     * fall back on the local ones.
142
     *
143
     * @param string $locale
144
     * @param string $domain
145
     * @param string $key
146
     *
147
     * @return null|Message
148
     */
149
    public function syncAndFetchMessage($locale, $domain, $key)
150
    {
151
        $message = $this->getFromStorages($this->remoteStorages, $locale, $domain, $key);
152
        if (!$message) {
153
            // If message is not in remote storages
154
            $message = $this->getFromStorages($this->localStorages, $locale, $domain, $key);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $message is correct as $this->getFromStorages($...$locale, $domain, $key) (which targets Translation\Bundle\Servi...vice::getFromStorages()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
155
        }
156
157
        $this->updateStorages($this->localStorages, $message);
0 ignored issues
show
Bug introduced by
It seems like $message can be null; however, updateStorages() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
158
159
        return $message;
160
    }
161
162
    /**
163
     * Try to get a translation from all the storages, start looking in the first
164
     * local storage and then move on to the remote storages.
165
     * {@inheritdoc}
166
     */
167
    public function get($locale, $domain, $key)
168
    {
169
        foreach ([$this->localStorages, $this->remoteStorages] as $storages) {
170
            $value = $this->getFromStorages($storages, $locale, $domain, $key);
171
            if (!empty($value)) {
172
                return $value;
173
            }
174
        }
175
176
        return;
177
    }
178
179
    /**
180
     * @param Storage[] $storages
181
     * @param string    $locale
182
     * @param string    $domain
183
     * @param string    $key
184
     *
185
     * @return null|Message
186
     */
187
    private function getFromStorages($storages, $locale, $domain, $key)
188
    {
189
        foreach ($storages as $storage) {
190
            $value = $storage->get($locale, $domain, $key);
191
            if (!empty($value)) {
192
                return $value;
193
            }
194
        }
195
196
        return;
197
    }
198
199
    /**
200
     * Create all configured storages with this message. This will not overwrite
201
     * existing message.
202
     *
203
     * {@inheritdoc}
204
     */
205
    public function create(Message $message)
206
    {
207
        foreach ([$this->localStorages, $this->remoteStorages] as $storages) {
208
            $this->createStorages($storages, $message);
209
        }
210
    }
211
212
    /**
213
     * @param Storage[] $storages
214
     * @param Message   $message
215
     */
216
    private function createStorages($storages, Message $message)
217
    {
218
        // Validate if message actually has data
219
        if (empty((array) $message)) {
220
            return;
221
        }
222
223
        foreach ($storages as $storage) {
224
            $storage->update($message);
225
        }
226
    }
227
228
    /**
229
     * Update all configured storages with this message. If messages does not exist
230
     * it will be created.
231
     *
232
     * {@inheritdoc}
233
     */
234
    public function update(Message $message)
235
    {
236
        foreach ([$this->localStorages, $this->remoteStorages] as $storages) {
237
            $this->updateStorages($storages, $message);
238
        }
239
    }
240
241
    /**
242
     * @param Storage[] $storages
243
     * @param Message   $message
244
     */
245
    private function updateStorages($storages, Message $message)
246
    {
247
        // Validate if message actually has data
248
        if (empty((array) $message)) {
249
            return;
250
        }
251
252
        foreach ($storages as $storage) {
253
            $storage->update($message);
254
        }
255
    }
256
257
    /**
258
     * Delete the message form all storages.
259
     *
260
     * {@inheritdoc}
261
     */
262
    public function delete($locale, $domain, $key)
263
    {
264
        foreach ([$this->localStorages, $this->remoteStorages] as $storages) {
265
            $this->deleteFromStorages($storages, $locale, $domain, $key);
266
        }
267
    }
268
269
    /**
270
     * @param Storage[] $storages
271
     * @param string    $locale
272
     * @param string    $domain
273
     * @param string    $key
274
     */
275
    private function deleteFromStorages($storages, $locale, $domain, $key)
276
    {
277
        foreach ($storages as $storage) {
278
            $storage->delete($locale, $domain, $key);
279
        }
280
    }
281
282
    /**
283
     * @param Storage $localStorage
284
     *
285
     * @return StorageService
286
     */
287
    public function addLocalStorage(Storage $localStorage)
288
    {
289
        $this->localStorages[] = $localStorage;
290
291
        return $this;
292
    }
293
294
    /**
295
     * @param Storage $remoteStorages
0 ignored issues
show
Documentation introduced by
There is no parameter named $remoteStorages. Did you maybe mean $remoteStorage?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
296
     *
297
     * @return StorageService
298
     */
299
    public function addRemoteStorage(Storage $remoteStorage)
300
    {
301
        $this->remoteStorages[] = $remoteStorage;
302
303
        return $this;
304
    }
305
306
    /**
307
     * Download catalogues from all storages.
308
     *
309
     * @return MessageCatalogue[]
310
     */
311
    private function doDownload()
312
    {
313
        $catalogues = [];
314
        foreach ($this->config->getLocales() as $locale) {
315
            $catalogues[$locale] = new MessageCatalogue($locale);
316
            foreach ($this->remoteStorages as $storage) {
317
                if ($storage instanceof TransferableStorage) {
0 ignored issues
show
Bug introduced by
The class Translation\Common\TransferableStorage does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
318
                    $storage->export($catalogues[$locale]);
319
                }
320
            }
321
        }
322
323
        return $catalogues;
324
    }
325
}
326