Login   Register  
PHP Classes
elePHPant
Icontem

File: php.io.php

Recommend this page to a friend!
Stumble It! Stumble It! Bookmark in del.icio.us Bookmark in del.icio.us
  Classes of Tobias Marstaller  >  Stream IO  >  php.io.php  >  Download  
File: php.io.php
Role: Class source
Content type: text/plain
Description: Contains all classes; No need to include anything else.
Class: Stream IO
Access files with classes similar to Java IO
Author: By
Last change: Charsets added.
Date: 2 years ago
Size: 51,820 bytes
 

Contents

Class file image Download
<?php
/**
 * @author Tobias Marstaller
 * @desc Provides an interface to exchange PHP numbers with arrays that represent their value in bit-format
*/
abstract class BigEndian {
	public static function readInt($len, InputStream $from) {
		if ($len%8!=0)
			throw new Exception("Length must be a multiple of 8");
		$bytes=array();
		$len=$len/8;
		for ($i=0;$i<$len;$i++) {
			$b=$from->read();
			if (!is_int($b)) $b=ord($b);
			$bytes[]=$b;
		}
		$number=0;
		$bytes=array_reverse($bytes);
		for ($i=count($bytes)-1;$i>=0;$i--) {
			$x=$bytes[$i]*(self::exp(256, $i));
			$number+=$x;
		}
		return $number;
	}
	public static function writeInt($len, $int) {
		if ($len%8!=0)
			throw new Exception("Length must be a multiple of 8");

		$len=$len/8;
		$bytes=array();
		for ($i=$len-1;$i>=0;$i--) {
			$exp=self::exp(256, $i);
			$x=floor($int/$exp);
			$int-=$x*$exp;
			$bytes[]=$x;
		}
		return $bytes;
	}
	public static function readInt16(InputStream $from) {
		return self::readInt(16, $from);
	}
	public static function readInt32(InputStream $from) {
		return self::readInt(32, $from);
	}
	public static function readInt64(InputStream $from) {
		return self::readInt(64, $from);
	}
	public static function readInt128(InputStream $from) {
		return self::readInt(128, $from);
	}
	public static function writeInt16($int) {
		return self::writeInt(16, $int);
	}
	public static function writeInt32($int) {
		return self::writeInt(32, $int);
	}
	public static function writeInt64($int) {
		return self::writeInt(64, $int);
	}
	public static function writeInt128($int) {
		return self::writeInt(128, $int);
	}
	private static function exp($a, $b) {
		if ($b==0) return 1;
		for ($i=1;$i<$b;$i++) {
			$a*=$a;
		}
		return $a;
	}
}
interface Closable {
	public function close();
}
interface Flushable {
	/**
	 * @throws IOException
	*/
	public function flush();
}
/**
 * @desc Thrown whenever an error occures during an input/output action.
 * @author Tobias Marstaller
*/
class IOException extends Exception {
}
interface FileFilter {
	public function accept(File $file);
}
interface FilenameFilter {
	public function accept(File $parent, $file);
}
/**
 * @author Tobias Marstaller
 * @descr Represents a temporary file
*/
class TemporaryFile extends File {
	/**
	 * @desc The PHP-Handle to the file
	 * @param -resource $handle
	*/
	private $handle;
	/**
	 * @desc Holds all information available about the file
	 * @param -array $streamMetaData
	*/
	private $streamMetaData;
	public function __construct() {
		super();
		$this->handle=tmpfile();
		$this->streamMetaData=stream_get_metadata($this->handle);
		$this->path=$this->streamMetaData["uri"];
		$this->readable=true;
		$this->writable=true;
		$this->executable=false;
		$this->filename=basename($this->path);
		$this->isFile=true;
		$this->hidden=false;
	}
	public function __destruct() {
		fclose($this->handle);
	}
	public static function createTmpFile() {
		return;
	}
	/**
	 * @hidden
	*/
	public function getHandle() {
		return $this->handle;
	}
}
/**
 * @author Tobias Marstaller
 * @desc Represents a file in the physical file-system. It does not have to exist
*/
class File {
	const FILE_SEPARATOR=DIRECTORY_SEPARATOR;
	/**
	 * @param -String $path The absolute path to this file
	 * @param -String $filename The name of this file (not path and no extension)
	 * @param -boolean $isFile Weather this represents a file or a directory
	 * @param -boolean $executable
	 * @param -boolean $readable
	 * @param -boolean $writable
	 * @param -boolean $deleteOnExit If true, this file we be deleted when the execution is stopped
	 * @param -boolean $isFileOwner Wheather the user, php is ran under, owns this file
	 */
	protected $path;
	protected $filename;
	protected $isFile=true;
	protected $executable=false;
	protected $readable=false;
	protected $writable=false;
	protected $deleteOnExit=false;
	protected $isFileOwner=false;
	/*
		File(String $path);
		File(String $parent, String $child);
		File(File $parent, String $child);
	 * @param -String/File $path Parent directory or path to the file
	 * @param -String $child Optional; File in the parent directory
	*/
	protected function __constrcut() {
		// creates a null-file
	}
	public function __construct($path, $child=null) {
		if ($path instanceof File) {
			if ($child==null) {
				throw new IOException("Missing second argument");
			}
			$parent=$path;
			if (!$parent->isDirectory()) {
				throw new IOException("File musst be a directory");
			}
			$path=$parent->getAbsolutePath();
			$path.=$child;
			$this->path=$path;
			$this->filename=basename($path);
			$this->isFile=!is_dir($path);
		} else if (is_string($path)) {
			if ($child!=null) {
				if (!is_string($child)) {
					throw new IOException("\$child must be a string");
				}
				if (!is_directory($path)) {
					throw new IOException("\$path must be a directory");
				}
				$path=dirname($path).File::FILE_SEPARATOR.$child;
				if ($this->exists()) {
					$this->isFile=!is_directory($path);
				} else {
					$a=substr($path, strrpos($path, File::FILE_SEPARATOR));
					$this->isFile=strpos($a, ".")!==false;
				}
				$this->path=$path;
				$this->filename=basename($path);
			} else {
				$this->isFile=!is_dir($path);
				if (!$this->isFile) {
					$this->path=dirname($path);
				} else {
					$this->path=$path;
				}
				$this->filename=basename($path);
			}
		} else {
			throw new IOException("Invalid argument \$path. Must be instance of File or String.");
		}
		// Absoluten Pfad erstellen
		if (!preg_match("!^\w:\\\!", $this->path)
		 && !preg_match("!^/!", $this->path)) {
			$path=getcwd();
			if (substr($path, strlen($path)-1)!=File::FILE_SEPARATOR) {
				$path.=File::FILE_SEPARATOR;
			}
			$this->path=$path.$this->path;
		}
		if ($this->exists()) {
			if (substr(PHP_OS, 0, 3)!="WIN") {
				$rights="".fileperms($this->path);
				$user_r=(int) substr($rights, 0, 1);
				$group_r=(int) substr($rights, 1, 1);
				$public_r=(int) substr($rights, 2, 1);
				$owner=posix_getpwuid(fileowner($this->path));
				$rights=0;
				if ($owner["name"]==$_ENV["user"]) {
					$rights=$user_r;
					$this->isFileOwner=true;
				} else if ($owner["gid"]==filegroup($this->path)) {
					$rights=$group_r;
				} else $rights=$public_r;
				switch ($rights) {
					case 1:
						$this->executable=true;
					break;
					case 2:
						$this->writable=true;
					break;
					case 3:
						$this->writable=true;
						$this->readable=false;
					break;
					case 4:
						$this->readable=true;
					break;
					case 5:
						$this->readable=true;
						$this->executable=true;
					break;
					case 6:
						$this->readable=true;
						$this->writeable=true;
					break;
					case 7:
						$this->readable=true;
						$this->writable=true;
						$this->executable=true;
					break;
				}
			} else {
				$this->executable=$this->readable=$this->writeable=true;
			}
		}
	}
	public function __destruct() {
		if ($this->deleteOnExit) {
			$this->delete();
		}
	}
	/**
	 * @return boolean
	 * @desc Returns weather this file can be executed; Always true on Windows.
	 */
	public function canExecute() {
		return $this->executeable;
	}
	/**
	 * @return boolean
	 * @desc Returns weather this file can be read; Always true on Windows.
	*/
	public function canRead() {
		return $this->readable;
	}
	/**
	 * @return boolean
	 * @desc Returns weather this file can be written; Always true on Windows.
	*/
	public function canWrite() {
		return $this->writable;
	}
	/**
	 * @return int
	 * @desc Compares the given file to this lexicographycally; Returns -1 if given less than this, 0 upon equal, 1 otherweise
	 * @param $f The File to compare to
	*/
	public function compareTo(File $f) {
		$a=$this->path;
		$b=$f->getAbsolutePath();
		if (strlen($a)==strlen($b)) return 0;
		if (strlen($a)<strlen($b)) {
			$x=strlen($a);
			$y=strlen($b);
			for($i=$y-$x;$i<$y;$i++) {
				$a[$x+$i]=chr(0);
			}
		} else if (strlen($b)<strlen($a)) {
			$x=strlen($b);
			$y=strlen($a);
			for($i=$y-$x;$i<$y;$i++) {
				$b[$x+$i]=chr(0);
			}
		}
		$x=strlen($a);
		for ($i=0;$i<$x;$i++) {
			$c1=signedByteToUnsigned(ord($a[$i]));
			$c2=signedByteToUnsigned(ord($b[$i]));
			if ($c1==$c2) continue;
			if ($c1>$c2) return -1;
			if ($c1<$c2) return 1;
		}
		return 0;
	}
	/**
	 * @hidden
	*/
	private function signedByteToUnsigned($byte) {
		if ($byte<0) {
			return abs($byte)+128;
		} else return $byte;
	}
	/**
	 * @desc Creates this file if it dosent already exist; Returns weather the file was created.
	 * @return boolean
	 * @throws IOException
	*/
	public function create() {
		$this->createNewFile();
	}
	/**
	 * @desc Creates this file if it dosent already exist; Returns weather the file was created.
	 * @return boolean
	*/
	public function createNewFile() {
		if ($this->exists()) {
			return false;
		}
		@$fp=fopen($this->path, "w");
		if (!$fp) {
			throw new IOException("Could not create file.");
		}
		fclose($fp);
		return true;
	}
	/**
	 * @descr Creates a temporary file. See class TemporaryFile.
	 * @return File
	*/
	public static function createTmpFile() {
		return new TemporaryFile();
	}
	/**
		@desc Deletes this file.
		@return boolean
	*/
	public function delete() {
		if (!$this->exists()) {
			return false;
		}
		if ($this->isFile) {
			$d=@unlink($this->path);
		} else {
			$d=@rmdir($this->path);
		}
		return $d;
	}
	/**
	 * @desc Once called, this file be deleted upon the end of the application
	*/
	public function deleteOnExit() {
		$this->deleteOnExit=true;
	}
	/**
	 * @param -File/String $obj The file to compare to
	 * @desc Compares the paths of this and the given file
	 * @return boolean
	*/
	public function equals($obj) {
		if ($obj instanceof File) {
			return $this->path==$obj->getAbsolutePath();
		} else if (is_string($obj)) {
			return $this->path==$obj;
		} else return false;
	}
	/**
	 * @desc Checks, weather this file exists
	 * @return boolean
	*/
	public function exists() {
		return file_exists($this->path);
	}
	public function getAbsoluteFile() {
		return $this;
	}
	public function getAbsolutePath() {
		return $this->path;
	}
	public function getCanonicalFile() {
		return $this;
	}
	public function getCanonicalPath() {
		return $this->path;
	}
	/**
	 * @desc Returns the free disk space of the partition this file is stored in/pointing on.
	 * @return long
	*/
	public function getFreeSpace() {
		return disk_free_space($this->path);
	}
	/**
	 * @desc Returns this Files name; e.g "/var/foo.txt" as path would return "foo.txt"
	 * @return String
	*/
	public function getName() {
		return $this->filename;
	}
	/**
	 * @return String
	 * @desc Returns the parent directory if this file
	*/
	public function getParent() {
		if ($this->isFile) {
			return dirname($this->path);
		} else {
			$path=dirname($this->path);
			if ($pos=strpos($path, File::FILE_SEPARATOR)===false) return null;
			return substr($path, 0, $pos-1);
		}
	}
	/**
	 * @return File
	 * @desc Returns the parent directory if this file
	*/
	public function getParentFile() {
		$path=$this->getParent();
		if ($path==null) return null;
		return new File($path);
	}
	/**
	 * @desc Returns the space usable in the partition, this file is stored in/pointing on
	 * @return long
	*/
	public function getUsableSpace() {
		return disk_free_space($this->path);
	}
	/**
	 * @return long
	 * @desc Returns the total space of the physical drive this file is stored on/pointing at
	*/
	public function getTotalSpace() {
		return disk_total_space($this->path);
	}
	/**
	 * @return boolean
	 * @desc Always true
	*/
	public function isAbsolute() {
		return true;
	}
	/**
	 * @return boolean
	 * @desc Returns weather this file is a directory
	*/
	public function isDirectory() {
		return !$this->isFile;
	}
	/**
	 * @return boolean
	 * @desc Returns weather this file is a File
	*/
	public function isFile() {
		return $this->isFile;
	}
	/**
	 * @return boolean
	 * @desc Returns weather this file is hidden; always true on Windows
	*/
	public function isHidden() {
		if (substr(PHP_OS, 0, 3)=="WIN") {
			return false;
		} else {
			return substr($this->filename, 0, 1)==".";
		}
	}
	/**
	 * @return int
	 * @desc Returns the timestamp this file was last modified
	*/
	public function lastModified() {
		return filemtime($this->path);
	}
	/**
	 * @return int
	 * @desc Returns this files size in bytes and 0 if it doesnt exist
	*/
	public function length() {
		if (!$this->exists()) return 0;
		return filesize($this->path);
	}
	/**
	 * @return String[]
	 * @desc Lists all files on this directory (including . and ..) and optionally using the filter to sort out.
	 * @param $filter The filter to use
	 * @throws IOException
	*/
	public function _list(FilenameFilter $filter=null) {
		if ($this->isFile) {
			throw new IOException("This File is not a directory");
		}
		@$dir=opendir($this->path);
		if (!$dir) {
			throw new IOException("Failed to open directory for reading");
		}
		$ar=array();
		while ($file=readdir($dir)) {
			if ($filter!=null) {
				if ($filter->accept($this, $file)) {
					$ar[]=$file;
				}
			} else {
				$ar[]=$file;
			}
		}
		@closedir($dir);
		return $ar;
	}
	/**
	 * @return File[]
	 * @desc Lists all files on this directory (including . and ..) and optionally using the filter to sort out.
	 * @param -FileFilter/FilenamFilter $filter The filter to use
	*/
	public function listFiles($filter=null) {
		if (!$filter instanceof FilenameFilter && !$filter instanceof FileFilter) {
			throw new IOException("Illegal argument");
		}
		$ar=$this->_list();
		$ar2=array();
		foreach ($ar as $name) {
			if ($filter!=null && $filter instanceof FilenameFilter) {
				if (!$filter->accept($name)) {
					continue;
				}
			}
			$path=$this->path.$name;
			$f=new File($path);
			if ($filter!=null && $filter instanceof FileFilter) {
				if (!$filter->accept($name)) {
					continue;
				}
			}
			$ar2[]=$f;
		}
		return $ar2;
	}
	/**
	 * @return File[]
	 * @desc In Windows: returns the drive this file is stored on; In Linux: Returns / and everything found in /media/
	 * @throws IOException
	*/
	public static function listRoots() {
		if (substr(PHP_OS, 0, 3)=="WIN") {
			return array(new File(substr(__FILE__, 0, 3)));
		} else {
			$roots=array(new File("/"));
			@$dir=opendir("/media/");
			if (!$dir) {
				throw new IOException("Unable to open /media/; ".$php_errormsg);
			}
			while ($file=readdir($dir)) {
				if ($file!="." && $file!="..") {
					if (!is_file("/media/".$file)) {
						$roots[]=new File("/media/", $file);
					}
				}
			}
		}
	}
	/**
	 * @return boolean
	 * @desc Creates this directory
	*/
	public function mkdir() {
		$d=@mkdir($this->path);
		return $d;
	}
	/**
	 * @return boolean
	 * @desc Creates this directory and all parental directories that dont exist
	*/
	public function mkdirs() {
		$d=@mkdirs($this->path);
		return $d;
	}
	/**
	 * @return boolean
	 * @desc Renames this file to the given one; May also move the file if the directories are different
	*/
	public function renameTo(File $target) {
		@$r=rename($this->path, $name->getAbsolutePath());
		if (!$r) {
			throw new IOException("Unable to rename; ".$php_errormsg);
		}
		return $r;
	}
	/**
	 * @return octal Int
	 * @desc Creates the octal integer of this right-setup
	 * @param -boolean $read
	 * @param -boolean $write
	 * @param -boolean $exec
	*/
	public static function makePerms($read, $write, $exec) {
		if (!$read && !$write && !$exec) {
			return 0;
		}
		if (!$read && !$write && $exec) {
			return 1;
		}
		if (!$read && $write && !$exec) {
			return 2;
		}
		if ($read && !$write && $exec) {
			return 3;
		}
		if ($read && !$write && !$exec) {
			return 4;
		}
		if ($read && !$write && $exec) {
			return 5;
		}
		if ($read && $write && !$exec) {
			return 6;
		}
		if ($read && $write && $exec) {
			return 7;
		}
	}
	/**
	 * @return boolean[]
	 * @desc Returns the 3 permission-groups for this right-byte
	 * @param -octal_Int $int
	*/
	public static function getPerms($int) {
		$exec=false;
		$write=false;
		$read=true;
		switch ($int) {
			case 1:
				$exec=true;
			break;
			case 2:
				$write=true;
			break;
			case 3:
				$write=true;
				$read=false;
			break;
			case 4:
				$read=true;
			break;
			case 5:
				$read=true;
				$exec=true;
			break;
			case 6:
				$read=true;
				$write=true;
			break;
			case 7:
				$read=true;
				$write=true;
				$exec=true;
			break;
		}
		return array($read, $write, $exec);
	}
	/*
	$right:
		0 : read
		1 : write
		2 : execute
	*/
	/**
	 * @hidden
	*/
	private function setRight($right, $allowed, $ownerOnly=false) {
		if (substr(PHP_OS, 0, 3)!="WIN" && !$this->isFileOwner) {
			throw new IOException("Only the file owner (PHP is ran by ".$_ENV["USER"].") can change this");
		}
		if (!$this->exists()) {
			throw new IOException("File not found");
		}
		if ($right<0 || $right>2) {
			throw new Exception("Illegal argument \$right");
		}
		$rights="".fileperms($this->path);
		$user_r=(int) substr($rights, 0, 1);
		$group_r=(int) substr($rights, 1, 1);
		$public_r=(int) substr($rights, 2, 1);
		if ($ownerOnly) {
			$rights=File::getPerms($user_r);
			$rights[$right]=true;
			$rights=(int) ("0".File::makePerms($rights[0], $rights[1], $rights[2])."".$group_r.$public_r);
			chmod($this->path, $rights);
		} else {
			$rights=File::getPerms($user_r);
			$rights[$right]=true;
			$user_r=File::makePerms($rights[0], $rights[1], $rights[2]);
			
			$rights=File::getPerms($group_r);
			$rights[$right]=true;
			$group_r=File::makePerms($rights[0], $rights[1], $rights[2]);
			
			$rights=File::getPerms($public_r);
			$rights[$right]=true;
			$public_r=File::makePerms($rights[0], $rights[1], $rights[2]);
			
			$rights=(int) ("0".$user_r.$group_r.$public_r);
			chmod($this->path, $rights);
		}
	}
	/**
	 * @desc Trys to set the execute-permission for this file
	 * @throws IOException
	 * @param -boolean $exec
	 * @param -boolean $owner If false, everyone will be able to execute this file
	*/
	public function setExecutable($exec, $owner=true) {
		$this->setRight(2, $exec, $owner);
	}
	/**
	 * @desc Trys to set the write-permission for this file
	 * @throws IOException
	 * @param -boolean $write
	 * @param -boolean $owner If false, everyone will be able to write to this file
	*/
	public function setWritable($write, $owner=true) {
		$this->setRight(1, $write, $owner);
	}
	/**
	 * @desc Trys to set the read-permission for this file
	 * @throws IOException
	 * @param -boolean $read
	 * @param -boolean $owner If false, everyone will be able to read this file
	*/
	public function setReadable($read, $owner=true) {
		$this->setRight(0, $read, $owner);
	}
	/**
	 * @desc Trys to deny writing and executing for everyone
	 * @throws IOException
	*/
	public function setReadOnly() {
		$this->setRight(2, false, false);
		$this->setRight(1, false, false);
	}
	public function __toString() {
		return $this->path;
	}
}
/**
 * @author Tobias Marstaller
 * @desc Provides a simple interface to read raw bytes from a php-handle
*/
class RawInputStream implements InputStream {
	protected $handle;
	protected $available=false;
	// PHP ftell() doesnt work properly
	protected $pos=0;
	protected $marker=0;
	
	protected $streamMetaData;
	public function __construct($handle) {
		if (!$handle) {
			throw new IOException("Invalid resource");
		}
		$this->handle=$handle;
		@$this->streamMetaData=stream_get_meta_data($handle);
		@$this->fullSize=filesize($this->streamMetaData["uri"]);
		if ($this->fullSize!=0) {
			$this->available=true;
		}
	}
	public function read(array &$byte_array=null, $off=0, $len=0) {
		if ($byte_array==null) {
			return ord($this->readChar());
		} else {
			if ($len==0) $len=count($byte_array);
			$target=$off+$len;
			for (;$off<$target;$off++) {
				$byte_array[$off]=ord($this->readChar());
			}
		}
	}
	public function readChar() {
		@$char=fread($this->handle, 1);
		if ($char===false) {
			$this->available=false;
			if (feof($this->handle)) {
				return null;
			} else {
				throw new IOException("Unable to read from file; ".$php_errormsg);
			}
		}
		$this->pos++;
		return $char;
	}
	public function skip($long) {
		if (!$this->streamMetaData["seekable"]) {
			throw new IOException("This stream does not suppoer seek.");
		}
		$pos=$this->pos+=$long;
		if ($pos>$this->fullSize) {
			$skipped=$pos-$this->fullSize;
			$pos=$this->pos+$skipped;
		} else {
			$skipped=$long;
		}
		@$seek=fseek($this->handle, $pos);
		if (!$seek) {
			throw new IOException("Failed to seek; Maybe not supported by this handle.");
		}
		$this->pos=$pos;
		return $skipped;
	}
	public function available() {
		return feof($this->handle);
	}
	public function markSupported() {
		return $this->streamMetaData["seekable"];
	}
	public function markSeekable() {
		return $this->streamMetaData["seekable"];
	}
	public function mark() {
		if (!$this->streamMetaData["seekable"]) {
			throw new IOException("Mark/Seek is not supported by this stream");
		}
		$this->marker=$this->pos;
	}
	public function reset() {
		if (!$this->streamMetaData["seekable"]) {
			throw new IOException("Mark/Seek is not supported by this stream");
		}
		$seek=fseek($this->handle, $this->marker);
		if (!$seek) {
			throw new IOException("Failed to seek; ".$php_errormsg);
		}
		$this->pos=$this->marker;
	}
	public function rewind() {
		if (!$this->streamMetaData["seekable"]) {
			throw new IOException("Seek is not supported by this stream");
		}
		$seek=fseek($this->handle, 0);
		if (!$seek) {
			throw new IOException("Failed to seek; ".$php_errormsg);
		}
		$this->pos=0;
	}
}
/**
 * @author Tobias Marstaller
 * @desc Provides a simple interface to write raw bytes to a php-handle
*/
class RawOutputStream implements OutputStream {
	protected $handle;
	protected $closed=false;
	/**
	 * @param -resource $r The stream to write to
	 * @param -boolean $append Weather to append all contents
	*/
	public function __construct($r) {
		$this->handle=$r;
	}
	/**
	 * @desc Closes this stream; Temporary files will be deleted
	 * @throws IOException
	*/
	public function close() {
		if ($this->closed()) {
			throw new IOException("Stream already closed.");
		}
		@$c=fclose($this->handle);
		$this->closed=true;
		if (!$c) {
			throw new IOException("Unable to close stream; ".$php_errormsg);
		}
	}
	/**
	 * @desc writes the byte(s) $b to the stream
	 * @param -Byte/Byte[] $b
	 * @param -int $off Where in the array to start reading
	 * @param -int $len How much bytes to be read from the array
	 * @throws IOException
	*/
	public function write($b, $off=0, $len=0) {
		if (is_double($b) || is_float($b)) {
			$b=(int) round($b);
		}
		if (is_int($b)) {
			if ($b<-128 || $b>257)
				throw new IOException("Byte out of range.");
			fwrite($this->handle, chr($b));
		} else if (is_string($b)) {
			if ($len==0)
				$len=strlen($b);
			$j=$len+$off;
			for ($i=$off;$i<$j;$i++) {
				$this->write(ord($b[$i]));
			}
		} else if (is_array($b)) {
			if ($len==0)
				$len=count($b);
			$j=$len+$off;
			for ($i=$off;$i<$j;$i++) {
				$this->write($b[$i]);
			}
		}
	}
	protected function appendToBuffer(array $b) {
		foreach($b as $byte) {
			$this->buffer[]=$byte;
		}
	}
}
/**
 * @author Tobias Marstaller
*/
interface InputStream {
	/**
	 * @desc Read $len bytes from the stream and write them to $byte_array at the offset $off
	 * @param -int $off
	 * @param -int $len
	 * @throws IOException
	*/
	public function read(array &$byte_array=null, $off=0, $len=0);
	/**
	 * @desc Skips the given amount of bytes in the stream. Returns the amount of bytes acutally skipped
	 * @param -long $bytes
	 * @return long
	 * @throws IOException
	*/
	public function skip($bytes);
	/**
	 * @return boolean
	 * @desc Returns weather there is data available to be read or not
	*/
	public function available();
	/**
	 * @return boolean
	*/
	public function markSupported();
	/**
	 * @desc Marks a position in this stream
	 * @throws IOException
	*/
	public function mark();
	/**
	 * @desc Sets the reading-position to the point marked with mark();
	 * @throws IOException
	*/
	public function reset();
}
/**
 * @desc Provides the option to read characters from bytestreams using different charsets
 * @author Tobias Marstaller
*/
class InputStreamReader implements Closable {
	/**
	 * @param -int $maker The marked position in the underlaying stream
	 * @param -InputStream $stream The underlaying stream
	 * @param -Charset $charset The used charset
	 * @param -array $buffer
	 * @param -int $eof_offset The approximated distance to EOF
	 * @param -int $buffer_pos The current reading-position in the buffer
	 * @param -boolean $closed Weather this stream is closed
	*/
	private $marker;
	private $stream;
	private $charset;
	private $buffer;
	private $eof_offset=-1;
	private $buffer_pos;
	private $closed=false;
	public function __construct(InputStream $source, Charset $charset=null) {
		$this->stream=$source;
		if ($charset==null)
			$charset=new UTF8Charset();
		$this->charset=$charset;
	}
	public function close() {
		if ($this->closed)
			throw new IOException("This stream has already been closed");
		if ($this->stream instanceof Closable) {
			$this->stream->close();
			$this->buffer=null;
			$this->closed=true;
		}
	}
	public function read(array &$ar=null, $off=0, $len=0) {
		if ($this->closed)
			throw new IOException("This stream has been closed");
		if ($this->buffer_pos==$this->eof_offset)
			return null;
		if ($this->buffer_pos==$this->charset->decodeBufferSize || $this->buffer==null) {
			$this->buffer_pos=0;
			$this->fillBuffer();
		}
		if ($ar!=null) {
			if ($len==0)
				$len=count($ar);
			for ($off--;$off<$len;$off++) {
				$ar[$off]=$this->read();
			}
		} else
			return $this->buffer[$this->buffer_pos++];
	}
	/**
	 * @hidden
	*/
	private function fillBuffer() {
		$ar=array();
		for ($i=0;$i<$this->charset->decodeBufferSize;$i++) {
			$x=$this->stream->read();
			if ($x==null) {
				$this->eof_offset=$this->charset->decodeBufferSize-$i;
				for (;$i<$this->charset->decodeBufferSize;$i++) $ar[]=0;
				break;
			}
			$ar[]=$x;
		}
		$this->buffer=$this->charset->decodeChars($ar);
	}
}
/**
 * @author Tobias Marstaller
 * @desc Provides the option to read raw bytes from a file
*/
class FileInputStream extends RawInputStream  implements InputStream, Closable {
	protected $closed=false;
	public function __construct($f) {
		if (is_string($f)) {
			$f=new File($f);
		} else if (!$f instanceof File) {
			throw new IOException("Illegal argument");
		}
		if (!$f->exists()) {
			throw new IOException("File not found");
		}
		if ($f instanceof TemporaryFile) {
			$handle=$f->getHandle();
		} else {
			if (substr(PHP_OS, 0, 3)=="WIN") {
				@$handle=fopen($f->getAbsolutePath(), "rb");
			} else {
				@$handle=fopen($f->getAbsolutePath(), "r");
			}
		}
		parent::__construct($handle);
	}
	public function close() {
		if ($this->closed)
			throw new IOException("This stream has already been closed.");
		$this->closed=true;
		@$c=fclose($this->handle);
		$this->handle=null;
		if (!$c) {
			throw new IOException("Failed to close the stream;".$php_errormsg);
		}
	}
}
/**
 * @author Tobias Marstaller
 * @desc Buffers a php-native stream for performance increasements
 */
class BufferedReader implements Closable, InputStream {
	private $stream;
	private $bufferSize;
	private $bufferPos;
	private $localBufferMax=null;
	private $buffer=array();
	private $closed=false;
	private $marker=0;
	/**
	 * @param -int bufferSize Size of the buffer
	 */
	public function __construct($stream, $bufferSize=1024) {
		$this->stream=$stream;
		for ($i=0;$i<$bufferSize;$i++) {
			$this->buffer[$i]=null;
		}
		$this->bufferSize=$bufferSize;
		$this->bufferPos=0;
		$this->fillBuffer();
	}
	public static function forFile(File $f, $bufferSize=1024) {
		if (substr(PHP_OS, 0, 3)=="WIN") {
			@$handle=fopen($f->getAbsolutePath(), "rb");
		} else {
			@$handle=fopen($f->getAbsolutePath(), "r");
		}
		return new BufferedReader($handle, $bufferSize);
	}
	/**
	 * @desc Fills the buffer
	 */
	private function fillBuffer() {
		if ($this->closed)
			throw new IOException("Stream closed.");
		$str=fread($this->stream, $this->bufferSize);
		if ($str===false) {
			throw new IOException("Unable to fill buffer: ".$php_errmsg);
		}
		$length=strlen($str);
		if ($length!=$this->bufferSize) 
			$this->localBufferMax=$length;
		else $this->localBufferMax=null;
		for ($i=0;$i<$length;$i++) {
			$this->buffer[$i]=$str[$i];
		}
		for ($i=$this->bufferSize-$length;$i<$this->bufferSize;$i++) {
			$this->buffer[$i]=null;
		}
		$this->bufferPos=0;
	}
	/**
	 * @param -int $off Offset in $ar to begin writing at
	 * @param -int $len Number of bytes to read to $ar
	 */
	public function read(array &$ar=null, $off=0, $len=null) {
		if ($this->closed)
			throw new IOException("Stream closed.");
		if ($ar==null) {
			if ($this->localBufferMax===null)
				if ($this->bufferPos==$this->bufferSize-1) {
					$this->fillBuffer();
				}
			else if ($this->bufferPos==$this->localBufferMax-1)
				$this->fillBuffer();
			return $this->buffer[$this->bufferPos++];
		} else {
			if ($len==null) $len=count($ar);
			for (;$off<$len;$off++) {
				$ar[$off]=$this->read();
			}
		}
	}
	/**
	 * @desc Skips the given amount of bytes in the stream. Returns the amount of bytes acutally skipped
	 * @param -long $bytes
	 * @return long
	*/
	public function skip($length) {
		if ($this->closed)
			throw new IOException("Stream closed.");
		$max=$this->localBufferMax===null ? $this->bufferSize : $this->localBufferMax;
		if ($length<$max) {
			$this->bufferPos+=$length;
		} else if ($length==$max) {
			$this->fillBuffer();
		} else {
			$actual_skip=$length-$max;
			fseek($this->stream, ftell($this->stream)+$actual_skip);
			$this->fillBuffer();
		}
	}
	/**
	 * @return boolean
	 * @desc Returns weather there is data available to be read or not
	*/
	public function available() {
		if ($this->closed)
			throw new IOException("Stream closed.");
		if ($this->localBufferMax===null)
			if ($this->bufferPos==$this->bufferSize-1) {
				$this->fillBuffer();
			}
		else if ($this->bufferPos==$this->localBufferMax-1)
			$this->fillBuffer();
		return !$this->buffer[$this->bufferPos]===null;
	}
	/**
	 * @return boolean
	*/
	public function markSupported() {
		return true;
	}
	/**
	 * @desc Marks a position in this stream
	*/
	public function mark() {
		if ($this->closed)
			throw new IOException("Stream closed.");
		$this->marker=ftell($this->stream);
	}
	/**
	 * @desc Sets the reading-position to the point marked with mark();
	*/
	public function reset() {
		if ($this->closed)
			throw new IOException("Stream closed.");
		fseek($this->stream, $this->marker);
		$this->fillBuffer();
	}
	public function close() {
		if ($this->closed)
			throw new IOException("Stream closed.");
		fclose($this->stream);
		$this->closed=true;
		$this->buffer=null;
	}
}
/**
 * @author Tobias Marstaller
*/
interface OutputStream {
	/**
	 * @param -array/byte $bytes
	 * @param -int $off The offset to start at in the array $b
	 * @param -int $len How many bytes to read from $b
	*/
	public function write($bytes, $off=0, $len=0);
}
/**
 * @author Tobias Marstaller
 * @desc Provides the option to read complex data such as nested arrays and objects from a stream
*/
class DataInputStream extends RawInputStream implements Closable {
	private $stream;
	public function __construct(InputStream $s) {
		$this->stream=$s;
	}
	public function __destruct() {
		$this->close();
	}
	public function readObject() {
		$size=BigEndian::readInt32($this->stream);
		$obj=array();
		for ($i=0;$i<$size;$i++) {
			$b=$this->stream->read();
			if (is_int($b)) $b=chr($b);
			$obj[]=$b;
		}
		return unserialize(implode("", $obj));
	}
	public function close() {
		$this->stream->close();
	}
}
/**
 * @author Tobias Marstaller
 * @desc Provides the option to write complex data such as nested arrays and objects to a stream
*/
class DataOutputStream extends RawOutputStream implements Closable {
	private $stream;
	public function __construct(OutputStream $s) {
		$this->stream=$s;
	}
	public function __destruct() {
		$this->close();
	}
	public function writeObject($obj) {
		$obj=serialize($obj);
		$size=strlen($obj);
		$this->stream->write(BigEndian::writeInt32($size));
		$this->stream->write($obj);
	}
	public function write($b, $len=0, $off=0) {
		if (is_array($b)) {
			if ($len==0)
				$len=count($b);
			$j=$len+$off;
			for ($i=$off;$i<$j;$i++) {
				$this->writeObject($b[$i]);
			}
		} else {
			$this->writeObject($b);
		}
	}
	public function close() {
		$this->stream->close();
	}
}
/**
 * @author Tobias Marstaller
 * @desc Provides the option to write raw bytes to a file
*/
class FileOutputStream extends RawOutputStream implements OutputStream, Closable {
	private $streamMetaData;
	protected $closed=false;
	/*
		FileOutputStream(File $f);
		FileOutputStream(TemporaryFile $f);
		FileOutputStream(File $f, boolean $append);
		FileOutputStream(TemporaryFile $f, boolean $append);
		FileOutputStream(String $URI);
		FileOutputStream(String $URI, boolean $apppend);
		 * @param -String/(Temporary)File $file The file/URI to write to
		 * @param -boolean $append Weather to append all contents
	*/
	public function __construct($file, $append=false) {
		if (is_string($file)) {
			$file=new File($file);
		} else if (!$file instanceof File) {
			throw new IOException("Illegal argument \$file");
		}
		if ($file->isDirectory()) {
			throw new IOException("Given file is a directory");
		}
		if (!$file->exists()) {
			$file->create();
		}
		if ($file instanceof TemporaryFile) {
			$this->handle=$file->getHandle();
		} else {
			if ($append) {
				$this->handle=fopen($file->getAbsolutePath(), "a");
			} else {
				if (substr(PHP_OS, 0, 3)=="WIN")
					$this->handle=fopen($file->getAbsolutePath(), "w");
				else
					$this->handle=fopen($file->getAbsolutePath(), "w+");
			}
		}
		$this->streamMetaData=stream_get_meta_data($this->handle);
		if ($append && $file instanceof TemporaryFile)
			fseek($this->handle, $this->streamMetaData["size"]);
	}
	public function close() {
		if ($this->closed)
			throw new IOException("This stream has already been closed.");
		$this->closed=true;
		@$c=fclose($this->handle);
		$this->handle=null;
		if (!$c) {
			throw new IOException("Failed to close the stream;".$php_errormsg);
		}
	}
}
class EncryptedInputStream extends RawInputStream implements InputStream, Closable {
	const DES=MCRYPT_DES;
	const BLOWFISH=MCRYPT_BLOWFISH;
	const GOST=MCRYPT_GOST;
	const THREEWAY=MCRYPT_THREEWAY;
	const WAKE=MCRYPT_WAKE;
	const XTEA=MCRYPT_XTEA;
	const SKIPJACK=MCRYPT_SKIPJACK;
	
	const MODE_ECB="ecb";
	const MODE_CBC="cbc";
	const MODE_CFB="cfb";
	const MODE_OFB="ofb";
	const MODE_STREAM="stream";
	
	private $cipher;
	private $mode;
	private $key;
	private $iv;
	private $stream;
	private $readBufferSize;
	private $storedBufferSize;
	private $currentBufferPos=0;
	private $buffer=array();
	
	public static function createIV($algo, $mode) {
		$iv_size=mcrypt_get_iv_size($algo, $mode);
		return mcrypt_create_iv($iv_size, MCRYPT_RAND);
	}
	
	public function __construct(OutputStream $stream, $algo, $mode, $key, $iv=null, $bufferSize=32) {
		$this->stream=$stream;
		switch ($algo) {
			default:
				throw new CryptoException($algo, $mode, "Unknown algorithm");
			case MCRYPT_DES: case MCRYPT_BLOWFISH: case MCRYPT_GOST: case MCRYPT_THREEWAY: 
			case MCRYPT_WAKE: case MCRYPT_XTEA: case MCRYPT_SKIPJACK:
				$this->cipher=$algo;
		}
		switch ($mode) {
			default:
				throw new CryptoException($algo, $mode, "Unknwon mode");
			case "ecb": case MCRYPT_MODE_ECB:
			case "cbc": case MCRYPT_MODE_CBC:
			case "cfb": case MCRYPT_MODE_CFB:
			case "ofb": case MCRYPT_MODE_OFB:
			case "stream": case MCRYPT_MODE_STREAM:
				$this->mode=$mode;
		}
		$this->key=$key;
		if ($iv==null) $iv=self::createIV($algo, $mode);
		$this->iv=$iv;
		$this->bufferSize=$bufferSize;
	}
	public function getIV() {
		return $this->iv;
	}
	public function getAlgorithmUsed() {
		return $this->cipher;
	}
	public function getMode() {
		return $this->mode;
	}
	public function close() {
		$this->key=null;
		$this->iv=null;
		$this->buffer=null;
		$this->stream->close();
	}
	private function refillBuffer() {
		$this->buffer=array();
		$this->currentBufferPos=0;
		for ($i=0;$i<$this->readBufferSize;$i++) {
			$this->buffer[$i]=chr($this->stream->read());
		}
		$data=implode("", $this->buffer);
		@$data=mcrypt_decrypt($this->cipher, $this->key, $data, $this->mode, $this->iv);
		if ($data=="") {
			throw new CryptoException($this->cipher, $this->mode, "Failed to decrypt: ".$php_errmsg);
		}
		$this->storedBufferSize=strlen($data);
		for ($i=0;$i<$this->storedBufferSize;$i++) {
			$this->buffer[$i]=$data[$i];
		}
	}
	private function appendToBuffer($char) {
		if ($this->currentBufferPos==$this->bufferSize) {
			$this->flush();
		}
		$this->buffer[$this->currentBufferPos++]=$char;
	}
	public function read(array &$ar=null, $len=null, $off=0) {
		if ($ar!=null) {
			if (is_array($ar)) {
				$j=$len+$off;
				for ($i=$off;$i<$j;$i++) {
					$ar[$i]=$this->read();
				}
			} else {
				throw new IOException("Unsupported data-type ".gettype($char)."; Use DataOutputStream for this");
			}
		} else {
			if ($this->currentBufferPos==$this->storedBufferSize) {
				$this->refillBuffer();
			}
			return $this->buffer[$this->currentBufferPos++];
		}
	}
}
class EncryptedOutputStream implements OutputStream, Closable, Flushable {
	const DES=MCRYPT_DES;
	const BLOWFISH=MCRYPT_BLOWFISH;
	const GOST=MCRYPT_GOST;
	const THREEWAY=MCRYPT_THREEWAY;
	const WAKE=MCRYPT_WAKE;
	const XTEA=MCRYPT_XTEA;
	const SKIPJACK=MCRYPT_SKIPJACK;
	
	const MODE_ECB="ecb";
	const MODE_CBC="cbc";
	const MODE_CFB="cfb";
	const MODE_OFB="ofb";
	const MODE_STREAM="stream";
	
	private $cipher;
	private $mode;
	private $key;
	private $iv;
	private $stream;
	private $bufferSize;
	private $currentBufferPos=0;
	private $buffer=array();
	
	public static function createIV($algo, $mode) {
		$iv_size=mcrypt_get_iv_size($algo, $mode);
		return mcrypt_create_iv($iv_size, MCRYPT_RAND);
	}
	
	public function __construct(OutputStream $stream, $algo, $mode, $key, $iv=null, $bufferSize=32) {
		$this->stream=$stream;
		switch ($algo) {
			default:
				throw new CryptoException($algo, $mode, "Unknown algorithm");
			case MCRYPT_DES: case MCRYPT_BLOWFISH: case MCRYPT_GOST: case MCRYPT_THREEWAY: 
			case MCRYPT_WAKE: case MCRYPT_XTEA: case MCRYPT_SKIPJACK:
				$this->cipher=$algo;
		}
		switch ($mode) {
			default:
				throw new CryptoException($algo, $mode, "Unknwon mode");
			case "ecb": case MCRYPT_MODE_ECB:
			case "cbc": case MCRYPT_MODE_CBC:
			case "cfb": case MCRYPT_MODE_CFB:
			case "ofb": case MCRYPT_MODE_OFB:
			case "stream": case MCRYPT_MODE_STREAM:
				$this->mode=$mode;
		}
		$this->key=$key;
		if ($iv==null) $iv=self::createIV($algo, $mode);
		$this->iv=$iv;
		$this->bufferSize=$bufferSize;
	}
	public function getIV() {
		return $this->iv;
	}
	public function getAlgorithmUsed() {
		return $this->cipher;
	}
	public function getMode() {
		return $this->mode;
	}
	public function close() {
		$this->key=null;
		$this->iv=null;
		$this->buffer=null;
		$this->stream->close();
	}
	private function resetBuffer() {
		$this->buffer=array();
		$this->currentBufferPos=0;
	}
	public function flush() {
		$buffer=array_slice($this->buffer, 0, $this->currentBufferPos);
		$data=implode("", $buffer);
		@$data=mcrypt_encrypt($this->cipher, $this->key, $data, $this->mode, $this->iv);
		if ($data=="") {
			throw new CryptoException($this->cipher, $this->mode, "Failed to encrypt: ".$php_errmsg);
		}
		$j=strlen($data);
		for ($i=0;$i<$j;$i++) {
			$this->stream->write($data[$i]);
		}
		$this->resetBuffer();
	}
	private function appendToBuffer($char) {
		if ($this->currentBufferPos==$this->bufferSize) {
			$this->flush();
		}
		$this->buffer[$this->currentBufferPos++]=$char;
	}
	public function write($char, $len=null, $off=null) {
		if ($off==null) $off=0;
		if (is_string($char)) {
			if (strlen($char)==1)
				$this->appendToBuffer($char);
			else {
				if ($len==null) $len=strlen($char);
				$j=$len+$off;
				for ($i=$off;$i<$j;$i++) {
					$this->appendToBuffer($char[$i]);
				}
			}
		} elseif (is_int($char)) {
			$this->appendToBuffer(chr($char));
		} elseif (is_array($char)) {
			$j=$len+$off;
			for ($i=$off;$i<$j;$i++) {
				$this->appendToBuffer($char[$i]);
			}
		} else {
			throw new IOException("Unsupported data-type ".gettype($char)."; Use DataOutputStream for this");
		}
	}
	public function setBufferSize($newsize) {
		if ($newsize<$this->currentBufferPos) {
			$this->flush();
		}
		$this->bufferSize=$newsize;
	}
}
class CryptoException extends IOException {
	protected $message;
	public function __construct($algo, $mode, $message) {
		$msg="";
		switch ($algo) {
			case MCRYPT_DES:
				$msg="(DES;";
				break;
			case MCRYPT_BLOWFISH:
				$msg="(Blowfish;";
				break;
			case MCRYPT_GOST:
				$msg="(Gost;";
				break;
			case MCRYPT_THREEWAY:
				$msg="(Threeway;";
				break;
			case MCRYPT_WAKE:
				$msg="(Wake;";
				break;
			case MCRYPT_XTEA:
				$msg="(XTEA;";
				break;
			case MCRYPT_SKIPJACK:
				$msg="(Skipjack;";
				break;
			default:
				if (is_string($algo))
					$msg="(".$algo.";";
				else
					$msg="(unknwon;";
		}
		switch ($mode) {
			case "ecb": case MCRYPT_MODE_ECB:
				$msg.="ECB)";
			break;
			case "cbc": case MCRYPT_MODE_CBC:
				$msg.="CBC)";
			break;
			case "cfb": case MCRYPT_MODE_CFB:
				$msg.="CFB)";
			break;
			case "ofb": case MCRYPT_MODE_OFB:
				$msg.="OFB)";
			break;
			case "stream": case MCRYPT_MODE_STREAM:
				$msg.="STREAM)";
			break;
			default:
				if (is_string($algo))
					$msg.=$algo.")";
				else
					$msg.="unknwon)";
		}
		$this->message=$msg." ".$message;
	}
}
/**
 * @author Tobias Marstaller
 * @desc Provides the option to write characters to a stream
*/
class PrintWriter implements Closable, Flushable {
	/**
	 * @param -OutputStream $stream The stream the formatted bytes are written to
	*/
	private $stream;
	/**
	 * @param -boolean $auto_flush Weather the  buffer should be flushed immideiately
	*/
	private $auto_flush;
	/**
	 * @param $buffer Contains chars
	*/
	protected $buffer=array();
	/**
	 * @param -String/File/OutputStream $object If its a string, a file is constructed from it. If its a file, a stream is constructed from it.
	 * @param -boolean $autoflush Weather this stream should be immediately flushed
	*/
	public function __construct($object, $autoflush=true) {
		if (is_string($object))
			$object=new File($object);
		if ($object instanceof File) {
			if (!$object->exists()) {
				throw new IOException("File not found.");
			}
			$object=new FileOutputStream($object);
		}
		if (!$object instanceof OutputStream) {
			$object=new RawOutputStream($object);
		}
		if (!is_bool($autoflush))
			throw new IOException("Illegal argument \$autoflush.");
		$this->auto_flush=$autoflush;
		$this->stream=$object;
	}
	/**
	 * @desc Closes the underlying stream without flushing it
	*/
	public function close() {
		if ($this->stream instanceof Closable) {
			$this->stream->close();
		} else {
			throw new IOException("This stream does not support closing.");
		}
	}
	/**
	 * @desc Flushes the buffer (if filled) to the stream
	*/
	public function flush() {
		$b=$this->buffer;
		$this->buffer=array();
		foreach ($b as $byte) {
			if (!is_string($byte))
				$byte=chr($byte);
			$this->stream->write($byte);
		}
	}
	/**
	 * @desc Writes a byte to the stream
	 * @param -byte $obj
	*/
	private function write($obj) {
		if ($this->auto_flush)
			$this->stream->write(chr($obj));
		else {
			$this->buffer[]=$obj;
			if ($obj=='\n')
				$this->flush();
		}
	}
	/**
	 * @desc Writes a character/string to the underlaying stream
	*/
	public function _print($o) {
		if ($o===true || $o===false)
			$this->_print($o ? 'true' : 'false');
		else if (is_string($o) || is_array($o)) {
			if (is_string($o))
				$j=strlen($o);
			else
				$j=count($o);
			for ($i=0;$i<$j;$i++) {
				$this->write(ord($o[$i]));
			}
		} else if (is_int($o) || is_long($o) || is_double($o)) {
			$this->_print("".$o);
		} else if (is_object($o)) {
			if (method_exists($o, "__toString")) {
				$this->_print($o->__toString());
			} else
				throw new IOException("Unable to convert instanceof ".get_class($o)." to string.");
		} else {
			throw new IOException("Illegal argument \$o.");
		}
		if ($this->auto_flush)
			$this->flush();
	}
	/**
	 * @desc Writes a character/string to the underlaying stream and appends a new line
	*/
	public function println($obj=null) {
		if ($obj!==null) {
			$this->_print($obj);
		}
		$this->_print("\n");
	}
	
}
/**
 * @author Tobias Marstaller
 * @desc Creates a Socket to the given computer/port and provides stream-interfaces.
*/
class Socket implements Closable {
	/**
	 * @param -resourcce $handle The PHP-Socket
	 * @param -RawInputStream $in_stream A RawInputStream constructed from $handle
	 * @param -RawOutputStream $out_stream A RawOutputStream constructed from $handle
	 * @param -boolean $closed Weather this socket is closed
	 * @param -String $url The URI this Socket is connected to
	 * @param -int $port This sockets target port
	*/
	private $handle;
	private $in_stream;
	private $out_stream;
	private $closed=true;
	private $url;
	private $port;
	/**
	 * @param -String $URL The url/ip to connect to
	 * @param -int $port The target port to connect to
	 * @param -int $timeout The sockets connection timeout in milliseconds
	*/
	public function __construct($URL, $port, $timeout=-1) {
		if ($port<0 || $port>65535)
			throw new IOException("Invalid port number");
			
		if ($timeout==-1)
			$timeout=ini_get("default_socket_timeout");
		
		$this->url=$URL;
		$this->port=$port;
		$error="";
		$errnr=-1;
		try {
			$this->handle=fsockopen($URL, $port, $errnr, $error, $timeout);
			if (!$this->handle) {
				throw new IOException("(".$errnr.") ".$error);
			}
			$this->closed=false;
		} catch (Exception $ex) {
			throw new IOException("Invalid domain name");
		}
	}
	public function __destruct() {
		if (!$this->closed)
			$this->close();
	}
	/**
	 * @desc Returns an InputStream to read information from
	 * @return InputStream
	*/
	public function getInputStream() {
		if ($this->closed)
			throw new IOException("Socket closed");
		
		if ($this->in_stream==null)
			$this->in_stream=new RawInputStream($this->handle);

		return $this->in_stream;
	}
	/**
	 * @desc Returns an OutputSteam to write inforation to
	 * @return OutputStream
	*/
	public function getOutputStream() {
		if ($this->closed)
			throw new IOException("Socket closed");
		
		if ($this->out_stream==null)
			$this->out_stream=new RawOutputStream($this->handle);
		
		return $this->out_stream;
	}
	/**
	 * @desc Closes this socket
	*/
	public function close() {
		if ($this->closed)
			throw new IOException("Socket closed");
		
		$this->closed=true;
		fclose($this->handle);
	}
	/**
	 * @desc Returns the port this socket is connected to
	 * @return int
	*/
	public function getPort() {
		return $this->port;
	}
	/**
	 * @desc Returns the URI/IP this socket is connected to
	 * @return String
	*/
	public function getURI() {
		return $this->url;
	}
	/**
	 * @return boolean;
	*/
	public function isConnected() {
		return !$this->closed;
	}
	/**
	 * @desc E.g.: php.io.Socket[www.google.com:80]:connected or php.io.Socket[127.0.0.1:4185]:disconnected
	*/
	public function __toString() {
		$str="php.io.Socket[".$this->url
			.":".$this->port."]:";
		if ($this->closed)
			$str.="dis";
		return $str."connected";
	}
}
abstract class Charset {
	public $decodeBufferSize=0;
	public $encodeBufferSize=0;
	/*
		@desc Decodes the given bytes
		@var -Array $buffer Array to decode; count($buffer) must be $instance->bufferSize
		@return String
	*/
	public function decodeChars(array $buffer) {
		if (count($buffer)!=$this->bufferSize)
			throw new IOException("Illegal buffer size.");
		return "";
	}
	public function encodeChar($char) {
		return array(ord($char));
	}
	public function getName() {
		return "CorruptInstance!";
	}
	public static function getInstance($encoding, $BOM=null) {
		switch ($encoding) {
			default:
				throw new IOException("Invalid encoding given (".$encoding.")");
			case "UTF-8":
				return new UTF8Charset();
			case "UTF-16BE":
				return new UTF16BECharset();
			case "UTF-16LE":
				return new UTF16LECharset();
			case "UTF-16":
				if ($BOM===null)
					throw new IOException("Byte order mark argument missing.");
				return new UTF16Charset($BOM);
			case "ASCII-7":
			case "ISO646-US":
				return new ASCII7Charset();
		}
	}
}
class UTF8Charset extends Charset {
	public $encodeBufferSize=1;
	public $decodeBufferSize=1;
	public function decodeChars(array $buffer) {
		return array(chr($buffer[0]));
	}
	public function encodeChars(array $chars) {
		foreach ($chars as $key=>$char) {
			$chars[$key]=ord($char);
		}
		return $chars;
	}
	public function getName() {
		return "UTF-8";
	}
}
class ASCII7Charset extends Charset {
	public $decodeBufferSize=7;
	public $encodeBufferSize=8;
	public function decodeChars(array $bytes) {
		if (count($bytes)!=7)
			throw new IOException("Invalid input size.");
		$bits="";
		for ($i=0;$i<7;$i++) {
			$str=decbin($bytes[$i]);
			$append=strlen($str)-7;
			if ($append>0) {
				$str=str_repeat('0', $append).$str;
				$bits.=$str;
			}
			$bits.=$str;
		}
		$ar=array();
		for ($i=0;$i<8;$i++) {
			$byte=substr($bits, $i*7, 7);
			$ar[]=chr(bindec($byte));
		}
		return $ar;
	}
	public function encodeChars($bytes) {
		if (is_array($bytes))
			$bytes=implode("", $bytes);
		if (!is_string($bytes))
			throw new IOException("Invalid argument.");
		$bits="";
		for ($i=0;$i<8;$i++) {
			$str=decbin(ord($bytes[$i]));
			$append=strlen($str)-6;
			if ($append>0) {
				$str=str_repeat('0', $append).$str;
			}
			$bits.=$str;
		}
		$ar=array();
		for ($i=0;$i<7;$i++) {
			$byte=substr($bits, $i*8, 8);
			$byte=substr($byte, 1);
			$ar[]=bindec($byte);
		}
		return $ar;
	}
}
?>