Completed
Pull Request — master (#93)
by Michele
02:10
created

Po::convertString()   B

Complexity

Conditions 3
Paths 3

Size

Total Lines 25
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 25
rs 8.8571
cc 3
eloc 17
nc 3
nop 1
1
<?php
2
3
namespace Gettext\Extractors;
4
5
use Gettext\Translations;
6
use Gettext\Translation;
7
8
/**
9
 * Class to get gettext strings from php files returning arrays.
10
 */
11
class Po extends Extractor implements ExtractorInterface
12
{
13
    /**
14
     * Parses a .po file and append the translations found in the Translations instance.
15
     *
16
     * {@inheritdoc}
17
     */
18
    public static function fromString($string, Translations $translations = null, $file = '')
19
    {
20
        if ($translations === null) {
21
            $translations = new Translations();
22
        }
23
24
        $lines = explode("\n", $string);
25
        $i = 0;
26
27
        $translation = new Translation('', '');
28
29
        for ($n = count($lines); $i < $n; ++$i) {
30
            $line = trim($lines[$i]);
31
32
            $line = self::fixMultiLines($line, $lines, $i);
33
34
            if ($line === '') {
35
                if ($translation->is('', '')) {
36
                    self::parseHeaders($translation->getTranslation(), $translations);
37
                } elseif ($translation->hasOriginal()) {
38
                    $translations[] = $translation;
39
                }
40
41
                $translation = new Translation('', '');
42
                continue;
43
            }
44
45
            $splitLine = preg_split('/\s+/', $line, 2);
46
            $key = $splitLine[0];
47
            $data = isset($splitLine[1]) ? $splitLine[1] : '';
48
49
            switch ($key) {
50
                case '#':
51
                    $translation->addComment($data);
52
                    $append = null;
53
                    break;
54
55
                case '#.':
56
                    $translation->addExtractedComment($data);
57
                    $append = null;
58
                    break;
59
60
                case '#,':
61
                    foreach (array_map('trim', explode(',', trim($data))) as $value) {
62
                        $translation->addFlag($value);
63
                    }
64
                    $append = null;
65
                    break;
66
67
                case '#:':
68
                    foreach (preg_split('/\s+/', trim($data)) as $value) {
69
                        if (preg_match('/^(.+)(:(\d*))?$/U', $value, $matches)) {
70
                            $translation->addReference($matches[1], isset($matches[3]) ? $matches[3] : null);
71
                        }
72
                    }
73
                    $append = null;
74
                    break;
75
76
                case 'msgctxt':
77
                    $translation = $translation->getClone(self::convertString($data));
78
                    $append = 'Context';
79
                    break;
80
81
                case 'msgid':
82
                    $translation = $translation->getClone(null, self::convertString($data));
83
                    $append = 'Original';
84
                    break;
85
86
                case 'msgid_plural':
87
                    $translation->setPlural(self::convertString($data));
88
                    $append = 'Plural';
89
                    break;
90
91
                case 'msgstr':
92
                case 'msgstr[0]':
93
                    $translation->setTranslation(self::convertString($data));
94
                    $append = 'Translation';
95
                    break;
96
97
                case 'msgstr[1]':
98
                    $translation->setPluralTranslation(self::convertString($data), 0);
99
                    $append = 'PluralTranslation';
100
                    break;
101
102
                default:
103
                    if (strpos($key, 'msgstr[') === 0) {
104
                        $translation->setPluralTranslation(self::convertString($data), intval(substr($key, 7, -1)) - 1);
105
                        $append = 'PluralTranslation';
106
                        break;
107
                    }
108
109
                    if (isset($append)) {
110
                        if ($append === 'Context') {
111
                            $translation = $translation->getClone($translation->getContext()."\n".self::convertString($data));
112
                            break;
113
                        }
114
115
                        if ($append === 'Original') {
116
                            $translation = $translation->getClone(null, $translation->getOriginal()."\n".self::convertString($data));
117
                            break;
118
                        }
119
120
                        if ($append === 'PluralTranslation') {
121
                            $key = count($translation->getPluralTranslation()) - 1;
122
                            $translation->setPluralTranslation($translation->getPluralTranslation($key)."\n".self::convertString($data), $key);
123
                            break;
124
                        }
125
126
                        $getMethod = 'get'.$append;
127
                        $setMethod = 'set'.$append;
128
                        $translation->$setMethod($translation->$getMethod()."\n".self::convertString($data));
129
                    }
130
                    break;
131
            }
132
        }
133
134
        if ($translation->hasOriginal() && !in_array($translation, iterator_to_array($translations))) {
135
            $translations[] = $translation;
136
        }
137
138
        return $translations;
139
    }
140
141
    /**
142
     * Checks if it is a header definition line. Useful for distguishing between header definitions
143
     * and possible continuations of a header entry.
144
     *
145
     * @param string $line Line to parse
146
     *
147
     * @return bool
148
     */
149
    private static function isHeaderDefinition($line)
150
    {
151
        return (bool) preg_match('/^[\w-]+:/', $line);
152
    }
153
154
    /**
155
     * Parse the po headers.
156
     *
157
     * @param string       $headers
158
     * @param Translations $translations
159
     */
160
    private static function parseHeaders($headers, Translations $translations)
161
    {
162
        $headers = explode("\n", $headers);
163
        $currentHeader = null;
164
165
        foreach ($headers as $line) {
166
            $line = self::convertString($line);
167
168
            if (self::isHeaderDefinition($line)) {
169
                $header = array_map('trim', explode(':', $line, 2));
170
                $currentHeader = $header[0];
171
                $translations->setHeader($currentHeader, $header[1]);
172
            } else {
173
                $entry = $translations->getHeader($currentHeader);
174
                $translations->setHeader($currentHeader, $entry.$line);
175
            }
176
        }
177
    }
178
179
    /**
180
     * Gets one string from multiline strings.
181
     *
182
     * @param string $line
183
     * @param array  $lines
184
     * @param int    &$i
185
     *
186
     * @return string
187
     */
188
    private static function fixMultiLines($line, array $lines, &$i)
189
    {
190
        for ($j = $i, $t = count($lines); $j < $t; ++$j) {
191
            if (substr($line, -1, 1) == '"'
192
                && isset($lines[$j + 1])
193
                && substr(trim($lines[$j + 1]), 0, 1) == '"'
194
            ) {
195
                $line = substr($line, 0, -1).substr(trim($lines[$j + 1]), 1);
196
            } else {
197
                $i = $j;
198
                break;
199
            }
200
        }
201
202
        return $line;
203
    }
204
205
    /**
206
     * Convert a string from its PO representation.
207
     *
208
     * @param string $value
209
     *
210
     * @return string
211
     */
212
    public static function convertString($value)
213
    {
214
        if (!$value) {
215
            return '';
216
        }
217
218
        if ($value[0] === '"') {
219
            $value = substr($value, 1, -1);
220
        }
221
222
        return strtr(
223
            $value,
224
            array(
225
                '\\\\' => '\\',
226
                '\\a' => "\x07",
227
                '\\b' => "\x08",
228
                '\\t' => "\t",
229
                '\\n' => "\n",
230
                '\\v' => "\x0b",
231
                '\\f' => "\x0c",
232
                '\\r' => "\r",
233
                '\\"' => '"',
234
            )
235
        );
236
    }
237
}
238