Completed
Push — v5-dev ( a42eb3 )
by Oscar
01:56
created

PoLoader   B

Complexity

Total Complexity 43

Size/Duplication

Total Lines 225
Duplicated Lines 6.22 %

Coupling/Cohesion

Components 1
Dependencies 8

Importance

Changes 0
Metric Value
dl 14
loc 225
rs 8.96
c 0
b 0
f 0
wmc 43
lcom 1
cbo 8

4 Methods

Rating   Name   Duplication   Size   Complexity  
F loadString() 14 158 32
A fixMultiLines() 0 16 5
A decode() 0 25 3
A isEmpty() 0 12 3

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like PoLoader often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use PoLoader, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Gettext\Loader;
4
5
use Gettext\Translations;
6
use Gettext\Translation;
7
8
/**
9
 * Class to load a PO file
10
 */
11
final class PoLoader extends Loader
12
{
13
    use HeadersLoaderTrait;
14
15
    public function loadString(string $string, Translations $translations = null): Translations
16
    {
17
        $translations = parent::loadString($string, $translations);
18
19
        $lines = explode("\n", $string);
20
        $i = 0;
21
        $append = null;
22
23
        $translation = $this->createTranslation(null, '');
24
25
        for ($n = count($lines); $i < $n; ++$i) {
26
            $line = trim($lines[$i]);
27
            $line = self::fixMultiLines($line, $lines, $i);
28
29
            if ($line === '') {
30
                if (!self::isEmpty($translation)) {
31
                    $translations->add($translation);
32
                }
33
34
                $translation = $this->createTranslation(null, '');
35
                continue;
36
            }
37
38
            $splitLine = preg_split('/\s+/', $line, 2);
39
            $key = $splitLine[0];
40
            $data = isset($splitLine[1]) ? $splitLine[1] : '';
41
42
            if ($key === '#~') {
43
                $translation->setDisabled(true);
0 ignored issues
show
Bug introduced by
The method setDisabled() does not exist on Gettext\Translation. Did you maybe mean disable()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
44
45
                $splitLine = preg_split('/\s+/', $data, 2);
46
                $key = $splitLine[0];
47
                $data = isset($splitLine[1]) ? $splitLine[1] : '';
48
            }
49
50
            if ($data === '') {
51
                continue;
52
            }
53
54
            switch ($key) {
55
                case '#':
56
                    $translation->getComments()->add($data);
57
                    $append = null;
58
                    break;
59
60
                case '#.':
61
                    $translation->getExtractedComments()->add($data);
62
                    $append = null;
63
                    break;
64
65
                case '#,':
66
                    foreach (array_map('trim', explode(',', trim($data))) as $value) {
67
                        $translation->getFlags()->add($value);
68
                    }
69
                    $append = null;
70
                    break;
71
72
                case '#:':
73
                    foreach (preg_split('/\s+/', trim($data)) as $value) {
74
                        if (preg_match('/^(.+)(:(\d*))?$/U', $value, $matches)) {
75
                            $translation->getReferences()->add($matches[1], isset($matches[3]) ? $matches[3] : null);
76
                        }
77
                    }
78
                    $append = null;
79
                    break;
80
81
                case 'msgctxt':
82
                    $translation = $translation->withContext(static::decode($data));
83
                    $append = 'Context';
84
                    break;
85
86
                case 'msgid':
87
                    $translation = $translation->withOriginal(static::decode($data));
88
                    $append = 'Original';
89
                    break;
90
91
                case 'msgid_plural':
92
                    $translation->setPlural(static::decode($data));
93
                    $append = 'Plural';
94
                    break;
95
96
                case 'msgstr':
97
                case 'msgstr[0]':
98
                    $translation->translate(static::decode($data));
99
                    $append = 'Translation';
100
                    break;
101
102
                case 'msgstr[1]':
103
                    $translation->translatePlural(static::decode($data));
104
                    $append = 'PluralTranslation';
105
                    break;
106
107
                default:
108 View Code Duplication
                    if (strpos($key, 'msgstr[') === 0) {
0 ignored issues
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...
109
                        $p = $translation->getPluralTranslations();
110
                        $p[] = static::decode($data);
111
112
                        $translation->translatePlural(...$p);
113
                        $append = 'PluralTranslation';
114
                        break;
115
                    }
116
117
                    if (isset($append)) {
118
                        if ($append === 'Context') {
119
                            $context = $translation->getContext()."\n".static::decode($data);
120
                            $translation = $translation->withContext($context);
121
                            break;
122
                        }
123
124
                        if ($append === 'Original') {
125
                            $original = $translation->getOriginal()."\n".static::decode($data);
126
                            $translation = $translation->withOriginal($original);
127
                            break;
128
                        }
129
130
                        if ($append === 'Plural') {
131
                            $plural = $translation->getPlural()."\n".static::decode($data);
132
                            $translation->setPlural($plural);
133
                            break;
134
                        }
135
136
                        if ($append === 'Translation') {
137
                            $text = $translation->getTranslation()."\n".static::decode($data);
138
                            $translation->translate($text);
139
                            break;
140
                        }
141
142 View Code Duplication
                        if ($append === 'PluralTranslation') {
0 ignored issues
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...
143
                            $p = $translation->getPluralTranslations();
144
                            $p[] = array_pop($p)."\n".static::decode($data);
145
                            $translation->translatePlural(...$p);
146
                            break;
147
                        }
148
                    }
149
                    break;
150
            }
151
        }
152
153
        if (!self::isEmpty($translation)) {
154
            $translations->add($translation);
155
        }
156
157
        //Headers
158
        $translation = $translations->find(null, '');
159
160
        if (!$translation) {
161
            return $translations;
162
        }
163
164
        $translations->remove($translation);
165
        $headers = $translations->getHeaders();
166
167
        foreach (static::parseHeaders($translation->getTranslation()) as $name => $value) {
0 ignored issues
show
Comprehensibility introduced by
Since Gettext\Loader\PoLoader is declared final, using late-static binding will have no effect. You might want to replace static with self instead.

Late static binding only has effect in subclasses. A final class cannot be extended anymore so late static binding cannot occurr. Consider replacing static:: with self::.

To learn more about late static binding, please refer to the PHP core documentation.

Loading history...
168
            $headers->set($name, $value);
169
        }
170
171
        return $translations;
172
    }
173
174
    /**
175
     * Gets one string from multiline strings.
176
     */
177
    private static function fixMultiLines(string $line, array $lines, int &$i): string
178
    {
179
        for ($j = $i, $t = count($lines); $j < $t; ++$j) {
180
            if (substr($line, -1, 1) == '"'
181
                && isset($lines[$j + 1])
182
                && substr(trim($lines[$j + 1]), 0, 1) == '"'
183
            ) {
184
                $line = substr($line, 0, -1).substr(trim($lines[$j + 1]), 1);
185
            } else {
186
                $i = $j;
187
                break;
188
            }
189
        }
190
191
        return $line;
192
    }
193
194
    /**
195
     * Convert a string from its PO representation.
196
     */
197
    public static function decode(string $value): string
198
    {
199
        if (!$value) {
200
            return '';
201
        }
202
203
        if ($value[0] === '"') {
204
            $value = substr($value, 1, -1);
205
        }
206
207
        return strtr(
208
            $value,
209
            [
210
                '\\\\' => '\\',
211
                '\\a' => "\x07",
212
                '\\b' => "\x08",
213
                '\\t' => "\t",
214
                '\\n' => "\n",
215
                '\\v' => "\x0b",
216
                '\\f' => "\x0c",
217
                '\\r' => "\r",
218
                '\\"' => '"',
219
            ]
220
        );
221
    }
222
223
    private static function isEmpty(Translation $translation): bool
224
    {
225
        if (!empty($translation->getOriginal())) {
226
            return false;
227
        }
228
229
        if (!empty($translation->getTranslation())) {
230
            return false;
231
        }
232
233
        return true;
234
    }
235
}
236