Completed
Push — master ( 33b59c...9598cf )
by Avtandil
04:30
created

MultiLang::getText()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 1
Metric Value
c 2
b 0
f 1
dl 0
loc 5
rs 9.4285
cc 1
eloc 3
nc 1
nop 1
1
<?php
2
/*
3
 * This file is part of the Laravel MultiLang package.
4
 *
5
 * (c) Avtandil Kikabidze aka LONGMAN <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
11
namespace Longman\LaravelMultiLang;
12
13
use Illuminate\Contracts\Cache\Factory as CacheContract;
14
use Illuminate\Database\DatabaseManager as DatabaseContract;
15
use Illuminate\Support\Collection;
16
use InvalidArgumentException;
17
18
class MultiLang
19
{
20
    /**
21
     * Language/Locale.
22
     *
23
     * @var string
24
     */
25
    protected $lang;
26
27
    /**
28
     * System environment
29
     *
30
     * @var string
31
     */
32
    protected $environment;
33
34
    /**
35
     * The instance of the laravel app.
36
     *
37
     * @var \Illuminate\Foundation\Application
38
     */
39
    protected $app;
40
41
    /**
42
     * The instance of the cache.
43
     *
44
     * @var \Illuminate\Cache\CacheManager
45
     */
46
    protected $cache;
47
48
    /**
49
     * Config.
50
     *
51
     * @var array
52
     */
53
    protected $config;
54
55
    /**
56
     * The instance of the database.
57
     *
58
     * @var \Illuminate\Database\DatabaseManager
59
     */
60
    protected $db;
61
62
    /**
63
     * Name of the cache.
64
     *
65
     * @var string
66
     */
67
    protected $cache_name;
68
69
    /**
70
     * Texts collection.
71
     *
72
     * @var \Illuminate\Support\Collection
73
     */
74
    protected $texts;
75
76
    /**
77
     * Missing texts.
78
     *
79
     * @var array
80
     */
81
    protected $new_texts;
82
83
    /**
84
     * Create a new MultiLang instance.
85
     *
86
     * @param  string                               $environment
87
     * @param  array                                $config
88
     * @param  \Illuminate\Cache\CacheManager       $cache
89
     * @param  \Illuminate\Database\DatabaseManager $db
90
     * @return void
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
91
     */
92
    public function __construct($environment, array $config, CacheContract $cache, DatabaseContract $db)
93
    {
94
        $this->environment = $environment;
95
        $this->cache       = $cache;
0 ignored issues
show
Documentation Bug introduced by
$cache is of type object<Illuminate\Contracts\Cache\Factory>, but the property $cache was declared to be of type object<Illuminate\Cache\CacheManager>. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly.

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
96
        $this->db          = $db;
97
98
        $this->setConfig($config);
99
    }
100
101
    public function setConfig(array $config)
102
    {
103
        $this->config = [
104
            'enabled'        => true,
105
            'locales'        => [
106
                'en' => [
107
                    'name'        => 'English',
108
                    'native_name' => 'English',
109
                    'default'     => true,
110
                ],
111
            ],
112
            'autosave'       => true,
113
            'cache'          => true,
114
            'cache_lifetime' => 1440,
115
            'texts_table'    => 'texts',
116
        ];
117
118
        foreach ($config as $k => $v) {
119
            $this->config[$k] = $v;
120
        }
121
    }
122
123
    public function getConfig($key = null)
124
    {
125
        if ($key === null) {
126
            return $this->config;
127
        }
128
129
        return isset($this->config[$key]) ? $this->config[$key] : null;
130
    }
131
132
    /**
133
     * Set locale and load texts
134
     *
135
     * @param  string                               $lang
136
     * @param  \Illuminate\Support\Collection|array $text
0 ignored issues
show
Bug introduced by
There is no parameter named $text. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

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

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

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

Loading history...
137
     * @return void
138
     */
139
    public function setLocale($lang, $texts = null)
140
    {
141
        if (!$lang) {
142
            throw new InvalidArgumentException('Locale is empty!');
143
        }
144
        $this->lang = $lang;
145
146
        $this->setCacheName();
147
148
        if (is_array($texts)) {
149
            $texts = new Collection($texts);
150
        }
151
152
        $this->texts = $texts ? $texts : $this->loadTexts($this->getLocale());
153
    }
154
155
    /**
156
     * Load texts
157
     *
158
     * @param  string                           $lang
159
     * @return \Illuminate\Support\Collection
160
     */
161
    public function loadTexts($lang = null)
162
    {
163
        $cache = $this->getConfig('cache');
164
165
        if (!$cache || $this->cache === null || $this->environment != 'production') {
166
            $texts = $this->loadTextsFromDatabase($lang);
167
            return $texts;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $texts; (array) is incompatible with the return type documented by Longman\LaravelMultiLang\MultiLang::loadTexts of type Illuminate\Support\Collection.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
168
        }
169
170
        if ($this->mustLoadFromCache()) {
171
            $texts = $this->loadTextsFromCache();
172
        } else {
173
            $texts = $this->loadTextsFromDatabase($lang);
174
            $this->storeTextsInCache($texts);
175
        }
176
177
        $texts = new Collection($texts);
178
179
        return $texts;
180
    }
181
182
    /**
183
     * Get translated text
184
     *
185
     * @param  string   $key
186
     * @param  string   $default
0 ignored issues
show
Bug introduced by
There is no parameter named $default. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

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

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

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

Loading history...
187
     * @return string
188
     */
189
    public function get($key)
190
    {
191
192
        if (empty($key)) {
193
            return null;
194
        }
195
196
        if (!$this->lang) {
197
            return $key;
198
        }
199
200
        if (!$this->texts->has($key)) {
201
            $this->queueToSave($key);
202
            return $key;
203
        }
204
205
        $text = $this->texts->get($key);
206
207
        return $text;
208
    }
209
210
    /**
211
     * Get texts
212
     *
213
     * @param  string   $lang
214
     * @return string
215
     */
216
    public function getTexts($lang = null)
217
    {
218
219
        return $this->loadTexts($lang);
220
    }
221
222
    /**
223
     * Set texts manually
224
     *
225
     * @param  array                                 $texts_array
226
     * @return \Longman\LaravelMultiLang\MultiLang
227
     */
228
    public function setTexts(array $texts_array)
229
    {
230
        $texts = [];
231
        foreach ($texts_array as $key => $value) {
232
            $texts[$key] = $value;
233
        }
234
235
        $this->texts = new Collection($texts);
236
237
        return $this;
238
    }
239
240
    /**
241
     * Queue missing texts
242
     *
243
     * @param  string $key
244
     * @return void
245
     */
246
    protected function queueToSave($key)
247
    {
248
        $this->new_texts[$key] = $key;
249
    }
250
251
    /**
252
     * Check if we must load texts from cache
253
     *
254
     * @return bool
255
     */
256
    protected function mustLoadFromCache()
257
    {
258
        return $this->cache->has($this->cache_name);
259
    }
260
261
    protected function storeTextsInCache(array $texts)
262
    {
263
        $cache_lifetime = $this->getConfig('cache_lifetime', 1440);
0 ignored issues
show
Unused Code introduced by
The call to MultiLang::getConfig() has too many arguments starting with 1440.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
264
        $status         = $this->cache->put($this->cache_name, $texts, $cache_lifetime);
265
        return $status;
266
    }
267
268
    protected function loadTextsFromDatabase($lang)
269
    {
270
        $texts = $lang ? $this->db->table($this->getTableName())
271
            ->where('lang', $lang)
272
            ->get(['key', 'value', 'lang', 'scope']) : $this->db->table($this->getTableName())->get(['key', 'value', 'lang', 'scope']);
273
274
        $array = [];
275
        foreach ($texts as $row) {
276
            $array[$row->key] = $row->value;
277
        }
278
        return $array;
279
    }
280
281
    protected function loadTextsFromCache()
282
    {
283
        $texts = $this->cache->get($this->cache_name);
0 ignored issues
show
Bug introduced by
The method get() cannot be called from this context as it is declared protected in class Illuminate\Cache\CacheManager.

This check looks for access to methods that are not accessible from the current context.

If you need to make a method accessible to another context you can raise its visibility level in the defining class.

Loading history...
284
        return $texts;
285
    }
286
287
    protected function setCacheName()
288
    {
289
        $this->cache_name = 'texts.' . $this->lang;
290
    }
291
292
    public function getUrl($path)
293
    {
294
        $locale = $this->getLocale();
295
        if ($locale) {
296
            $path = $locale . '/' . $path;
297
        }
298
        return $path;
299
    }
300
301
    public function autoSaveIsAllowed()
302
    {
303
        if ($this->environment == 'local' && $this->getConfig('autosave') && $this->db !== null) {
304
            return true;
305
        }
306
        return false;
307
    }
308
309
    public function getLocale()
310
    {
311
        return $this->lang;
312
    }
313
314
    public function saveTexts()
315
    {
316
        if (empty($this->new_texts)) {
317
            return null;
318
        }
319
320
        $ins          = [];
321
        $placeholders = [];
322
        $lang         = $this->lang;
323
        $i            = 1;
324
        foreach ($this->new_texts as $k => $v) {
325
            $ins['key' . $i]   = $k;
326
            $ins['lang' . $i]  = $lang;
327
            $ins['value' . $i] = $v;
328
329
            $placeholders[] = '(:key' . $i . ', :lang' . $i . ', :value' . $i . ')';
330
            $i++;
331
        }
332
333
        $fields = ['key', 'lang', 'value'];
334
335
        $placeholders = implode(', ', $placeholders);
336
337
        $table = $this->getTableName(true);
338
339
        $query = 'INSERT IGNORE
340
            INTO `' . $table . '` (`' . implode('`, `', $fields) . '`)
341
            VALUES ' . $placeholders;
342
343
        $this->db->insert($query, $ins);
344
    }
345
346
    protected function getTableName($with_prefix = false)
347
    {
348
        $table = $this->getConfig('texts_table');
349
        if ($with_prefix) {
350
            $prefix = $this->db->getTablePrefix();
351
            $table  = $prefix . $table;
352
        }
353
        return $table;
354
    }
355
}
356