ArrayCharacterStream.php 8.3KB

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