PageRenderTime 47ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/yii/framework/i18n/gettext/CGettextMoFile.php

https://bitbucket.org/mstowe/zurmo
PHP | 273 lines | 134 code | 31 blank | 108 comment | 20 complexity | 3197ea9126a31dcae282f93b9467e920 MD5 | raw file
Possible License(s): GPL-3.0, BSD-3-Clause, LGPL-3.0, LGPL-2.1, BSD-2-Clause
  1. <?php
  2. /**
  3. * CGettextMoFile class file.
  4. *
  5. * @author Qiang Xue <qiang.xue@gmail.com>
  6. * @link http://www.yiiframework.com/
  7. * @copyright Copyright &copy; 2008-2011 Yii Software LLC
  8. * @license http://www.yiiframework.com/license/
  9. */
  10. /**
  11. * CGettextMoFile represents an MO Gettext message file.
  12. *
  13. * This class is written by adapting Michael's Gettext_MO class in PEAR.
  14. * Please refer to the following license terms.
  15. *
  16. * Copyright (c) 2004-2005, Michael Wallner <mike@iworks.at>.
  17. * All rights reserved.
  18. *
  19. * Redistribution and use in source and binary forms, with or without
  20. * modification, are permitted provided that the following conditions are met:
  21. *
  22. * * Redistributions of source code must retain the above copyright notice,
  23. * this list of conditions and the following disclaimer.
  24. * * Redistributions in binary form must reproduce the above copyright
  25. * notice, this list of conditions and the following disclaimer in the
  26. * documentation and/or other materials provided with the distribution.
  27. *
  28. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  29. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  30. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  31. * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
  32. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  33. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  34. * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  35. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  36. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  37. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  38. *
  39. * @author Qiang Xue <qiang.xue@gmail.com>
  40. * @version $Id$
  41. * @package system.i18n.gettext
  42. * @since 1.0
  43. */
  44. class CGettextMoFile extends CGettextFile
  45. {
  46. /**
  47. * @var boolean whether to use Big Endian when reading and writing an integer.
  48. */
  49. public $useBigEndian=false;
  50. /**
  51. * Constructor.
  52. * @param boolean $useBigEndian whether to use Big Endian when reading and writing an integer.
  53. */
  54. public function __construct($useBigEndian=false)
  55. {
  56. $this->useBigEndian=$useBigEndian;
  57. }
  58. /**
  59. * Loads messages from an MO file.
  60. * @param string $file file path
  61. * @param string $context message context
  62. * @return array message translations (source message => translated message)
  63. */
  64. public function load($file,$context)
  65. {
  66. if(!($fr=@fopen($file,'rb')))
  67. throw new CException(Yii::t('yii','Unable to read file "{file}".',
  68. array('{file}'=>$file)));
  69. if(!@flock($fr,LOCK_SH))
  70. throw new CException(Yii::t('yii','Unable to lock file "{file}" for reading.',
  71. array('{file}'=>$file)));
  72. $magic=current($array=unpack('c',$this->readByte($fr,4)));
  73. if($magic==-34)
  74. $this->useBigEndian=false;
  75. else if($magic==-107)
  76. $this->useBigEndian=true;
  77. else
  78. throw new CException(Yii::t('yii','Invalid MO file: {file} (magic: {magic}).',
  79. array('{file}'=>$file,'{magic}'=>$magic)));
  80. if(($revision=$this->readInteger($fr))!=0)
  81. throw new CException(Yii::t('yii','Invalid MO file revision: {revision}.',
  82. array('{revision}'=>$revision)));
  83. $count=$this->readInteger($fr);
  84. $sourceOffset=$this->readInteger($fr);
  85. $targetOffset=$this->readInteger($fr);
  86. $sourceLengths=array();
  87. $sourceOffsets=array();
  88. fseek($fr,$sourceOffset);
  89. for($i=0;$i<$count;++$i)
  90. {
  91. $sourceLengths[]=$this->readInteger($fr);
  92. $sourceOffsets[]=$this->readInteger($fr);
  93. }
  94. $targetLengths=array();
  95. $targetOffsets=array();
  96. fseek($fr,$targetOffset);
  97. for($i=0;$i<$count;++$i)
  98. {
  99. $targetLengths[]=$this->readInteger($fr);
  100. $targetOffsets[]=$this->readInteger($fr);
  101. }
  102. $messages=array();
  103. for($i=0;$i<$count;++$i)
  104. {
  105. $id=$this->readString($fr,$sourceLengths[$i],$sourceOffsets[$i]);
  106. $pos = strpos($id,chr(4));
  107. if(($context && $pos!==false && substr($id,0,$pos)===$context) || (!$context && $pos===false))
  108. {
  109. if($pos !== false)
  110. $id=substr($id,$pos+1);
  111. $message=$this->readString($fr,$targetLengths[$i],$targetOffsets[$i]);
  112. $messages[$id]=$message;
  113. }
  114. }
  115. @flock($fr,LOCK_UN);
  116. @fclose($fr);
  117. return $messages;
  118. }
  119. /**
  120. * Saves messages to an MO file.
  121. * @param string $file file path
  122. * @param array $messages message translations (message id => translated message).
  123. * Note if the message has a context, the message id must be prefixed with
  124. * the context with chr(4) as the separator.
  125. */
  126. public function save($file,$messages)
  127. {
  128. if(!($fw=@fopen($file,'wb')))
  129. throw new CException(Yii::t('yii','Unable to write file "{file}".',
  130. array('{file}'=>$file)));
  131. if(!@flock($fw,LOCK_EX))
  132. throw new CException(Yii::t('yii','Unable to lock file "{file}" for writing.',
  133. array('{file}'=>$file)));
  134. // magic
  135. if($this->useBigEndian)
  136. $this->writeByte($fw,pack('c*', 0x95, 0x04, 0x12, 0xde));
  137. else
  138. $this->writeByte($fw,pack('c*', 0xde, 0x12, 0x04, 0x95));
  139. // revision
  140. $this->writeInteger($fw,0);
  141. // message count
  142. $n=count($messages);
  143. $this->writeInteger($fw,$n);
  144. // offset of source message table
  145. $offset=28;
  146. $this->writeInteger($fw,$offset);
  147. $offset+=($n*8);
  148. $this->writeInteger($fw,$offset);
  149. // hashtable size, omitted
  150. $this->writeInteger($fw,0);
  151. $offset+=($n*8);
  152. $this->writeInteger($fw,$offset);
  153. // length and offsets for source messagess
  154. foreach(array_keys($messages) as $id)
  155. {
  156. $len=strlen($id);
  157. $this->writeInteger($fw,$len);
  158. $this->writeInteger($fw,$offset);
  159. $offset+=$len+1;
  160. }
  161. // length and offsets for target messagess
  162. foreach($messages as $message)
  163. {
  164. $len=strlen($message);
  165. $this->writeInteger($fw,$len);
  166. $this->writeInteger($fw,$offset);
  167. $offset+=$len+1;
  168. }
  169. // source messages
  170. foreach(array_keys($messages) as $id)
  171. $this->writeString($fw,$id);
  172. // target messages
  173. foreach($messages as $message)
  174. $this->writeString($fw,$message);
  175. @flock($fw,LOCK_UN);
  176. @fclose($fw);
  177. }
  178. /**
  179. * Reads one or several bytes.
  180. * @param resource $fr file handle
  181. * @param integer $n number of bytes to read
  182. * @return string bytes
  183. */
  184. protected function readByte($fr,$n=1)
  185. {
  186. if($n>0)
  187. return fread($fr,$n);
  188. }
  189. /**
  190. * Writes bytes.
  191. * @param resource $fw file handle
  192. * @param string $data the data
  193. * @return integer how many bytes are written
  194. */
  195. protected function writeByte($fw,$data)
  196. {
  197. return fwrite($fw,$data);
  198. }
  199. /**
  200. * Reads a 4-byte integer.
  201. * @param resource $fr file handle
  202. * @return integer the result
  203. * @see useBigEndian
  204. */
  205. protected function readInteger($fr)
  206. {
  207. return current($array=unpack($this->useBigEndian ? 'N' : 'V', $this->readByte($fr,4)));
  208. }
  209. /**
  210. * Writes a 4-byte integer.
  211. * @param resource $fw file handle
  212. * @param integer $data the data
  213. * @return integer how many bytes are written
  214. */
  215. protected function writeInteger($fw,$data)
  216. {
  217. return $this->writeByte($fw,pack($this->useBigEndian ? 'N' : 'V', (int)$data));
  218. }
  219. /**
  220. * Reads a string.
  221. * @param resource $fr file handle
  222. * @param integer $length string length
  223. * @param integer $offset offset of the string in the file. If null, it reads from the current position.
  224. * @return string the result
  225. */
  226. protected function readString($fr,$length,$offset=null)
  227. {
  228. if($offset!==null)
  229. fseek($fr,$offset);
  230. return $this->readByte($fr,$length);
  231. }
  232. /**
  233. * Writes a string.
  234. * @param resource $fw file handle
  235. * @param string $data the string
  236. * @return integer how many bytes are written
  237. */
  238. protected function writeString($fw,$data)
  239. {
  240. return $this->writeByte($fw,$data."\0");
  241. }
  242. }