ArrayCharacterStream.php 7.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. <?php
  2. /*
  3. * This file is part of SwiftMailer.
  4. * (c) 2004-2009 Chris Corbyn
  5. *
  6. * For the full copyright and license information, please view the LICENSE
  7. * file that was distributed with this source code.
  8. */
  9. /**
  10. * A CharacterStream implementation which stores characters in an internal array.
  11. * @package Swift
  12. * @subpackage CharacterStream
  13. * @author Chris Corbyn
  14. */
  15. class Swift_CharacterStream_ArrayCharacterStream
  16. implements Swift_CharacterStream
  17. {
  18. /** A map of byte values and their respective characters */
  19. private static $_charMap;
  20. /** A map of characters and their derivative byte values */
  21. private static $_byteMap;
  22. /** The char reader (lazy-loaded) for the current charset */
  23. private $_charReader;
  24. /** A factory for creatiing CharacterReader instances */
  25. private $_charReaderFactory;
  26. /** The character set this stream is using */
  27. private $_charset;
  28. /** Array of characters */
  29. private $_array = array();
  30. /** Size of the array of character */
  31. private $_array_size = array();
  32. /** The current character offset in the stream */
  33. private $_offset = 0;
  34. /**
  35. * Create a new CharacterStream with the given $chars, if set.
  36. * @param Swift_CharacterReaderFactory $factory for loading validators
  37. * @param string $charset used in the stream
  38. */
  39. public function __construct(Swift_CharacterReaderFactory $factory,
  40. $charset)
  41. {
  42. self::_initializeMaps();
  43. $this->setCharacterReaderFactory($factory);
  44. $this->setCharacterSet($charset);
  45. }
  46. /**
  47. * Set the character set used in this CharacterStream.
  48. * @param string $charset
  49. */
  50. public function setCharacterSet($charset)
  51. {
  52. $this->_charset = $charset;
  53. $this->_charReader = null;
  54. }
  55. /**
  56. * Set the CharacterReaderFactory for multi charset support.
  57. * @param Swift_CharacterReaderFactory $factory
  58. */
  59. public function setCharacterReaderFactory(
  60. Swift_CharacterReaderFactory $factory)
  61. {
  62. $this->_charReaderFactory = $factory;
  63. }
  64. /**
  65. * Overwrite this character stream using the byte sequence in the byte stream.
  66. * @param Swift_OutputByteStream $os output stream to read from
  67. */
  68. public function importByteStream(Swift_OutputByteStream $os)
  69. {
  70. if (!isset($this->_charReader))
  71. {
  72. $this->_charReader = $this->_charReaderFactory
  73. ->getReaderFor($this->_charset);
  74. }
  75. $startLength = $this->_charReader->getInitialByteSize();
  76. while (false !== $bytes = $os->read($startLength))
  77. {
  78. $c = array();
  79. for ($i = 0, $len = strlen($bytes); $i < $len; ++$i)
  80. {
  81. $c[] = self::$_byteMap[$bytes[$i]];
  82. }
  83. $size = count($c);
  84. $need = $this->_charReader
  85. ->validateByteSequence($c, $size);
  86. if ($need > 0 &&
  87. false !== $bytes = $os->read($need))
  88. {
  89. for ($i = 0, $len = strlen($bytes); $i < $len; ++$i)
  90. {
  91. $c[] = self::$_byteMap[$bytes[$i]];
  92. }
  93. }
  94. $this->_array[] = $c;
  95. ++$this->_array_size;
  96. }
  97. }
  98. /**
  99. * Import a string a bytes into this CharacterStream, overwriting any existing
  100. * data in the stream.
  101. * @param string $string
  102. */
  103. public function importString($string)
  104. {
  105. $this->flushContents();
  106. $this->write($string);
  107. }
  108. /**
  109. * Read $length characters from the stream and move the internal pointer
  110. * $length further into the stream.
  111. * @param int $length
  112. * @return string
  113. */
  114. public function read($length)
  115. {
  116. if ($this->_offset == $this->_array_size)
  117. {
  118. return false;
  119. }
  120. // Don't use array slice
  121. $arrays = array();
  122. $end = $length + $this->_offset;
  123. for ($i = $this->_offset; $i < $end; ++$i)
  124. {
  125. if (!isset($this->_array[$i]))
  126. {
  127. break;
  128. }
  129. $arrays[] = $this->_array[$i];
  130. }
  131. $this->_offset += $i - $this->_offset; // Limit function calls
  132. $chars = false;
  133. foreach ($arrays as $array)
  134. {
  135. $chars .= implode('', array_map('chr', $array));
  136. }
  137. return $chars;
  138. }
  139. /**
  140. * Read $length characters from the stream and return a 1-dimensional array
  141. * containing there octet values.
  142. * @param int $length
  143. * @return int[]
  144. */
  145. public function readBytes($length)
  146. {
  147. if ($this->_offset == $this->_array_size)
  148. {
  149. return false;
  150. }
  151. $arrays = array();
  152. $end = $length + $this->_offset;
  153. for ($i = $this->_offset; $i < $end; ++$i)
  154. {
  155. if (!isset($this->_array[$i]))
  156. {
  157. break;
  158. }
  159. $arrays[] = $this->_array[$i];
  160. }
  161. $this->_offset += ($i - $this->_offset); // Limit function calls
  162. return call_user_func_array('array_merge', $arrays);
  163. }
  164. /**
  165. * Write $chars to the end of the stream.
  166. * @param string $chars
  167. */
  168. public function write($chars)
  169. {
  170. if (!isset($this->_charReader))
  171. {
  172. $this->_charReader = $this->_charReaderFactory->getReaderFor(
  173. $this->_charset);
  174. }
  175. $startLength = $this->_charReader->getInitialByteSize();
  176. $fp = fopen('php://memory', 'w+b');
  177. fwrite($fp, $chars);
  178. unset($chars);
  179. fseek($fp, 0, SEEK_SET);
  180. $buffer = array(0);
  181. $buf_pos = 1;
  182. $buf_len = 1;
  183. $has_datas = true;
  184. do
  185. {
  186. $bytes = array();
  187. // Buffer Filing
  188. if ($buf_len - $buf_pos < $startLength)
  189. {
  190. $buf = array_splice($buffer, $buf_pos);
  191. $new = $this->_reloadBuffer($fp, 100);
  192. if ($new)
  193. {
  194. $buffer = array_merge($buf, $new);
  195. $buf_len = count($buffer);
  196. $buf_pos = 0;
  197. }
  198. else
  199. {
  200. $has_datas = false;
  201. }
  202. }
  203. if ($buf_len - $buf_pos > 0)
  204. {
  205. $size = 0;
  206. for ($i = 0; $i < $startLength && isset($buffer[$buf_pos]); ++$i)
  207. {
  208. ++$size;
  209. $bytes[] = $buffer[$buf_pos++];
  210. }
  211. $need = $this->_charReader->validateByteSequence(
  212. $bytes, $size);
  213. if ($need > 0)
  214. {
  215. if ($buf_len - $buf_pos < $need)
  216. {
  217. $new = $this->_reloadBuffer($fp, $need);
  218. if ($new)
  219. {
  220. $buffer = array_merge($buffer, $new);
  221. $buf_len = count($buffer);
  222. }
  223. }
  224. for ($i = 0; $i < $need && isset($buffer[$buf_pos]); ++$i)
  225. {
  226. $bytes[] = $buffer[$buf_pos++];
  227. }
  228. }
  229. $this->_array[] = $bytes;
  230. ++$this->_array_size;
  231. }
  232. }
  233. while ($has_datas);
  234. fclose($fp);
  235. }
  236. /**
  237. * Move the internal pointer to $charOffset in the stream.
  238. * @param int $charOffset
  239. */
  240. public function setPointer($charOffset)
  241. {
  242. if ($charOffset > $this->_array_size)
  243. {
  244. $charOffset = $this->_array_size;
  245. }
  246. elseif ($charOffset < 0)
  247. {
  248. $charOffset = 0;
  249. }
  250. $this->_offset = $charOffset;
  251. }
  252. /**
  253. * Empty the stream and reset the internal pointer.
  254. */
  255. public function flushContents()
  256. {
  257. $this->_offset = 0;
  258. $this->_array = array();
  259. $this->_array_size = 0;
  260. }
  261. private function _reloadBuffer($fp, $len)
  262. {
  263. if (!feof($fp) && ($bytes = fread($fp, $len)) !== false)
  264. {
  265. $buf = array();
  266. for ($i = 0, $len = strlen($bytes); $i < $len; ++$i)
  267. {
  268. $buf[] = self::$_byteMap[$bytes[$i]];
  269. }
  270. return $buf;
  271. }
  272. return false;
  273. }
  274. private static function _initializeMaps()
  275. {
  276. if (!isset(self::$_charMap))
  277. {
  278. self::$_charMap = array();
  279. for ($byte = 0; $byte < 256; ++$byte)
  280. {
  281. self::$_charMap[$byte] = chr($byte);
  282. }
  283. self::$_byteMap = array_flip(self::$_charMap);
  284. }
  285. }
  286. }