PHP Classes

File: php.io.php

Recommend this page to a friend!
  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: 12 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; } } ?>