Chris@0: Chris@0: * Chris@0: * For the full copyright and license information, please view the LICENSE Chris@0: * file that was distributed with this source code. Chris@0: */ Chris@0: Chris@0: namespace Symfony\Component\Translation\Loader; Chris@0: Chris@0: use Symfony\Component\Translation\Exception\InvalidResourceException; Chris@0: Chris@0: /** Chris@0: * @copyright Copyright (c) 2010, Union of RAD http://union-of-rad.org (http://lithify.me/) Chris@0: */ Chris@0: class MoFileLoader extends FileLoader Chris@0: { Chris@0: /** Chris@0: * Magic used for validating the format of a MO file as well as Chris@0: * detecting if the machine used to create that file was little endian. Chris@0: */ Chris@0: const MO_LITTLE_ENDIAN_MAGIC = 0x950412de; Chris@0: Chris@0: /** Chris@0: * Magic used for validating the format of a MO file as well as Chris@0: * detecting if the machine used to create that file was big endian. Chris@0: */ Chris@0: const MO_BIG_ENDIAN_MAGIC = 0xde120495; Chris@0: Chris@0: /** Chris@0: * The size of the header of a MO file in bytes. Chris@0: */ Chris@0: const MO_HEADER_SIZE = 28; Chris@0: Chris@0: /** Chris@0: * Parses machine object (MO) format, independent of the machine's endian it Chris@0: * was created on. Both 32bit and 64bit systems are supported. Chris@0: * Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: protected function loadResource($resource) Chris@0: { Chris@0: $stream = fopen($resource, 'r'); Chris@0: Chris@0: $stat = fstat($stream); Chris@0: Chris@0: if ($stat['size'] < self::MO_HEADER_SIZE) { Chris@0: throw new InvalidResourceException('MO stream content has an invalid format.'); Chris@0: } Chris@0: $magic = unpack('V1', fread($stream, 4)); Chris@0: $magic = hexdec(substr(dechex(current($magic)), -8)); Chris@0: Chris@14: if (self::MO_LITTLE_ENDIAN_MAGIC == $magic) { Chris@0: $isBigEndian = false; Chris@14: } elseif (self::MO_BIG_ENDIAN_MAGIC == $magic) { Chris@0: $isBigEndian = true; Chris@0: } else { Chris@0: throw new InvalidResourceException('MO stream content has an invalid format.'); Chris@0: } Chris@0: Chris@0: // formatRevision Chris@0: $this->readLong($stream, $isBigEndian); Chris@0: $count = $this->readLong($stream, $isBigEndian); Chris@0: $offsetId = $this->readLong($stream, $isBigEndian); Chris@0: $offsetTranslated = $this->readLong($stream, $isBigEndian); Chris@0: // sizeHashes Chris@0: $this->readLong($stream, $isBigEndian); Chris@0: // offsetHashes Chris@0: $this->readLong($stream, $isBigEndian); Chris@0: Chris@17: $messages = []; Chris@0: Chris@0: for ($i = 0; $i < $count; ++$i) { Chris@0: $pluralId = null; Chris@0: $translated = null; Chris@0: Chris@0: fseek($stream, $offsetId + $i * 8); Chris@0: Chris@0: $length = $this->readLong($stream, $isBigEndian); Chris@0: $offset = $this->readLong($stream, $isBigEndian); Chris@0: Chris@0: if ($length < 1) { Chris@0: continue; Chris@0: } Chris@0: Chris@0: fseek($stream, $offset); Chris@0: $singularId = fread($stream, $length); Chris@0: Chris@14: if (false !== strpos($singularId, "\000")) { Chris@0: list($singularId, $pluralId) = explode("\000", $singularId); Chris@0: } Chris@0: Chris@0: fseek($stream, $offsetTranslated + $i * 8); Chris@0: $length = $this->readLong($stream, $isBigEndian); Chris@0: $offset = $this->readLong($stream, $isBigEndian); Chris@0: Chris@0: if ($length < 1) { Chris@0: continue; Chris@0: } Chris@0: Chris@0: fseek($stream, $offset); Chris@0: $translated = fread($stream, $length); Chris@0: Chris@14: if (false !== strpos($translated, "\000")) { Chris@0: $translated = explode("\000", $translated); Chris@0: } Chris@0: Chris@17: $ids = ['singular' => $singularId, 'plural' => $pluralId]; Chris@0: $item = compact('ids', 'translated'); Chris@0: Chris@17: if (\is_array($item['translated'])) { Chris@0: $messages[$item['ids']['singular']] = stripcslashes($item['translated'][0]); Chris@0: if (isset($item['ids']['plural'])) { Chris@17: $plurals = []; Chris@0: foreach ($item['translated'] as $plural => $translated) { Chris@0: $plurals[] = sprintf('{%d} %s', $plural, $translated); Chris@0: } Chris@0: $messages[$item['ids']['plural']] = stripcslashes(implode('|', $plurals)); Chris@0: } Chris@0: } elseif (!empty($item['ids']['singular'])) { Chris@0: $messages[$item['ids']['singular']] = stripcslashes($item['translated']); Chris@0: } Chris@0: } Chris@0: Chris@0: fclose($stream); Chris@0: Chris@0: return array_filter($messages); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Reads an unsigned long from stream respecting endianness. Chris@0: * Chris@0: * @param resource $stream Chris@0: * @param bool $isBigEndian Chris@0: * Chris@0: * @return int Chris@0: */ Chris@0: private function readLong($stream, $isBigEndian) Chris@0: { Chris@0: $result = unpack($isBigEndian ? 'N1' : 'V1', fread($stream, 4)); Chris@0: $result = current($result); Chris@0: Chris@0: return (int) substr($result, -8); Chris@0: } Chris@0: }