1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14:
15: namespace Cake\I18n\Parser;
16:
17: use RuntimeException;
18:
19: 20: 21: 22: 23: 24:
25: class MoFileParser
26: {
27: 28: 29: 30: 31: 32:
33: const MO_LITTLE_ENDIAN_MAGIC = 0x950412de;
34:
35: 36: 37: 38: 39: 40:
41: const MO_BIG_ENDIAN_MAGIC = 0xde120495;
42:
43: 44: 45: 46: 47:
48: const MO_HEADER_SIZE = 28;
49:
50: 51: 52: 53: 54: 55: 56: 57: 58:
59: public function parse($resource)
60: {
61: $stream = fopen($resource, 'rb');
62:
63: $stat = fstat($stream);
64:
65: if ($stat['size'] < self::MO_HEADER_SIZE) {
66: throw new RuntimeException('Invalid format for MO translations file');
67: }
68: $magic = unpack('V1', fread($stream, 4));
69: $magic = hexdec(substr(dechex(current($magic)), -8));
70:
71: if ($magic == self::MO_LITTLE_ENDIAN_MAGIC) {
72: $isBigEndian = false;
73: } elseif ($magic == self::MO_BIG_ENDIAN_MAGIC) {
74: $isBigEndian = true;
75: } else {
76: throw new RuntimeException('Invalid format for MO translations file');
77: }
78:
79:
80: fread($stream, 4);
81:
82: $count = $this->_readLong($stream, $isBigEndian);
83: $offsetId = $this->_readLong($stream, $isBigEndian);
84: $offsetTranslated = $this->_readLong($stream, $isBigEndian);
85:
86:
87: fread($stream, 8);
88: $messages = [];
89:
90: for ($i = 0; $i < $count; $i++) {
91: $pluralId = null;
92: $context = null;
93: $plurals = null;
94:
95: fseek($stream, $offsetId + $i * 8);
96:
97: $length = $this->_readLong($stream, $isBigEndian);
98: $offset = $this->_readLong($stream, $isBigEndian);
99:
100: if ($length < 1) {
101: continue;
102: }
103:
104: fseek($stream, $offset);
105: $singularId = fread($stream, $length);
106:
107: if (strpos($singularId, "\x04") !== false) {
108: list($context, $singularId) = explode("\x04", $singularId);
109: }
110:
111: if (strpos($singularId, "\000") !== false) {
112: list($singularId, $pluralId) = explode("\000", $singularId);
113: }
114:
115: fseek($stream, $offsetTranslated + $i * 8);
116: $length = $this->_readLong($stream, $isBigEndian);
117: $offset = $this->_readLong($stream, $isBigEndian);
118: fseek($stream, $offset);
119: $translated = fread($stream, $length);
120:
121: if ($pluralId !== null || strpos($translated, "\000") !== false) {
122: $translated = explode("\000", $translated);
123: $plurals = $pluralId !== null ? array_map('stripcslashes', $translated) : null;
124: $translated = $translated[0];
125: }
126:
127: $singular = stripcslashes($translated);
128: if ($context !== null) {
129: $messages[$singularId]['_context'][$context] = $singular;
130: if ($pluralId !== null) {
131: $messages[$pluralId]['_context'][$context] = $plurals;
132: }
133: continue;
134: }
135:
136: $messages[$singularId]['_context'][''] = $singular;
137: if ($pluralId !== null) {
138: $messages[$pluralId]['_context'][''] = $plurals;
139: }
140: }
141:
142: fclose($stream);
143:
144: return $messages;
145: }
146:
147: 148: 149: 150: 151: 152: 153:
154: protected function _readLong($stream, $isBigEndian)
155: {
156: $result = unpack($isBigEndian ? 'N1' : 'V1', fread($stream, 4));
157: $result = current($result);
158:
159: return (int)substr($result, -8);
160: }
161: }
162: