not like eof in C or feof in PHP: * it returns TRUE if the *next* read would be end of file, * rather than if the *most recent* read read end of file. * @returns boolean true if at the end of file, and false otherwise */ public function is_eof() { throw new AvroNotImplementedException('Not implemented'); } /** * Closes this AvroIO instance. */ public function close() { throw new AvroNotImplementedException('Not implemented'); } } /** * AvroIO wrapper for string access * @package Avro */ class AvroStringIO extends AvroIO { /** * @var array array of individual bytes */ private $buffer; /** * @var int current position in string */ private $current_index; /** * @var boolean whether or not the string is closed. */ private $is_closed; /** * @param string $str initial value of AvroStringIO buffer. Regardless * of the initial value, the pointer is set to the * beginning of the buffer. * @throws AvroIOException if a non-string value is passed as $str */ public function __construct($str = '') { $this->is_closed = false; $this->buffer = array(); $this->current_index = 0; if (is_string($str)) $this->buffer = str_split($str); else throw new AvroIOException( sprintf('constructor argument must be a string: %s', gettype($str))); } /** * Append bytes to this buffer. * (Nothing more is needed to support Avro.) * @param str $arg bytes to write * @returns int count of bytes written. * @throws AvroIOException if $args is not a string value. */ public function write($arg) { $this->check_closed(); if (is_string($arg)) return $this->append_str($arg); throw new AvroIOException( sprintf('write argument must be a string: (%s) %s', gettype($arg), var_export($arg, true))); } /** * @returns string bytes read from buffer * @todo test for fencepost errors wrt updating current_index */ public function read($len) { $this->check_closed(); $read = array_slice($this->buffer, $this->current_index, $len); if (count($read) < $len) $this->current_index = $this->length(); else $this->current_index += $len; return join($read); } /** * @returns boolean true if successful * @throws AvroIOException if the seek failed. */ public function seek($offset, $whence=self::SEEK_SET) { if (!is_int($offset)) throw new AvroIOException('Seek offset must be an integer.'); // Prevent seeking before BOF switch ($whence) { case self::SEEK_SET: if (0 > $offset) throw new AvroIOException('Cannot seek before beginning of file.'); $this->current_index = $offset; break; case self::SEEK_CUR: if (0 > $this->current_index + $whence) throw new AvroIOException('Cannot seek before beginning of file.'); $this->current_index += $offset; break; case self::SEEK_END: if (0 > $this->length() + $offset) throw new AvroIOException('Cannot seek before beginning of file.'); $this->current_index = $this->length() + $offset; break; default: throw new AvroIOException(sprintf('Invalid seek whence %d', $whence)); } return true; } /** * @returns int * @see AvroIO::tell() */ public function tell() { return $this->current_index; } /** * @returns boolean * @see AvroIO::is_eof() */ public function is_eof() { return ($this->current_index >= $this->length()); } /** * No-op provided for compatibility with AvroIO interface. * @returns boolean true */ public function flush() { return true; } /** * Marks this buffer as closed. * @returns boolean true */ public function close() { $this->check_closed(); $this->is_closed = true; return true; } /** * @throws AvroIOException if the buffer is closed. */ private function check_closed() { if ($this->is_closed()) throw new AvroIOException('Buffer is closed'); } /** * Appends bytes to this buffer. * @param string $str * @returns integer count of bytes written. */ private function append_str($str) { $this->check_closed(); $ary = str_split($str); $len = count($ary); $this->buffer = array_merge($this->buffer, $ary); $this->current_index += $len; return $len; } /** * Truncates the truncate buffer to 0 bytes and returns the pointer * to the beginning of the buffer. * @returns boolean true */ public function truncate() { $this->check_closed(); $this->buffer = array(); $this->current_index = 0; return true; } /** * @returns int count of bytes in the buffer * @internal Could probably memoize length for performance, but * no need do this yet. */ public function length() { return count($this->buffer); } /** * @returns string */ public function __toString() { return join($this->buffer); } /** * @returns string * @uses self::__toString() */ public function string() { return $this->__toString(); } /** * @returns boolean true if this buffer is closed and false * otherwise. */ public function is_closed() { return $this->is_closed; } } /** * AvroIO wrapper for PHP file access functions * @package Avro */ class AvroFile extends AvroIO { /** * @var string fopen read mode value. Used internally. */ const FOPEN_READ_MODE = 'rb'; /** * @var string fopen write mode value. Used internally. */ const FOPEN_WRITE_MODE = 'wb'; /** * @var string */ private $file_path; /** * @var resource file handle for AvroFile instance */ private $file_handle; public function __construct($file_path, $mode = self::READ_MODE) { /** * XXX: should we check for file existence (in case of reading) * or anything else about the provided file_path argument? */ $this->file_path = $file_path; switch ($mode) { case self::WRITE_MODE: $this->file_handle = fopen($this->file_path, self::FOPEN_WRITE_MODE); if (false == $this->file_handle) throw new AvroIOException('Could not open file for writing'); break; case self::READ_MODE: $this->file_handle = fopen($this->file_path, self::FOPEN_READ_MODE); if (false == $this->file_handle) throw new AvroIOException('Could not open file for reading'); break; default: throw new AvroIOException( sprintf("Only modes '%s' and '%s' allowed. You provided '%s'.", self::READ_MODE, self::WRITE_MODE, $mode)); } } /** * @returns int count of bytes written * @throws AvroIOException if write failed. */ public function write($str) { $len = fwrite($this->file_handle, $str); if (false === $len) throw new AvroIOException(sprintf('Could not write to file')); return $len; } /** * @param int $len count of bytes to read. * @returns string bytes read * @throws AvroIOException if length value is negative or if the read failed */ public function read($len) { if (0 > $len) throw new AvroIOException( sprintf("Invalid length value passed to read: %d", $len)); if (0 == $len) return ''; $bytes = fread($this->file_handle, $len); if (false === $bytes) throw new AvroIOException('Could not read from file'); return $bytes; } /** * @returns int current position within the file * @throws AvroFileExcpetion if tell failed. */ public function tell() { $position = ftell($this->file_handle); if (false === $position) throw new AvroIOException('Could not execute tell on reader'); return $position; } /** * @param int $offset * @param int $whence * @returns boolean true upon success * @throws AvroIOException if seek failed. * @see AvroIO::seek() */ public function seek($offset, $whence = SEEK_SET) { $res = fseek($this->file_handle, $offset, $whence); // Note: does not catch seeking beyond end of file if (-1 === $res) throw new AvroIOException( sprintf("Could not execute seek (offset = %d, whence = %d)", $offset, $whence)); return true; } /** * Closes the file. * @returns boolean true if successful. * @throws AvroIOException if there was an error closing the file. */ public function close() { $res = fclose($this->file_handle); if (false === $res) throw new AvroIOException('Error closing file.'); return $res; } /** * @returns boolean true if the pointer is at the end of the file, * and false otherwise. * @see AvroIO::is_eof() as behavior differs from feof() */ public function is_eof() { $this->read(1); if (feof($this->file_handle)) return true; $this->seek(-1, self::SEEK_CUR); return false; } /** * @returns boolean true if the flush was successful. * @throws AvroIOException if there was an error flushing the file. */ public function flush() { $res = fflush($this->file_handle); if (false === $res) throw new AvroIOException('Could not flush file.'); return true; } }