Completed
Push — master ( 55fd9e...99545e )
by Mihail
04:10
created

Translate::__construct()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 7
rs 9.4285
cc 2
eloc 4
nc 2
nop 0
1
<?php
2
3
namespace Ffcms\Core\I18n;
4
5
use Ffcms\Core\App;
6
use Ffcms\Core\Helper\Serialize;
7
use Ffcms\Core\Helper\Type\Arr;
8
use Ffcms\Core\Helper\FileSystem\Directory;
9
use Ffcms\Core\Helper\FileSystem\File;
10
use Ffcms\Core\Helper\FileSystem\Normalize;
11
use Ffcms\Core\Helper\Type\Obj;
12
use Ffcms\Core\Helper\Type\Str;
13
14
/**
15
 * Class Translate. Provide methods to work with internalization data in ffcms.
16
 * @package Ffcms\Core\I18n
17
 */
18
class Translate
19
{
20
21
    protected $cached = [];
22
    protected $indexes = [];
23
24
    /**
25
     * Translate constructor. Load default translations.
26
     */
27
    public function __construct()
28
    {
29
        if (App::$Request->getLanguage() !== App::$Properties->get('baseLanguage')) {
30
            $this->cached = $this->load('Default');
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->load('Default') can be null. However, the property $cached is declared as array. Maybe change the type of the property to array|null or add a type check?

Our type inference engine has found an assignment of a scalar value (like a string, an integer or null) to a property which is an array.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property.

To type hint that a parameter can be either an array or null, you can set a type hint of array and a default value of null. The PHP interpreter will then accept both an array or null for that parameter.

function aContainsB(array $needle = null, array  $haystack) {
    if (!$needle) {
        return false;
    }

    return array_intersect($haystack, $needle) == $haystack;
}

The function can be called with either null or an array for the parameter $needle but will only accept an array as $haystack.

Loading history...
31
            $this->indexes[] = 'Default';
32
        }
33
    }
34
35
36
    /**
37
     * Get internalization of current text from i18n
38
     * @param string $index
39
     * @param string $text
40
     * @param array|null $params
41
     * @return string
42
     */
43
    public function get($index, $text, array $params = null)
44
    {
45
        if (App::$Request->getLanguage() !== App::$Properties->get('baseLanguage')) {
46
            if ($index !== null && !Arr::in($index, $this->indexes)) {
47
                $this->cached = Arr::merge($this->cached, $this->load($index));
48
                $this->indexes[] = $index;
49
            }
50
51
            if ($this->cached !== null && Obj::isString($text) && isset($this->cached[$text])) {
52
                $text = $this->cached[$text];
53
            }
54
        }
55
56
        if (Obj::isArray($params) && count($params) > 0) {
57
            foreach ($params as $var => $value) {
0 ignored issues
show
Bug introduced by
The expression $params of type null|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
58
                $text = Str::replace('%' . $var . '%', $value, $text);
59
            }
60
        }
61
        return $text;
62
    }
63
64
    /**
65
     * Get internalization based on called controller
66
     * @param string $text
67
     * @param array $params
68
     * @return string
69
     */
70
    public function translate($text, array $params = [])
71
    {
72
        $index = null;
73
        $namespace = 'Apps\Controller\\' . env_name . '\\';
74
        foreach (@debug_backtrace() as $caller) {
75
            if (isset($caller['class']) && Str::startsWith($namespace, $caller['class'])) {
76
                $index = Str::sub((string)$caller['class'], Str::length($namespace));
77
            }
78
        }
79
        return $this->get($index, $text, $params);
80
    }
81
82
    /**
83
     * Load locale file from local storage
84
     * @param string $index
85
     * @return array|null
86
     */
87
    protected function load($index)
88
    {
89
        $file = root . '/I18n/' . env_name . '/' . App::$Request->getLanguage() . '/' . $index . '.php';
90
        if (!File::exist($file)) {
91
            return [];
92
        }
93
        return require($file);
94
    }
95
96
    /**
97
     * Append translation data from exist full path
98
     * @param string $path
99
     * @return bool
100
     */
101
    public function append($path)
102
    {
103
        $path = Normalize::diskFullPath($path);
104
        // check if file exist
105
        if (!File::exist($path)) {
106
            return false;
107
        }
108
109
        // load file translations
110
        $addTranslation = require($path);
111
        if (!Obj::isArray($addTranslation)) {
112
            return false;
113
        }
114
115
        // merge data
116
        $this->cached = Arr::merge($this->cached, $addTranslation);
117
        return true;
118
    }
119
120
    /**
121
     * Get available languages in the filesystem
122
     * @return array
123
     */
124
    public function getAvailableLangs()
125
    {
126
        $langs = ['en'];
127
        $scan = Directory::scan(root . '/I18n/' . env_name . '/', GLOB_ONLYDIR, true);
128
        foreach ($scan as $row) {
0 ignored issues
show
Bug introduced by
The expression $scan of type false|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
129
            $langs[] = trim($row, '/');
130
        }
131
        return $langs;
132
    }
133
134
    /**
135
     * Get locale data from input array or serialized string
136
     * @param array|string $input
137
     * @param string|null $lang
138
     * @param string|null $default
139
     * @return string|null
140
     */
141
    public function getLocaleText($input, $lang = null, $default = null)
142
    {
143
        // define language if empty
144
        if ($lang === null) {
145
            $lang = App::$Request->getLanguage();
146
        }
147
        // unserialize from string to array
148
        if (Obj::isString($input)) {
149
            $input = Serialize::decode($input);
0 ignored issues
show
Bug introduced by
It seems like $input defined by \Ffcms\Core\Helper\Serialize::decode($input) on line 149 can also be of type array; however, Ffcms\Core\Helper\Serialize::decode() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
150
        }
151
152
        if (Obj::isArray($input) && array_key_exists($lang, $input)) {
153
            return $input[$lang];
154
        }
155
156
        return $default;
157
    }
158
}