Completed
Push — master ( d5b3be...2885d7 )
by Alejandro
02:46
created

Localize::parseXmlStringValues()   B

Complexity

Conditions 6
Paths 3

Size

Total Lines 23
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 23
rs 8.5906
cc 6
eloc 16
nc 3
nop 1
1
<?php
2
3
/*
4
 * The MIT License
5
 *
6
 * Copyright 2016 Alejandro Peña Florentín ([email protected]).
7
 *
8
 * Permission is hereby granted, free of charge, to any person obtaining a copy
9
 * of this software and associated documentation files (the "Software"), to deal
10
 * in the Software without restriction, including without limitation the rights
11
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12
 * copies of the Software, and to permit persons to whom the Software is
13
 * furnished to do so, subject to the following conditions:
14
 *
15
 * The above copyright notice and this permission notice shall be included in
16
 * all copies or substantial portions of the Software.
17
 *
18
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24
 * THE SOFTWARE.
25
 */
26
27
namespace Tight\Modules\Localize;
28
29
/**
30
 * Localize module for translations
31
 *
32
 * @author Alejandro Peña Florentín ([email protected])
33
 */
34
class Localize extends \Tight\Modules\AbstractModule
35
{
36
37
    /**
38
     * @var string Resource file type
39
     */
40
    private $resourceFileType;
41
42
    /**
43
     * @var string Current locale
44
     */
45
    private $locale;
46
47
    /**
48
     * @var array Associative array for the current locale
49
     */
50
    private $values;
51
52
    /**
53
     * Constructor
54
     * 
55
     * @param array|\Tight\Modules\Localize\LocalizeConfig $config
56
     * @throws \InvalidArgumentException If $config is not an array or an object
57
     * of \Tight\Modules\Localize\LocalizeConfig class
58
     */
59
    public function __construct($config = []) {
60
        if (is_array($config)) {
61
            $config = new \Tight\Modules\Localize\LocalizeConfig($config);
62
        } else if (!$config instanceof \Tight\Modules\Localize\LocalizeConfig) {
63
            throw new \InvalidArgumentException("Argument 1 passed to " . get_class($this) . " must be an array or an instance of Tight\Modules\Localize\LocalizeConfig");
64
        }
65
        parent::__construct("LocalizeModule", "v1.2");
66
        $this->setConfig($config);
67
        $this->checkDependences();
68
        $this->setResourceFileType($this->getConfig()->resourceFileType);
69
        $this->setLocale($this->getConfig()->defaultLocale);
70
    }
71
72
    /**
73
     * Sets the resource file type
74
     * @param string $resourceFileType Resource file type
75
     * @return \Tight\Modules\Localize\Localize Fluent setter
76
     */
77
    public function setResourceFileType($resourceFileType) {
78
        $this->resourceFileType = $resourceFileType;
79
        $this->reloadConfig();
80
        return $this;
81
    }
82
83
    /**
84
     * Gets the current resource file type
85
     * @return string Resource file type
86
     * @see \Tight\Modules\Localize\LocalizeConfig::FILETYPE_INI
87
     * @see \Tight\Modules\Localize\LocalizeConfig::FILETYPE_JSON
88
     * @see \Tight\Modules\Localize\LocalizeConfig::FILETYPE_XML
89
     */
90
    public function getResourceFileType() {
91
        return $this->resourceFileType;
92
    }
93
94
    /**
95
     * Gets all the defined values for the current locale
96
     * @return array Associative array of values
97
     */
98
    public function getValues() {
99
        return $this->values;
100
    }
101
102
    /**
103
     * Reloads the class with the new locale
104
     */
105
    public function reloadConfig() {
106
        if (null == $this->locale) {
107
            $this->locale = $this->getConfig()->defaultLocale;
108
        }
109
        $values = null;
110
        switch ($this->resourceFileType) {
111
            case LocalizeConfig::FILETYPE_JSON:
112
                $values = $this->getValuesFromJson();
113
                break;
114
            case LocalizeConfig::FILETYPE_XML:
115
                $values = $this->getValuesFromXml();
116
                break;
117
        }
118
        $this->values = $values;
1 ignored issue
show
Documentation Bug introduced by
It seems like $values can be null. However, the property $values 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...
119
    }
120
121
    /**
122
     * Checks the dependences for the class
123
     * @throws \Tight\Exception\ModuleException If resource directory cant be 
124
     * found
125
     */
126
    private function checkDependences() {
127
        if (!is_dir($this->getConfig()->resourceFolder)) {
128
            throw new \Tight\Exception\ModuleException("Resource directory not found");
129
        }
130
    }
131
132
    /**
133
     * Sets a new locale
134
     * @param string $locale New defined locale
135
     * @throws \Tight\Exception\ModuleException If resource default resource file
136
     * cant be found
137
     */
138
    public function setLocale($locale) {
139
        $this->locale = $locale;
140
        $this->reloadConfig();
141
    }
142
143
    /**
144
     * Gets the available locales
145
     * @return array Available locales
146
     */
147
    public function getLocales() {
148
        $output = [];
149
        switch ($this->resourceFileType) {
150
            case LocalizeConfig::FILETYPE_JSON:
151
                $output = $this->getLocalesFromJson();
152
                break;
153
            case LocalizeConfig::FILETYPE_XML:
154
                $output = $this->getLocalesFromXml();
155
                break;
156
            case LocalizeConfig::FILETYPE_INI:
157
                break;
158
        }
159
        return $output;
160
    }
161
162
    private function getLocalesFromJson() {
163
        $output = [];
164
        $directory = $this->getConfig()->resourceFolder;
165
        $fileName = $this->getConfig()->resourceFileName;
166
        $dir = opendir($directory);
167
        $files = [];
168
        while ($entry = readdir($dir)) {
169
            if (strpos($entry, $fileName) !== false) {
170
                $files[] = $entry;
171
            }
172
        }
173
        foreach ($files as $element) {
174
            $file = \Tight\Utils::getSlicedFile($directory . $element);
175
            //Removes extension
176
            $name = $file["name"];
177
            $explode = explode($this->getConfig()->langSeparator, $name);
178
            // Get the locale of the defined file type
179
            if ($file["ext"] == $this->getConfig()->resourceFileType) {
180
                if (count($explode) > 1) {
181
                    $output[] = $explode[count($explode) - 1];
182
                } else {
183
                    $output[] = $this->getConfig()->defaultLocale;
184
                }
185
            }
186
        }
187
        return $output;
188
    }
189
190
    /**
191
     * Gets the json values from the json file
192
     * @return array String values
193
     * @throws \Tight\Exception\ModuleException If json file cant be found
194
     */
195
    private function getValuesFromJson() {
196
        $output = [];
1 ignored issue
show
Unused Code introduced by
$output is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
197
        $folder = \Tight\Utils::addTrailingSlash($this->getConfig()->resourceFolder);
198
        $fileName = $this->getConfig()->resourceFileName . $this->getConfig()->langSeparator . $this->locale . "." . $this->getConfig()->resourceFileType;
199
        $file = $folder . $fileName;
200
        if (is_file($file)) {
201
            $output = json_decode(file_get_contents($file), JSON_FORCE_OBJECT);
202
        } else {
203
            $fileName = $this->getConfig()->resourceFileName . "." . $this->getConfig()->resourceFileType;
204
            $file = $folder . $fileName;
205 View Code Duplication
            if (is_file($file)) {
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
206
                $output = json_decode(file_get_contents($file), JSON_FORCE_OBJECT);
207
            } else {
208
                throw new \Tight\Exception\ModuleException("Resource file <strong>" . $file . "</strong> not found");
209
            }
210
        }
211
        return $output;
212
    }
213
214
    /**
215
     * Gets the locales from the xml resource file
216
     * @return array Array of locales
217
     */
218
    private function getLocalesFromXml() {
219
        $output = [];
220
        $resources = $this->getXmlResources();
221
        $size = count($resources->values);
222
        for ($index = 0; $index < $size; $index++) {
223
            $output[] = $resources->values[$index]['lang']->__toString();
224
        }
225
        return $output;
226
    }
227
228
    /**
229
     * Gets the xml resource read from the xml resource file
230
     * @return \SimpleXMLElement XML values for the current locale
1 ignored issue
show
Documentation introduced by
Should the return type not be \SimpleXMLElement|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
231
     * @throws \Tight\Exception\ModuleException If xml values resource file cant be found
232
     */
233
    private function getXmlResources() {
234
        $output = null;
235
        if ($this->resourceFileType === LocalizeConfig::FILETYPE_XML) {
236
            $file = \Tight\Utils::addTrailingSlash($this->getConfig()->resourceFolder) . $this->getConfig()->resourceFileName . "." . $this->resourceFileType;
237 View Code Duplication
            if (is_file($file)) {
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
238
                $xmlContent = file_get_contents($file);
239
                $output = new \SimpleXMLElement($xmlContent);
240
            } else {
241
                throw new \Tight\Exception\ModuleException("Resource file <strong>" . $file . "</strong> does not exists");
242
            }
243
        }
244
        return $output;
245
    }
246
247
    /**
248
     * Gets the values from xml resource file
249
     * @return array Array of values
250
     * @throws \Tight\Exception\ModuleException If the current locale cant be 
251
     * found in the xml resource file
252
     */
253
    private function getValuesFromXml() {
254
        $output = [];
0 ignored issues
show
Unused Code introduced by
$output is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
255
        $resources = $this->getXmlResources();
256
        $locale = null;
257
        $size = count($resources->values);
258
        $index = 0;
259
        while ($index < $size && $locale == null) {
260
            if ($resources->values[$index]['lang'] == $this->locale) {
261
                $locale = $resources->values[$index];
262
            }
263
            $index++;
264
        }
265
        if (null == $locale) {
266
            $fileName = $this->getConfig()->resourceFileName . "." . $this->getConfig()->resourceFileType;
267
            throw new \Tight\Exception\ModuleException("Locale <strong>" . $this->locale . "</strong> not found at resource file <strong>" . $fileName . "</strong>");
268
        }
269
        return $this->parseXmlStringValues($locale);
270
    }
271
272
    /**
273
     * Parses a \SimpleXmlElement object to get its values
274
     * @param \SimpleXMLElement $xml XML object
275
     * @return array Values
276
     */
277
    private function parseXmlStringValues(\SimpleXMLElement $xml) {
278
        $output = [];
279
        if ($xml->count() > 0) {
280
            $children = $xml->children();
281
            for ($index = 0; $index < count($children); $index++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
282
                // If xml tag is <array>, parse without merge
283
                if ($children[$index]->getName() === "array") {
284
                    $key = $children[$index]['name'];
285
                    if (null !== $key) {
286
                        $output[$key->__toString()] = $this->parseXmlStringValues($children[$index]);
287
                    }
288
                } else {
289
                    $output = array_merge($output, $this->parseXmlStringValues($children[$index]));
290
                }
291
            }
292
        } else {
293
            $key = $xml['name'];
294
            if (null !== $key) {
295
                $output[$key->__toString()] = $xml->__toString();
296
            }
297
        }
298
        return $output;
299
    }
300
301
    /**
302
     * Gets a value from a defined key
303
     * @param string $key Key
304
     * @return string Value defined for the key $key or an empty string if the
305
     * key is not defined
306
     */
307
    public function get($key) {
308
        if (isset($this->values[$key])) {
309
            return $this->values[$key];
310
        } else {
311
            return "";
312
        }
313
    }
314
315
    public function onConfigChange() {
316
        $this->reloadConfig();
317
    }
318
319
    public function onLoad() {
320
        
321
    }
322
323
    public function onRemove() {
324
        
325
    }
326
327
}
328