Completed
Push — master ( 91576d...2659cb )
by Oscar
02:19
created

Translations::getHeaders()   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 0
1
<?php
2
3
namespace Gettext;
4
5
use Gettext\Languages\Language;
6
use BadMethodCallException;
7
use InvalidArgumentException;
8
9
/**
10
 * Class to manage a collection of translations.
11
 */
12
class Translations extends \ArrayObject
13
{
14
    const MERGE_ADD = 1;
15
    const MERGE_REMOVE = 2;
16
    const MERGE_HEADERS = 4;
17
    const MERGE_REFERENCES = 8;
18
    const MERGE_COMMENTS = 16;
19
    const MERGE_LANGUAGE = 32;
20
    const MERGE_PLURAL = 64;
21
    const MERGE_OVERRIDE = 128;
22
23
    const HEADER_LANGUAGE = 'Language';
24
    const HEADER_PLURAL = 'Plural-Forms';
25
    const HEADER_DOMAIN = 'X-Domain';
26
27
    public static $mergeDefault = 221; // self::MERGE_ADD | self::MERGE_OVERRIDE | self::MERGE_HEADERS | self::MERGE_COMMENTS | self::MERGE_REFERENCES | self::MERGE_PLURAL
28
    public static $insertDate = true;
29
30
    private $headers;
31
32
    /**
33
     * @see \ArrayObject::__construct()
34
     */
35
    public function __construct($input = [], $flags = 0, $iterator_class = 'ArrayIterator')
36
    {
37
        $this->headers = [
38
            'Project-Id-Version' => '',
39
            'Report-Msgid-Bugs-To' => '',
40
            'Last-Translator' => '',
41
            'Language-Team' => '',
42
            'MIME-Version' => '1.0',
43
            'Content-Type' => 'text/plain; charset=UTF-8',
44
            'Content-Transfer-Encoding' => '8bit',
45
        ];
46
47
        if (static::$insertDate) {
48
            $this->headers['POT-Creation-Date'] = $this->headers['PO-Revision-Date'] = date('c');
49
        }
50
51
        $this->headers[self::HEADER_LANGUAGE] = '';
52
        parent::__construct($input, $flags, $iterator_class);
53
    }
54
55
    /**
56
     * Magic method to create new instances using extractors
57
     * For example: Translations::fromMoFile($filename, $options);.
58
     *
59
     * @return Translations
60
     */
61
    public static function __callStatic($name, $arguments)
62
    {
63
        if (!preg_match('/^from(\w+)(File|String)$/i', $name, $matches)) {
64
            throw new BadMethodCallException("The method $name does not exists");
65
        }
66
67
        return call_user_func_array([new static(), 'add'.ucfirst($name)], $arguments);
68
    }
69
70
    /**
71
     * Magic method to import/export the translations to a specific format
72
     * For example: $translations->toMoFile($filename, $options);
73
     * For example: $translations->addFromMoFile($filename, $options);.
74
     *
75
     * @return self|bool
76
     */
77
    public function __call($name, $arguments)
78
    {
79
        if (!preg_match('/^(addFrom|to)(\w+)(File|String)$/i', $name, $matches)) {
80
            throw new BadMethodCallException("The method $name does not exists");
81
        }
82
83
        if ($matches[1] === 'addFrom') {
84
            $extractor = 'Gettext\\Extractors\\'.$matches[2].'::from'.$matches[3];
85
            $source = array_shift($arguments);
86
            $options = array_shift($arguments) ?: [];
87
88
            call_user_func($extractor, $source, $this, $options);
89
90
            return $this;
91
        }
92
93
        $generator = 'Gettext\\Generators\\'.$matches[2].'::to'.$matches[3];
94
95
        array_unshift($arguments, $this);
96
97
        return call_user_func_array($generator, $arguments);
98
    }
99
100
    /**
101
     * Magic method to clone each translation on clone the translations object.
102
     */
103
    public function __clone()
104
    {
105
        $array = [];
106
107
        foreach ($this as $key => $translation) {
108
            $array[$key] = clone $translation;
109
        }
110
111
        $this->exchangeArray($array);
112
    }
113
114
    /**
115
     * Control the new translations added.
116
     *
117
     * @param mixed       $index
118
     * @param Translation $value
119
     *
120
     * @throws InvalidArgumentException If the value is not an instance of Gettext\Translation
121
     *
122
     * @return Translation
123
     */
124
    public function offsetSet($index, $value)
125
    {
126
        if (!($value instanceof Translation)) {
127
            throw new InvalidArgumentException('Only instances of Gettext\\Translation must be added to a Gettext\\Translations');
128
        }
129
130
        $id = $value->getId();
131
132
        if ($this->offsetExists($id)) {
133
            $this[$id]->mergeWith($value);
134
135
            return $this[$id];
136
        }
137
138
        parent::offsetSet($id, $value);
139
140
        return $value;
141
    }
142
143
    /**
144
     * Set the plural definition.
145
     *
146
     * @param int    $count
147
     * @param string $rule
148
     * 
149
     * @return self
150
     */
151
    public function setPluralForms($count, $rule)
152
    {
153
        $this->setHeader(self::HEADER_PLURAL, "nplurals={$count}; plural={$rule};");
154
155
        return $this;
156
    }
157
158
    /**
159
     * Returns the parsed plural definition.
160
     *
161
     * @param null|array [count, rule]
162
     */
163
    public function getPluralForms()
164
    {
165
        $header = $this->getHeader(self::HEADER_PLURAL);
166
167
        if (!empty($header) && preg_match('/^nplurals\s*=\s*(\d+)\s*;\s*plural\s*=\s*([^;]+)\s*;$/', $header, $matches)) {
168
            return [intval($matches[1]), $matches[2]];
169
        }
170
    }
171
172
    /**
173
     * Set a new header.
174
     *
175
     * @param string $name
176
     * @param string $value
177
     * 
178
     * @return self
179
     */
180
    public function setHeader($name, $value)
181
    {
182
        $name = trim($name);
183
        $this->headers[$name] = trim($value);
184
185
        return $this;
186
    }
187
188
    /**
189
     * Returns a header value.
190
     *
191
     * @param string $name
192
     *
193
     * @return null|string
194
     */
195
    public function getHeader($name)
196
    {
197
        return isset($this->headers[$name]) ? $this->headers[$name] : null;
198
    }
199
200
    /**
201
     * Returns all header for this translations.
202
     *
203
     * @return array
204
     */
205
    public function getHeaders()
206
    {
207
        return $this->headers;
208
    }
209
210
    /**
211
     * Removes all headers.
212
     * 
213
     * @return self
214
     */
215
    public function deleteHeaders()
216
    {
217
        $this->headers = [];
218
219
        return $this;
220
    }
221
222
    /**
223
     * Removes one header.
224
     *
225
     * @param string $name
226
     * 
227
     * @return self
228
     */
229
    public function deleteHeader($name)
230
    {
231
        unset($this->headers[$name]);
232
233
        return $this;
234
    }
235
236
    /**
237
     * Returns the language value.
238
     *
239
     * @return string $language
240
     */
241
    public function getLanguage()
242
    {
243
        return $this->getHeader(self::HEADER_LANGUAGE);
244
    }
245
246
    /**
247
     * Sets the language and the plural forms.
248
     *
249
     * @param string $language
250
     * 
251
     * @throws InvalidArgumentException if the language hasn't been recognized
252
     *
253
     * @return self
254
     */
255
    public function setLanguage($language)
256
    {
257
        $this->setHeader(self::HEADER_LANGUAGE, trim($language));
258
259
        if (($info = Language::getById($language))) {
260
            return $this->setPluralForms(count($info->categories), $info->formula);
261
        }
262
263
        throw new InvalidArgumentException(sprintf('The language "%s" is not valid', $language));
264
    }
265
266
    /**
267
     * Checks whether the language is empty or not.
268
     *
269
     * @return bool
270
     */
271
    public function hasLanguage()
272
    {
273
        $language = $this->getLanguage();
274
275
        return (is_string($language) && ($language !== '')) ? true : false;
276
    }
277
278
    /**
279
     * Set a new domain for this translations.
280
     *
281
     * @param string $domain
282
     * 
283
     * @return self
284
     */
285
    public function setDomain($domain)
286
    {
287
        $this->setHeader(self::HEADER_DOMAIN, trim($domain));
288
289
        return $this;
290
    }
291
292
    /**
293
     * Returns the domain.
294
     *
295
     * @return string
296
     */
297
    public function getDomain()
298
    {
299
        return $this->getHeader(self::HEADER_DOMAIN);
300
    }
301
302
    /**
303
     * Checks whether the domain is empty or not.
304
     *
305
     * @return bool
306
     */
307
    public function hasDomain()
308
    {
309
        $domain = $this->getDomain();
310
311
        return (is_string($domain) && ($domain !== '')) ? true : false;
312
    }
313
314
    /**
315
     * Search for a specific translation.
316
     *
317
     * @param string|Translation $context  The context of the translation or a translation instance
318
     * @param string             $original The original string
319
     *
320
     * @return Translation|false
321
     */
322
    public function find($context, $original = '')
323
    {
324
        if ($context instanceof Translation) {
325
            $id = $context->getId();
326
        } else {
327
            $id = Translation::generateId($context, $original);
328
        }
329
330
        return $this->offsetExists($id) ? $this[$id] : false;
331
    }
332
333
    /**
334
     * Creates and insert/merges a new translation.
335
     *
336
     * @param string $context  The translation context
337
     * @param string $original The translation original string
338
     * @param string $plural   The translation original plural string
339
     *
340
     * @return Translation The translation created
341
     */
342
    public function insert($context, $original, $plural = '')
343
    {
344
        return $this->offsetSet(null, new Translation($context, $original, $plural));
345
    }
346
347
    /**
348
     * Merges this translations with other translations.
349
     *
350
     * @param Translations $translations The translations instance to merge with
351
     * @param int|null     $method       One or various Translations::MERGE_* constants to define how to merge the translations
352
     * 
353
     * @return self
354
     */
355
    public function mergeWith(Translations $translations, $method = null)
356
    {
357
        if ($method === null) {
358
            $method = self::$mergeDefault;
359
        }
360
361
        if ($method & self::MERGE_HEADERS) {
362
            foreach ($translations->getHeaders() as $name => $value) {
363
                if (!$this->getHeader($name)) {
364
                    $this->setHeader($name, $value);
365
                }
366
            }
367
        }
368
369
        $add = (boolean) ($method & self::MERGE_ADD);
370
371
        foreach ($translations as $entry) {
372
            if (($existing = $this->find($entry))) {
373
                $existing->mergeWith($entry, $method);
374
            } elseif ($add) {
375
                $this[] = clone $entry;
376
            }
377
        }
378
379
        if ($method & self::MERGE_REMOVE) {
380
            $filtered = [];
381
382
            foreach ($this as $entry) {
383
                if ($translations->find($entry)) {
384
                    $filtered[] = $entry;
385
                }
386
            }
387
388
            $this->exchangeArray($filtered);
389
        }
390
391
        if ($method & self::MERGE_LANGUAGE) {
392
            $language = $translations->getLanguage();
393
            $pluralForm = $translations->getPluralForms();
394
395
            if (!$pluralForm) {
396
                if (!empty($language)) {
397
                    $this->setLanguage($language);
398
                }
399
            } else {
400
                if (!empty($language)) {
401
                    $this->setHeader(self::HEADER_LANGUAGE, $language);
402
                }
403
404
                $this->setPluralForms($pluralForm[0], $pluralForm[1]);
405
            }
406
        }
407
408
        return $this;
409
    }
410
}
411