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