TYPO3 API  SVNRelease
ArrayCharacterStream.php
Go to the documentation of this file.
00001 <?php
00002 
00003 /*
00004  * This file is part of SwiftMailer.
00005  * (c) 2004-2009 Chris Corbyn
00006  *
00007  * For the full copyright and license information, please view the LICENSE
00008  * file that was distributed with this source code.
00009  */
00010 
00011 //@require 'Swift/CharacterStream.php';
00012 //@require 'Swift/OutputByteStream.php';
00013 
00014 
00015 /**
00016  * A CharacterStream implementation which stores characters in an internal array.
00017  * @package Swift
00018  * @subpackage CharacterStream
00019  * @author Chris Corbyn
00020  */
00021 class Swift_CharacterStream_ArrayCharacterStream
00022   implements Swift_CharacterStream
00023 {
00024 
00025   /** A map of byte values and their respective characters */
00026   private static $_charMap;
00027 
00028   /** A map of characters and their derivative byte values */
00029   private static $_byteMap;
00030 
00031   /** The char reader (lazy-loaded) for the current charset */
00032   private $_charReader;
00033 
00034   /** A factory for creatiing CharacterReader instances */
00035   private $_charReaderFactory;
00036 
00037   /** The character set this stream is using */
00038   private $_charset;
00039 
00040   /** Array of characters */
00041   private $_array = array();
00042 
00043   /** Size of the array of character */
00044   private $_array_size = array();
00045 
00046   /** The current character offset in the stream */
00047   private $_offset = 0;
00048 
00049   /**
00050    * Create a new CharacterStream with the given $chars, if set.
00051    * @param Swift_CharacterReaderFactory $factory for loading validators
00052    * @param string $charset used in the stream
00053    */
00054   public function __construct(Swift_CharacterReaderFactory $factory,
00055     $charset)
00056   {
00057     self::_initializeMaps();
00058     $this->setCharacterReaderFactory($factory);
00059     $this->setCharacterSet($charset);
00060   }
00061 
00062   /**
00063    * Set the character set used in this CharacterStream.
00064    * @param string $charset
00065    */
00066   public function setCharacterSet($charset)
00067   {
00068     $this->_charset = $charset;
00069     $this->_charReader = null;
00070   }
00071 
00072   /**
00073    * Set the CharacterReaderFactory for multi charset support.
00074    * @param Swift_CharacterReaderFactory $factory
00075    */
00076   public function setCharacterReaderFactory(
00077     Swift_CharacterReaderFactory $factory)
00078   {
00079     $this->_charReaderFactory = $factory;
00080   }
00081 
00082   /**
00083    * Overwrite this character stream using the byte sequence in the byte stream.
00084    * @param Swift_OutputByteStream $os output stream to read from
00085    */
00086   public function importByteStream(Swift_OutputByteStream $os)
00087   {
00088     if (!isset($this->_charReader))
00089     {
00090       $this->_charReader = $this->_charReaderFactory
00091         ->getReaderFor($this->_charset);
00092     }
00093 
00094     $startLength = $this->_charReader->getInitialByteSize();
00095     while (false !== $bytes = $os->read($startLength))
00096     {
00097       $c = array();
00098       for ($i = 0, $len = strlen($bytes); $i < $len; ++$i)
00099       {
00100         $c[] = self::$_byteMap[$bytes[$i]];
00101       }
00102       $size = count($c);
00103       $need = $this->_charReader
00104         ->validateByteSequence($c, $size);
00105       if ($need > 0 &&
00106         false !== $bytes = $os->read($need))
00107       {
00108         for ($i = 0, $len = strlen($bytes); $i < $len; ++$i)
00109         {
00110           $c[] = self::$_byteMap[$bytes[$i]];
00111         }
00112       }
00113       $this->_array[] = $c;
00114       ++$this->_array_size;
00115     }
00116   }
00117 
00118   /**
00119    * Import a string a bytes into this CharacterStream, overwriting any existing
00120    * data in the stream.
00121    * @param string $string
00122    */
00123   public function importString($string)
00124   {
00125     $this->flushContents();
00126     $this->write($string);
00127   }
00128 
00129   /**
00130    * Read $length characters from the stream and move the internal pointer
00131    * $length further into the stream.
00132    * @param int $length
00133    * @return string
00134    */
00135   public function read($length)
00136   {
00137     if ($this->_offset == $this->_array_size)
00138     {
00139       return false;
00140     }
00141 
00142     // Don't use array slice
00143     $arrays = array();
00144     $end = $length + $this->_offset;
00145     for ($i = $this->_offset; $i < $end; ++$i)
00146     {
00147       if (!isset($this->_array[$i]))
00148       {
00149         break;
00150       }
00151       $arrays[] = $this->_array[$i];
00152     }
00153     $this->_offset += $i - $this->_offset; // Limit function calls
00154     $chars = false;
00155     foreach ($arrays as $array)
00156     {
00157       $chars .= implode('', array_map('chr', $array));
00158     }
00159     return $chars;
00160   }
00161 
00162   /**
00163    * Read $length characters from the stream and return a 1-dimensional array
00164    * containing there octet values.
00165    * @param int $length
00166    * @return int[]
00167    */
00168   public function readBytes($length)
00169   {
00170     if ($this->_offset == $this->_array_size)
00171     {
00172       return false;
00173     }
00174     $arrays = array();
00175     $end = $length + $this->_offset;
00176     for ($i = $this->_offset; $i < $end; ++$i)
00177     {
00178       if (!isset($this->_array[$i]))
00179       {
00180         break;
00181       }
00182       $arrays[] = $this->_array[$i];
00183     }
00184     $this->_offset += ($i - $this->_offset); // Limit function calls
00185     return call_user_func_array('array_merge', $arrays);
00186   }
00187 
00188   /**
00189    * Write $chars to the end of the stream.
00190    * @param string $chars
00191    */
00192   public function write($chars)
00193   {
00194     if (!isset($this->_charReader))
00195     {
00196       $this->_charReader = $this->_charReaderFactory->getReaderFor(
00197         $this->_charset);
00198     }
00199 
00200     $startLength = $this->_charReader->getInitialByteSize();
00201 
00202     $fp = fopen('php://memory', 'w+b');
00203     fwrite($fp, $chars);
00204     unset($chars);
00205     fseek($fp, 0, SEEK_SET);
00206 
00207     $buffer = array(0);
00208     $buf_pos = 1;
00209     $buf_len = 1;
00210     $has_datas = true;
00211     do
00212     {
00213       $bytes = array();
00214       // Buffer Filing
00215       if ($buf_len - $buf_pos < $startLength)
00216       {
00217         $buf = array_splice($buffer, $buf_pos);
00218         $new = $this->_reloadBuffer($fp, 100);
00219         if ($new)
00220         {
00221           $buffer = array_merge($buf, $new);
00222           $buf_len = count($buffer);
00223           $buf_pos = 0;
00224         }
00225         else
00226         {
00227           $has_datas = false;
00228         }
00229       }
00230       if ($buf_len - $buf_pos > 0)
00231       {
00232         $size = 0;
00233         for ($i = 0; $i < $startLength && isset($buffer[$buf_pos]); ++$i)
00234         {
00235           ++$size;
00236           $bytes[] = $buffer[$buf_pos++];
00237         }
00238         $need = $this->_charReader->validateByteSequence(
00239           $bytes, $size);
00240         if ($need > 0)
00241         {
00242           if ($buf_len - $buf_pos < $need)
00243           {
00244             $new = $this->_reloadBuffer($fp, $need);
00245 
00246             if ($new)
00247             {
00248               $buffer = array_merge($buffer, $new);
00249               $buf_len = count($buffer);
00250             }
00251           }
00252           for ($i = 0; $i < $need && isset($buffer[$buf_pos]); ++$i)
00253           {
00254             $bytes[] = $buffer[$buf_pos++];
00255           }
00256         }
00257         $this->_array[] = $bytes;
00258         ++$this->_array_size;
00259       }
00260     }
00261     while ($has_datas);
00262 
00263     fclose($fp);
00264   }
00265 
00266   /**
00267    * Move the internal pointer to $charOffset in the stream.
00268    * @param int $charOffset
00269    */
00270   public function setPointer($charOffset)
00271   {
00272     if ($charOffset > $this->_array_size)
00273     {
00274       $charOffset = $this->_array_size;
00275     }
00276     elseif ($charOffset < 0)
00277     {
00278       $charOffset = 0;
00279     }
00280     $this->_offset = $charOffset;
00281   }
00282 
00283   /**
00284    * Empty the stream and reset the internal pointer.
00285    */
00286   public function flushContents()
00287   {
00288     $this->_offset = 0;
00289     $this->_array = array();
00290     $this->_array_size = 0;
00291   }
00292 
00293   private function _reloadBuffer($fp, $len)
00294   {
00295     if (!feof($fp) && ($bytes = fread($fp, $len)) !== false)
00296     {
00297       $buf = array();
00298       for ($i = 0, $len = strlen($bytes); $i < $len; ++$i)
00299       {
00300         $buf[] = self::$_byteMap[$bytes[$i]];
00301       }
00302       return $buf;
00303     }
00304     return false;
00305   }
00306 
00307   private static function _initializeMaps()
00308   {
00309     if (!isset(self::$_charMap))
00310     {
00311       self::$_charMap = array();
00312       for ($byte = 0; $byte < 256; ++$byte)
00313       {
00314         self::$_charMap[$byte] = chr($byte);
00315       }
00316       self::$_byteMap = array_flip(self::$_charMap);
00317     }
00318   }
00319 }