ArrayCharacterStream.php 8.4KB

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