* @author Ryan Butterfield * @copyright Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License * @version $Id$ */ class Zend_Io_Writer { const MACHINE_ENDIAN_ORDER = 0; const LITTLE_ENDIAN_ORDER = 1; const BIG_ENDIAN_ORDER = 2; /** * The endianess of the current machine. * * @var integer */ private static $_endianess = 0; /** * The resource identifier of the stream. * * @var resource */ protected $_fd = null; /** * Size of the underlying stream. * * @var integer */ protected $_size = 0; /** * Constructs the Zend_Io_Writer class with given open file descriptor. * * @param resource $fd The file descriptor. * @throws Zend_Io_Exception if file descriptor is not valid */ public function __construct($fd) { if (!is_resource($fd) || !in_array(get_resource_type($fd), array('stream'))) { require_once('Zend/Io/Exception.php'); throw new Zend_Io_Exception ('Invalid resource type (only resources of type stream are supported)'); } $this->_fd = $fd; $offset = $this->getOffset(); fseek($this->_fd, 0, SEEK_END); $this->_size = ftell($this->_fd); fseek($this->_fd, $offset); } /** * Default destructor. */ public function __destruct() {} /** * Returns the current point of operation. * * @return integer * @throws Zend_Io_Exception if the stream is closed */ public function getOffset() { if ($this->_fd === null) { require_once('Zend/Io/Exception.php'); throw new Zend_Io_Exception('Cannot operate on a closed stream'); } return ftell($this->_fd); } /** * Sets the point of operation, ie the cursor offset value. The offset may * also be set to a negative value when it is interpreted as an offset from * the end of the stream instead of the beginning. * * @param integer $offset The new point of operation. * @return void * @throws Zend_Io_Exception if the stream is closed */ public function setOffset($offset) { if ($this->_fd === null) { require_once('Zend/Io/Exception.php'); throw new Zend_Io_Exception('Cannot operate on a closed stream'); } fseek($this->_fd, $offset < 0 ? $this->getSize() + $offset : $offset); } /** * Returns the stream size in bytes. * * @return integer * @throws Zend_Io_Exception if the stream is closed */ public function getSize() { if ($this->_fd === null) { require_once('Zend/Io/Exception.php'); throw new Zend_Io_Exception('Cannot operate on a closed stream'); } return $this->_size; } /** * Sets the stream size in bytes, and truncates if required. * * @param integer $size The new size * @return void * @throws Zend_Io_Exception if the stream is closed */ public function setSize($size) { if ($this->_fd === null) { require_once('Zend/Io/Exception.php'); throw new Zend_Io_Exception('Cannot operate on a closed stream'); } ftruncate($this->_fd, $size); } /** * Returns the underlying stream file descriptor. * * @return resource */ public function getFileDescriptor() { return $this->_fd; } /** * Writes value up to length bytes to the stream. * * @param string $value The value to write to the stream. * @param integer $length The number of bytes to write. Defaults to the * length of the given value. * @return Zend_Io_Writer * @throws Zend_Io_Exception if the stream is closed */ public function write($value, $length = null) { if ($this->_fd === null) { require_once('Zend/Io/Exception.php'); throw new Zend_Io_Exception('Cannot operate on a closed stream'); } if ($length === null) { $length = strlen($value); } fwrite($this->_fd, $value, $length); $this->_size += $length; return $this; } /** * Writes an 8-bit integer as binary data to the stream. * * @param integer $value The input value. * @return Zend_Io_Writer * @throws Zend_Io_Exception if the stream is closed */ public final function writeInt8($value) { return $this->write(pack('c*', $value)); } /** * Writes an unsigned 8-bit integer as binary data to the stream. * * @param integer $value The input value. * @return Zend_Io_Writer * @throws Zend_Io_Exception if the stream is closed */ public final function writeUInt8($value) { return $this->write(pack('C*', $value)); } /** * Returns signed 16-bit integer as machine endian ordered binary data. * * @param integer $value The input value. * @return string */ private function _toInt16($value) { return pack('s*', $value); } /** * Writes a signed 16-bit integer as little-endian ordered binary data to * the stream. * * @param integer $value The input value. * @return Zend_Io_Writer * @throws Zend_Io_Exception if the stream is closed */ public final function writeInt16LE($value) { if ($this->_isLittleEndian()) { return $this->write(strrev($this->_toInt16($value))); } else { return $this->write($this->_toInt16($value)); } } /** * Returns signed 16-bit integer as big-endian ordered binary data to the * stream. * * @param integer $value The input value. * @return Zend_Io_Writer * @throws Zend_Io_Exception if the stream is closed */ public final function writeInt16BE($value) { if ($this->_isBigEndian()) { return $this->write(strrev($this->_toInt16($value))); } else { return $this->write($this->_toInt16($value)); } } /** * Writes unsigned 16-bit integer as little-endian ordered binary data * to the stream. * * @param integer $value The input value. * @return Zend_Io_Writer * @throws Zend_Io_Exception if the stream is closed */ public final function writeUInt16LE($value) { return $this->write(pack('v*', $value)); } /** * Writes unsigned 16-bit integer as big-endian ordered binary data to the * stream. * * @param integer $value The input value. * @return Zend_Io_Writer * @throws Zend_Io_Exception if the stream is closed */ public final function writeUInt16BE($value) { return $this->write(pack('n*', $value)); } /** * Returns signed 32-bit integer as machine-endian ordered binary data. * * @param integer $value The input value. * @return string */ private final function _toInt32($value) { return pack('l*', $value); } /** * Writes signed 32-bit integer as little-endian ordered binary data to the * stream. * * @param integer $value The input value. * @return Zend_Io_Writer * @throws Zend_Io_Exception if the stream is closed */ public final function writeInt32LE($value) { if ($this->_isLittleEndian()) { return $this->write(strrev($this->_toInt32($value))); } else { return $this->write($this->_toInt32($value)); } } /** * Writes signed 32-bit integer as big-endian ordered binary data to the * stream. * * @param integer $value The input value. * @return Zend_Io_Writer * @throws Zend_Io_Exception if the stream is closed */ public final function writeInt32BE($value) { if ($this->_isBigEndian()) { return $this->write(strrev($this->_toInt32($value))); } else { return $this->write($this->_toInt32($value)); } } /** * Writes unsigned 32-bit integer as little-endian ordered binary data to * the stream. * * @param integer $value The input value. * @return Zend_Io_Writer * @throws Zend_Io_Exception if the stream is closed */ public final function writeUInt32LE($value) { return $this->write(pack('V*', $value)); } /** * Writes unsigned 32-bit integer as big-endian ordered binary data to the * stream. * * @param integer $value The input value. * @return Zend_Io_Writer * @throws Zend_Io_Exception if the stream is closed */ public final function writeUInt32BE($value) { return $this->write(pack('N*', $value)); } /** * Writes 64-bit float as little-endian ordered binary data string to the * stream. * * @param integer $value The input value. * @return Zend_Io_Writer * @throws Zend_Io_Exception if the stream is closed */ public final function writeInt64LE($value) { return $this->write (pack('V*', $value & 0xffffffff, $value / (0xffffffff+1))); } /** * Writes 64-bit float as big-endian ordered binary data string to the * stream. * * @param integer $value The input value. * @return Zend_Io_Writer * @throws Zend_Io_Exception if the stream is closed */ public final function writeInt64BE($value) { return $this->write (pack('N*', $value / (0xffffffff+1), $value & 0xffffffff)); } /** * Returns a floating point number as machine endian ordered binary data. * * @param float $value The input value. * @return string */ private function _toFloat($value) { return pack('f*', $value); } /** * Writes a floating point number as little-endian ordered binary data to * the stream. * * @param float $value The input value. * @return Zend_Io_Writer * @throws Zend_Io_Exception if the stream is closed */ public final function writeFloatLE($value) { if ($this->_isLittleEndian()) { return $this->write(strrev($this->_toFloat($value))); } else { return $this->write($this->_toFloat($value)); } } /** * Writes a floating point number as big-endian ordered binary data to the * stream. * * @param float $value The input value. * @return Zend_Io_Writer * @throws Zend_Io_Exception if the stream is closed */ public final function writeFloatBE($value) { if ($this->_isBigEndian()) { return $this->write(strrev($this->_toFloat($value))); } else { return $this->write($this->_toFloat($value)); } } /** * Writes string as binary data padded to given length with zeros. If * length is smaller than the length of the string, it is * considered as the length of the padding. * * @param string $value The input value. * @param integer $length The length to which to pad the value. * @param string $padding The padding character. * @return Zend_Io_Writer * @throws Zend_Io_Exception if the stream is closed */ public final function writeString8($value, $length = null, $padding = "\0") { if ($length === null) { $length = strlen($value); } if ($length < ($tmp = strlen($value))) { $length = $tmp + $length; } return $this->write(str_pad($value, $length, $padding)); } /** * Writes the multibyte string as binary data with given byte order mark * (BOM) and padded to given length with zeros. Length is given in unicode * characters so each character adds two zeros to the string. If length is * smaller than the length of the string, it is considered as the length of * the padding. * * If byte order mark is null no mark is inserted to the binary * data. * * @param string $value The input value. * @param integer $order The byte order of the binary data string. * @param integer $length The length to which to pad the value. * @param string $padding The padding character. * @return string * @throws Zend_Io_Exception if the stream is closed */ public final function writeString16 ($value, $order = null, $length = null, $padding = "\0") { if ($length === null) { $length = (int)(strlen($value) / 2); } if ($length < ($tmp = strlen($value) / 2)) { $length = $tmp + $length; } if ($order == self::BIG_ENDIAN_ORDER && !(ord($value[0]) == 0xfe && ord($value[1]) == 0xff)) { $value = 0xfeff . $value; $length++; } if ($order == self::LITTLE_ENDIAN_ORDER && !(ord($value[0]) == 0xff && ord($value[1]) == 0xfe)) { $value = 0xfffe . $value; $length++; } return $this->write(str_pad($value, $length * 2, $padding)); } /** * Writes hexadecimal string having high nibble first as binary data to the * stream. * * @param string $value The input value. * @return Zend_Io_Writer * @throws Zend_Io_Exception if length attribute is negative or * if the stream is closed */ public final function writeHHex($value) { return $this->write(pack('H*', $value)); } /** * Writes hexadecimal string having low nibble first as binary data to the * stream. * * @param string $value The input value. * @return Zend_Io_Writer * @throws Zend_Io_Exception if length attribute is negative or * if the stream is closed */ public final function writeLHex($value) { return $this->write(pack('h*', $value)); } /** * Writes big-endian ordered hexadecimal GUID string as little-endian * ordered binary data string to the stream. * * @param string $value The input value. * @return Zend_Io_Writer * @throws Zend_Io_Exception if the stream is closed */ public final function writeGuid($value) { $string = ''; $C = preg_split('/-/', $value); return $this->write (pack ('V1v2N2', hexdec($C[0]), hexdec($C[1]), hexdec($C[2]), hexdec($C[3] . substr($C[4], 0, 4)), hexdec(substr($C[4], 4)))); } /** * Forces write of all buffered output to the underlying resource. * * @return void * @throws Zend_Io_Exception if the stream is closed */ public function flush() { if ($this->_fd === null) { require_once('Zend/Io/Exception.php'); throw new Zend_Io_Exception('Cannot operate on a closed stream'); } fflush($this->_fd); } /** * Closes the stream. Once a stream has been closed, further calls to write * methods will throw an exception. Closing a previously-closed stream, * however, has no effect. * * @return void * @throws Zend_Io_Exception if the stream is closed */ public function close() { if ($this->_fd !== null) { @fclose($this->_fd); $this->_fd = null; } } /** * Returns the current machine endian order. * * @return integer */ private function _getEndianess() { if (self::$_endianess === 0) { self::$_endianess = $this->_toInt32("\x01\x00\x00\x00") == 1 ? self::LITTLE_ENDIAN_ORDER : self::BIG_ENDIAN_ORDER; } return self::$_endianess; } /** * Returns whether the current machine endian order is little endian. * * @return boolean */ private function _isLittleEndian() { return $this->_getEndianess() == self::LITTLE_ENDIAN_ORDER; } /** * Returns whether the current machine endian order is big endian. * * @return boolean */ private function _isBigEndian() { return $this->_getEndianess() == self::BIG_ENDIAN_ORDER; } /** * Magic function so that $obj->value will work. * * @param string $name The field name. * @return mixed */ public function __get($name) { if (method_exists($this, 'get' . ucfirst(strtolower($name)))) { return call_user_func (array($this, 'get' . ucfirst(strtolower($name)))); } else { require_once('Zend/Io/Exception.php'); throw new Zend_Io_Exception('Unknown field: ' . $name); } } /** * Magic function so that assignments with $obj->value will work. * * @param string $name The field name. * @param string $value The field value. * @return mixed */ public function __set($name, $value) { if (method_exists($this, 'set' . ucfirst(strtolower($name)))) { call_user_func (array($this, 'set' . ucfirst(strtolower($name))), $value); } else { require_once('Zend/Io/Exception.php'); throw new Zend_Io_Exception('Unknown field: ' . $name); } } }