Completed
Push — master ( c8c410...b53e98 )
by Tobias
05:48
created

StorageService::update()   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 1
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 = [];
80
        foreach ($this->config->getLocales() as $locale) {
81
            $catalogues[$locale] = new MessageCatalogue($locale);
82
            foreach ($this->remoteStorages as $storage) {
83
                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...
84
                    $storage->export($catalogues[$locale]);
85
                }
86
            }
87
        }
88
89
        $this->catalogueWriter->writeCatalogues($this->config, $catalogues);
90
    }
91
92
    /**
93
     * Upload all local storages into all remote storages
94
     * This will overwrite your remote copy.
95
     */
96
    public function upload()
97
    {
98
        $catalogues = $this->catalogueFetcher->getCatalogues($this->config);
99
        foreach ($catalogues as $catalogue) {
100
            foreach ($this->remoteStorages as $storage) {
101
                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...
102
                    $storage->import($catalogue);
103
                }
104
            }
105
        }
106
    }
107
108
    /**
109
     * Synchronize translations with remote.
110
     */
111
    public function sync($direction = self::DIRECTION_DOWN)
112
    {
113
        switch ($direction) {
114
            case self::DIRECTION_DOWN:
115
                $this->mergeDown();
116
                $this->mergeUp();
117
                break;
118
            case self::DIRECTION_UP:
119
                $this->mergeUp();
120
                $this->mergeDown();
121
                break;
122
            default:
123
                throw new LogicException(sprintf('Direction must be either "up" or "down". Value "%s" was provided', $direction));
124
        }
125
    }
126
127
    /**
128
     * Download and merge all translations from remote storages down to your local storages.
129
     * Only the local storages will be changed.
130
     */
131
    public function mergeDown()
132
    {
133
        // TODO
134
    }
135
136
    /**
137
     * Upload and merge all translations from local storages up to your remote storages.
138
     * Only the remote storages will be changed.
139
     */
140
    public function mergeUp()
141
    {
142
        // TODO
143
    }
144
145
    /**
146
     * Get the very latest version we know of a message. First look at the remote storage
147
     * fall back on the local ones.
148
     *
149
     * @param string $locale
150
     * @param string $domain
151
     * @param string $key
152
     *
153
     * @return null|Message
154
     */
155
    public function syncAndFetchMessage($locale, $domain, $key)
156
    {
157
        $message = $this->getFromStorages($this->remoteStorages, $locale, $domain, $key);
158
        if (!$message) {
159
            // If message is not in remote storages
160
            $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...
161
        }
162
163
        $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...
164
165
        return $message;
166
    }
167
168
    /**
169
     * Try to get a translation from all the storages, start looking in the first
170
     * local storage and then move on to the remote storages.
171
     * {@inheritdoc}
172
     */
173
    public function get($locale, $domain, $key)
174
    {
175
        foreach ([$this->localStorages, $this->remoteStorages] as $storages) {
176
            $value = $this->getFromStorages($storages, $locale, $domain, $key);
177
            if (!empty($value)) {
178
                return $value;
179
            }
180
        }
181
182
        return;
183
    }
184
185
    /**
186
     * @param Storage[] $storages
187
     * @param string    $locale
188
     * @param string    $domain
189
     * @param string    $key
190
     *
191
     * @return null|Message
192
     */
193
    private function getFromStorages($storages, $locale, $domain, $key)
194
    {
195
        foreach ($storages as $storage) {
196
            $value = $storage->get($locale, $domain, $key);
197
            if (!empty($value)) {
198
                return $value;
199
            }
200
        }
201
202
        return;
203
    }
204
205
    /**
206
     * Create all configured storages with this message. This will not overwrite
207
     * existing message.
208
     *
209
     * {@inheritdoc}
210
     */
211
    public function create(Message $message)
212
    {
213
        foreach ([$this->localStorages, $this->remoteStorages] as $storages) {
214
            $this->createStorages($storages, $message);
215
        }
216
    }
217
218
    /**
219
     * @param Storage[] $storages
220
     * @param Message   $message
221
     */
222
    private function createStorages($storages, Message $message)
223
    {
224
        // Validate if message actually has data
225
        if (empty((array) $message)) {
226
            return;
227
        }
228
229
        foreach ($storages as $storage) {
230
            $storage->update($message);
231
        }
232
    }
233
234
    /**
235
     * Update all configured storages with this message. If messages does not exist
236
     * it will be created.
237
     *
238
     * {@inheritdoc}
239
     */
240
    public function update(Message $message)
241
    {
242
        foreach ([$this->localStorages, $this->remoteStorages] as $storages) {
243
            $this->updateStorages($storages, $message);
244
        }
245
    }
246
247
    /**
248
     * @param Storage[] $storages
249
     * @param Message   $message
250
     */
251
    private function updateStorages($storages, Message $message)
252
    {
253
        // Validate if message actually has data
254
        if (empty((array) $message)) {
255
            return;
256
        }
257
258
        foreach ($storages as $storage) {
259
            $storage->update($message);
260
        }
261
    }
262
263
    /**
264
     * Delete the message form all storages.
265
     *
266
     * {@inheritdoc}
267
     */
268
    public function delete($locale, $domain, $key)
269
    {
270
        foreach ([$this->localStorages, $this->remoteStorages] as $storages) {
271
            $this->deleteFromStorages($storages, $locale, $domain, $key);
272
        }
273
    }
274
275
    /**
276
     * @param Storage[] $storages
277
     * @param string    $locale
278
     * @param string    $domain
279
     * @param string    $key
280
     */
281
    private function deleteFromStorages($storages, $locale, $domain, $key)
282
    {
283
        foreach ($storages as $storage) {
284
            $storage->delete($locale, $domain, $key);
285
        }
286
    }
287
288
    /**
289
     * @param Storage $localStorage
290
     *
291
     * @return StorageService
292
     */
293
    public function addLocalStorage(Storage $localStorage)
294
    {
295
        $this->localStorages[] = $localStorage;
296
297
        return $this;
298
    }
299
300
    /**
301
     * @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...
302
     *
303
     * @return StorageService
304
     */
305
    public function addRemoteStorage(Storage $remoteStorage)
306
    {
307
        $this->remoteStorages[] = $remoteStorage;
308
309
        return $this;
310
    }
311
}
312