Completed
Push — v5-dev ( a42eb3...269a1b )
by Oscar
01:14
created

MoLoader::loadString()   C

Complexity

Conditions 13
Paths 33

Size

Total Lines 82

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 13
nc 33
nop 2
dl 0
loc 82
rs 5.686
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace Gettext\Loader;
4
5
use Gettext\Translations;
6
use Gettext\Translation;
7
use Exception;
8
9
/**
10
 * Class to load a MO file
11
 */
12
final class MoLoader extends Loader
13
{
14
    private $string;
15
    private $position;
16
    private $length;
17
18
    private const MAGIC1 = -1794895138;
19
    private const MAGIC2 = -569244523;
20
    private const MAGIC3 = 2500072158;
21
22
    public function loadString(string $string, Translations $translations = null): Translations
23
    {
24
        $translations = parent::loadString($string, $translations);
25
        $this->init($string);
26
27
        $magic = $this->readInt('V');
28
29
        if (($magic === self::MAGIC1) || ($magic === self::MAGIC3)) { //to make sure it works for 64-bit platforms
30
            $byteOrder = 'V'; //low endian
31
        } elseif ($magic === (self::MAGIC2 & 0xFFFFFFFF)) {
32
            $byteOrder = 'N'; //big endian
33
        } else {
34
            throw new Exception('Not MO file');
35
        }
36
37
        $this->readInt($byteOrder);
38
39
        $total = $this->readInt($byteOrder); //total string count
40
        $originals = $this->readInt($byteOrder); //offset of original table
41
        $tran = $this->readInt($byteOrder); //offset of translation table
42
43
        $this->seekto($originals);
44
        $table_originals = $this->readIntArray($byteOrder, $total * 2);
45
46
        $this->seekto($tran);
47
        $table_translations = $this->readIntArray($byteOrder, $total * 2);
48
49
        for ($i = 0; $i < $total; ++$i) {
50
            $next = $i * 2;
51
52
            $this->seekto($table_originals[$next + 2]);
53
            $original = $this->read($table_originals[$next + 1]);
54
55
            $this->seekto($table_translations[$next + 2]);
56
            $translated = $this->read($table_translations[$next + 1]);
57
58
            // Headers
59
            if ($original === '') {
60
                foreach (explode("\n", $translated) as $headerLine) {
61
                    if ($headerLine === '') {
62
                        continue;
63
                    }
64
65
                    $headerChunks = preg_split('/:\s*/', $headerLine, 2);
66
                    $translations->getHeaders()->set($headerChunks[0], isset($headerChunks[1]) ? $headerChunks[1] : '');
67
                }
68
69
                continue;
70
            }
71
72
            $context = $plural = null;
73
            $chunks = explode("\x04", $original, 2);
74
75
            if (isset($chunks[1])) {
76
                list($context, $original) = $chunks;
77
            }
78
79
            $chunks = explode("\x00", $original, 2);
80
81
            if (isset($chunks[1])) {
82
                list($original, $plural) = $chunks;
83
            }
84
85
            $translation = $this->createTranslation($context, $original, $plural);
86
            $translations->add($translation);
87
88
            if ($translated === '') {
89
                continue;
90
            }
91
92
            if ($plural === null) {
93
                $translation->translate($translated);
94
                continue;
95
            }
96
97
            $v = explode("\x00", $translated);
98
            $translation->translate(array_shift($v));
99
            $translation->translatePlural($v);
100
        }
101
        
102
        return $translations;
103
    }
104
    
105
    private function init(string $string): void
106
    {
107
        $this->string = $string;
108
        $this->position = 0;
109
        $this->length = strlen($string);
110
    }
111
112
    private function read(int $bytes): string
113
    {
114
        $data = substr($this->string, $this->position, $bytes);
115
116
        $this->seekTo($this->position + $bytes);
117
118
        return $data;
119
    }
120
121
    private function seekTo(int $position): void
122
    {
123
        $this->position = ($this->length < $position) ? $this->length : $position;
124
    }
125
126
    private function readInt(string $byteOrder)
127
    {
128
        if (($read = $this->read(4)) === false) {
129
            return false;
130
        }
131
132
        $read = unpack($byteOrder, $read);
133
134
        return array_shift($read);
135
    }
136
137
    private function readIntArray(string $byteOrder, int $count)
138
    {
139
        return unpack($byteOrder.$count, $this->read(4 * $count));
140
    }
141
}
142