320 lines
7.5 KiB
PHP
320 lines
7.5 KiB
PHP
<?php
|
|
|
|
/*
|
|
* This file is part of SwiftMailer.
|
|
* (c) 2004-2009 Chris Corbyn
|
|
*
|
|
* For the full copyright and license information, please view the LICENSE
|
|
* file that was distributed with this source code.
|
|
*/
|
|
|
|
//@require 'Swift/CharacterStream.php';
|
|
//@require 'Swift/OutputByteStream.php';
|
|
|
|
|
|
/**
|
|
* A CharacterStream implementation which stores characters in an internal array.
|
|
* @package Swift
|
|
* @subpackage CharacterStream
|
|
* @author Chris Corbyn
|
|
*/
|
|
class Swift_CharacterStream_ArrayCharacterStream
|
|
implements Swift_CharacterStream
|
|
{
|
|
|
|
/** A map of byte values and their respective characters */
|
|
private static $_charMap;
|
|
|
|
/** A map of characters and their derivative byte values */
|
|
private static $_byteMap;
|
|
|
|
/** The char reader (lazy-loaded) for the current charset */
|
|
private $_charReader;
|
|
|
|
/** A factory for creatiing CharacterReader instances */
|
|
private $_charReaderFactory;
|
|
|
|
/** The character set this stream is using */
|
|
private $_charset;
|
|
|
|
/** Array of characters */
|
|
private $_array = array();
|
|
|
|
/** Size of the array of character */
|
|
private $_array_size = array();
|
|
|
|
/** The current character offset in the stream */
|
|
private $_offset = 0;
|
|
|
|
/**
|
|
* Create a new CharacterStream with the given $chars, if set.
|
|
* @param Swift_CharacterReaderFactory $factory for loading validators
|
|
* @param string $charset used in the stream
|
|
*/
|
|
public function __construct(Swift_CharacterReaderFactory $factory,
|
|
$charset)
|
|
{
|
|
self::_initializeMaps();
|
|
$this->setCharacterReaderFactory($factory);
|
|
$this->setCharacterSet($charset);
|
|
}
|
|
|
|
/**
|
|
* Set the character set used in this CharacterStream.
|
|
* @param string $charset
|
|
*/
|
|
public function setCharacterSet($charset)
|
|
{
|
|
$this->_charset = $charset;
|
|
$this->_charReader = null;
|
|
}
|
|
|
|
/**
|
|
* Set the CharacterReaderFactory for multi charset support.
|
|
* @param Swift_CharacterReaderFactory $factory
|
|
*/
|
|
public function setCharacterReaderFactory(
|
|
Swift_CharacterReaderFactory $factory)
|
|
{
|
|
$this->_charReaderFactory = $factory;
|
|
}
|
|
|
|
/**
|
|
* Overwrite this character stream using the byte sequence in the byte stream.
|
|
* @param Swift_OutputByteStream $os output stream to read from
|
|
*/
|
|
public function importByteStream(Swift_OutputByteStream $os)
|
|
{
|
|
if (!isset($this->_charReader))
|
|
{
|
|
$this->_charReader = $this->_charReaderFactory
|
|
->getReaderFor($this->_charset);
|
|
}
|
|
|
|
$startLength = $this->_charReader->getInitialByteSize();
|
|
while (false !== $bytes = $os->read($startLength))
|
|
{
|
|
$c = array();
|
|
for ($i = 0, $len = strlen($bytes); $i < $len; ++$i)
|
|
{
|
|
$c[] = self::$_byteMap[$bytes[$i]];
|
|
}
|
|
$size = count($c);
|
|
$need = $this->_charReader
|
|
->validateByteSequence($c, $size);
|
|
if ($need > 0 &&
|
|
false !== $bytes = $os->read($need))
|
|
{
|
|
for ($i = 0, $len = strlen($bytes); $i < $len; ++$i)
|
|
{
|
|
$c[] = self::$_byteMap[$bytes[$i]];
|
|
}
|
|
}
|
|
$this->_array[] = $c;
|
|
++$this->_array_size;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Import a string a bytes into this CharacterStream, overwriting any existing
|
|
* data in the stream.
|
|
* @param string $string
|
|
*/
|
|
public function importString($string)
|
|
{
|
|
$this->flushContents();
|
|
$this->write($string);
|
|
}
|
|
|
|
/**
|
|
* Read $length characters from the stream and move the internal pointer
|
|
* $length further into the stream.
|
|
* @param int $length
|
|
* @return string
|
|
*/
|
|
public function read($length)
|
|
{
|
|
if ($this->_offset == $this->_array_size)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Don't use array slice
|
|
$arrays = array();
|
|
$end = $length + $this->_offset;
|
|
for ($i = $this->_offset; $i < $end; ++$i)
|
|
{
|
|
if (!isset($this->_array[$i]))
|
|
{
|
|
break;
|
|
}
|
|
$arrays[] = $this->_array[$i];
|
|
}
|
|
$this->_offset += $i - $this->_offset; // Limit function calls
|
|
$chars = false;
|
|
foreach ($arrays as $array)
|
|
{
|
|
$chars .= implode('', array_map('chr', $array));
|
|
}
|
|
return $chars;
|
|
}
|
|
|
|
/**
|
|
* Read $length characters from the stream and return a 1-dimensional array
|
|
* containing there octet values.
|
|
* @param int $length
|
|
* @return int[]
|
|
*/
|
|
public function readBytes($length)
|
|
{
|
|
if ($this->_offset == $this->_array_size)
|
|
{
|
|
return false;
|
|
}
|
|
$arrays = array();
|
|
$end = $length + $this->_offset;
|
|
for ($i = $this->_offset; $i < $end; ++$i)
|
|
{
|
|
if (!isset($this->_array[$i]))
|
|
{
|
|
break;
|
|
}
|
|
$arrays[] = $this->_array[$i];
|
|
}
|
|
$this->_offset += ($i - $this->_offset); // Limit function calls
|
|
return call_user_func_array('array_merge', $arrays);
|
|
}
|
|
|
|
/**
|
|
* Write $chars to the end of the stream.
|
|
* @param string $chars
|
|
*/
|
|
public function write($chars)
|
|
{
|
|
if (!isset($this->_charReader))
|
|
{
|
|
$this->_charReader = $this->_charReaderFactory->getReaderFor(
|
|
$this->_charset);
|
|
}
|
|
|
|
$startLength = $this->_charReader->getInitialByteSize();
|
|
|
|
$fp = fopen('php://memory', 'w+b');
|
|
fwrite($fp, $chars);
|
|
unset($chars);
|
|
fseek($fp, 0, SEEK_SET);
|
|
|
|
$buffer = array(0);
|
|
$buf_pos = 1;
|
|
$buf_len = 1;
|
|
$has_datas = true;
|
|
do
|
|
{
|
|
$bytes = array();
|
|
// Buffer Filing
|
|
if ($buf_len - $buf_pos < $startLength)
|
|
{
|
|
$buf = array_splice($buffer, $buf_pos);
|
|
$new = $this->_reloadBuffer($fp, 100);
|
|
if ($new)
|
|
{
|
|
$buffer = array_merge($buf, $new);
|
|
$buf_len = count($buffer);
|
|
$buf_pos = 0;
|
|
}
|
|
else
|
|
{
|
|
$has_datas = false;
|
|
}
|
|
}
|
|
if ($buf_len - $buf_pos > 0)
|
|
{
|
|
$size = 0;
|
|
for ($i = 0; $i < $startLength && isset($buffer[$buf_pos]); ++$i)
|
|
{
|
|
++$size;
|
|
$bytes[] = $buffer[$buf_pos++];
|
|
}
|
|
$need = $this->_charReader->validateByteSequence(
|
|
$bytes, $size);
|
|
if ($need > 0)
|
|
{
|
|
if ($buf_len - $buf_pos < $need)
|
|
{
|
|
$new = $this->_reloadBuffer($fp, $need);
|
|
|
|
if ($new)
|
|
{
|
|
$buffer = array_merge($buffer, $new);
|
|
$buf_len = count($buffer);
|
|
}
|
|
}
|
|
for ($i = 0; $i < $need && isset($buffer[$buf_pos]); ++$i)
|
|
{
|
|
$bytes[] = $buffer[$buf_pos++];
|
|
}
|
|
}
|
|
$this->_array[] = $bytes;
|
|
++$this->_array_size;
|
|
}
|
|
}
|
|
while ($has_datas);
|
|
|
|
fclose($fp);
|
|
}
|
|
|
|
/**
|
|
* Move the internal pointer to $charOffset in the stream.
|
|
* @param int $charOffset
|
|
*/
|
|
public function setPointer($charOffset)
|
|
{
|
|
if ($charOffset > $this->_array_size)
|
|
{
|
|
$charOffset = $this->_array_size;
|
|
}
|
|
elseif ($charOffset < 0)
|
|
{
|
|
$charOffset = 0;
|
|
}
|
|
$this->_offset = $charOffset;
|
|
}
|
|
|
|
/**
|
|
* Empty the stream and reset the internal pointer.
|
|
*/
|
|
public function flushContents()
|
|
{
|
|
$this->_offset = 0;
|
|
$this->_array = array();
|
|
$this->_array_size = 0;
|
|
}
|
|
|
|
private function _reloadBuffer($fp, $len)
|
|
{
|
|
if (!feof($fp) && ($bytes = fread($fp, $len)) !== false)
|
|
{
|
|
$buf = array();
|
|
for ($i = 0, $len = strlen($bytes); $i < $len; ++$i)
|
|
{
|
|
$buf[] = self::$_byteMap[$bytes[$i]];
|
|
}
|
|
return $buf;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private static function _initializeMaps()
|
|
{
|
|
if (!isset(self::$_charMap))
|
|
{
|
|
self::$_charMap = array();
|
|
for ($byte = 0; $byte < 256; ++$byte)
|
|
{
|
|
self::$_charMap[$byte] = chr($byte);
|
|
}
|
|
self::$_byteMap = array_flip(self::$_charMap);
|
|
}
|
|
}
|
|
}
|