Completed
Pull Request — master (#93)
by Michele
03:04 queued 01:21
created

Po::isHeaderDefinition()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

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