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