<?php
/**
* Snapshot File Stream Wrapper
*
* This class creates a new stream type that can be used when reading/writing files.
* The class creates snapshot versions of files upon changes, and can additionally
* retreive the contents of said file when given an arbitrary date.
*
* When you call the stream wrapper using snapshot://path/to/file, it will behave as if you
* are accessing the normal local filesystem. The only difference comes into effect when
* you attempt to modify the file, either by writing to it with fwrite or fput, or
* opening the file with a mode that alters the file such as mode 'w'. An unmodified
* copy of the file will be created with an dated extension. For example, file.txt would
* become file.txt.20061231153000. example1.php shows this functionality.
*
* When reading, specify your normal file path (eg. snapshot://path/to/file). You can however
* now additionally specify a date and time to view the file with. For example, accessing
* snapshot://path/to/file+20061231153500 would specify that you want to view the file as it
* was at 15:35 on 31/Dec/2006. NOTE: You cannot write to a file using this mode. Any
* to use a mode other than 'r' will result in an error. example2.php shows this
* functionality.
*
* @author Rick Hodger <hide@address.com>
* @version 1.0
* @package filesnap
* @example example1.php
* @example example2.php
*/
class filesnap {
var $pos = 0;
var $fp = 0;
var $madediff = false;
var $src = '';
function stream_open($path, $mode, $options, &$opened_path) {
$this->madediff=false;
$path = substr($path,11);
if (substr_count($path,'+') > 0) {
// find a specific dated version
if ($mode==='r') {
list($this->src,$get)=explode('+',$path);
foreach (glob($this->src.".*") as $x) {
$a[]=substr($x,strlen($this->src)+1);
}
$current=date('YmdHis',filemtime($this->src));
sort($a);
if($get >= $current) {
// do nothing, current src file is correct
} elseif ($get <= $a[0]) {
$this->src = $this->src.'.'.$a[0];
} else {
// somewhere in between
$i=0;
foreach($a as $b) {
if ($get > $b) {
break;
}
}
$this->src = $this->src.'.'.$b;
}
} else {
// writing to a dated file is bad, return a failure on anything but a read attempt
return false;
}
} else {
$this->src=$path;
}
$opened_path=$this->src;
if ($mode==='w' || $mode==='w+') {
$this->dodiff();
}
if ($mode==='r' || !file_exists($this->src)) {
$this->madediff=true;
}
if ($this->fp = fopen($this->src,$mode)) {
$this->pos = ftell($this->fp);
return true;
} else {
return false;
}
}
function stream_close() {
fclose($this->fp);
}
function stream_read($count) {
return fread($this->fp,$count);
}
function dodiff() {
if ($this->madediff==false) {
copy($this->src,$this->src.'.'.date('YmdHis'));
$this->madediff=true;
}
}
function stream_write($data) {
$this->dodiff();
if (fwrite($this->fp,$data) != false) {;
$this->pos = $this->pos + strlen($data);
return strlen($data);
} else {
return false;
}
}
function stream_eof() {
return feof($this->fp);
}
function stream_tell() {
return $this->pos;
}
function stream_seek($offset, $whence) {
if (fseek($this->fp,$offset,$whence)) {
$this->pos = ftell($this->fp);
return true;
} else {
return false;
}
}
function stream_flush() {
fflush($this->fp);
}
function stream_stat() {
fstat($this->fp);
}
}
stream_wrapper_register('snapshot','filesnap')
or die('Failed to register snapshot protocol!');
?>