PageRenderTime 49ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/demo/yii/i18n/gettext/CGettextMoFile.php

https://bitbucket.org/tof06/yii-bootstrap
PHP | 269 lines | 132 code | 29 blank | 108 comment | 16 complexity | 5293060f455d5dabd361ec12c71273a7 MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-2.1, BSD-2-Clause, Apache-2.0
  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: CGettextMoFile.php 2798 2011-01-01 19:29:03Z qiang.xue $
  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. if(($pos=strpos($id,chr(4)))!==false && substr($id,0,$pos)===$context)
  107. {
  108. $id=substr($id,$pos+1);
  109. $message=$this->readString($fr,$targetLengths[$i],$targetOffsets[$i]);
  110. $messages[$id]=$message;
  111. }
  112. }
  113. @flock($fr,LOCK_UN);
  114. @fclose($fr);
  115. return $messages;
  116. }
  117. /**
  118. * Saves messages to an MO file.
  119. * @param string $file file path
  120. * @param array $messages message translations (message id => translated message).
  121. * Note if the message has a context, the message id must be prefixed with
  122. * the context with chr(4) as the separator.
  123. */
  124. public function save($file,$messages)
  125. {
  126. if(!($fw=@fopen($file,'wb')))
  127. throw new CException(Yii::t('yii','Unable to write file "{file}".',
  128. array('{file}'=>$file)));
  129. if(!@flock($fw,LOCK_EX))
  130. throw new CException(Yii::t('yii','Unable to lock file "{file}" for writing.',
  131. array('{file}'=>$file)));
  132. // magic
  133. if($this->useBigEndian)
  134. $this->writeByte($fw,pack('c*', 0x95, 0x04, 0x12, 0xde));
  135. else
  136. $this->writeByte($fw,pack('c*', 0xde, 0x12, 0x04, 0x95));
  137. // revision
  138. $this->writeInteger($fw,0);
  139. // message count
  140. $n=count($messages);
  141. $this->writeInteger($fw,$n);
  142. // offset of source message table
  143. $offset=28;
  144. $this->writeInteger($fw,$offset);
  145. $offset+=($n*8);
  146. $this->writeInteger($fw,$offset);
  147. // hashtable size, omitted
  148. $this->writeInteger($fw,0);
  149. $offset+=($n*8);
  150. $this->writeInteger($fw,$offset);
  151. // length and offsets for source messagess
  152. foreach(array_keys($messages) as $id)
  153. {
  154. $len=strlen($id);
  155. $this->writeInteger($fw,$len);
  156. $this->writeInteger($fw,$offset);
  157. $offset+=$len+1;
  158. }
  159. // length and offsets for target messagess
  160. foreach($messages as $message)
  161. {
  162. $len=strlen($message);
  163. $this->writeInteger($fw,$len);
  164. $this->writeInteger($fw,$offset);
  165. $offset+=$len+1;
  166. }
  167. // source messages
  168. foreach(array_keys($messages) as $id)
  169. $this->writeString($fw,$id);
  170. // target messages
  171. foreach($messages as $message)
  172. $this->writeString($fw,$message);
  173. @flock($fw,LOCK_UN);
  174. @fclose($fw);
  175. }
  176. /**
  177. * Reads one or several bytes.
  178. * @param resource $fr file handle
  179. * @param integer $n number of bytes to read
  180. * @return string bytes
  181. */
  182. protected function readByte($fr,$n=1)
  183. {
  184. if($n>0)
  185. return fread($fr,$n);
  186. }
  187. /**
  188. * Writes bytes.
  189. * @param resource $fw file handle
  190. * @param string $data the data
  191. * @return integer how many bytes are written
  192. */
  193. protected function writeByte($fw,$data)
  194. {
  195. return fwrite($fw,$data);
  196. }
  197. /**
  198. * Reads a 4-byte integer.
  199. * @param resource $fr file handle
  200. * @return integer the result
  201. * @see useBigEndian
  202. */
  203. protected function readInteger($fr)
  204. {
  205. return current($array=unpack($this->useBigEndian ? 'N' : 'V', $this->readByte($fr,4)));
  206. }
  207. /**
  208. * Writes a 4-byte integer.
  209. * @param resource $fw file handle
  210. * @param integer $data the data
  211. * @return integer how many bytes are written
  212. */
  213. protected function writeInteger($fw,$data)
  214. {
  215. return $this->writeByte($fw,pack($this->useBigEndian ? 'N' : 'V', (int)$data));
  216. }
  217. /**
  218. * Reads a string.
  219. * @param resource $fr file handle
  220. * @param integer $length string length
  221. * @param integer $offset offset of the string in the file. If null, it reads from the current position.
  222. * @return string the result
  223. */
  224. protected function readString($fr,$length,$offset=null)
  225. {
  226. if($offset!==null)
  227. fseek($fr,$offset);
  228. return $this->readByte($fr,$length);
  229. }
  230. /**
  231. * Writes a string.
  232. * @param resource $fw file handle
  233. * @param string $data the string
  234. * @return integer how many bytes are written
  235. */
  236. protected function writeString($fw,$data)
  237. {
  238. return $this->writeByte($fw,$data."\0");
  239. }
  240. }