<?php
class BigFile
{
protected $file;
/**
* Constructor
* @var: $file Filename
*/
public function __construct($file)
{
$file = trim($file);
if(!file_exists($file) || !is_readable($file))
{
throw new Exception("File {$file} does not exist or is not readable");
}
$this->_file = $file;
}
/**
* Read data beginning at $offset. If $offset is set to null (by default), BigFile::read() starts at line 0. If $lines is set to null, BigFile::read() reads from $offset until the end of the file.
* @var: $offset int Line
* @var: $lines int Lines to read
*/
public function read($offset = null, $lines = null)
{
$offset = $offset === null ? 0 : intval($offset);
$fp = fopen($this->_file, 'r');
$line = 0;
$data = '';
if($offset > 0)
{
// Seek to offset line
fseek($fp, $this->_seekLine($offset));
}
while(!feof($fp))
{
if($lines !== null && $line >= $lines)
{
break;
}
$data .= fgets($fp);
$line += 1;
}
fclose($fp);
return $data;
}
/**
* Write data to file. If $offset is set to null, BigFile::write() will append data at the end.
* @var: $data str Content
* @var: $offset int Line
*/
public function write($data, $offset = null)
{
if($offset === null)
{
$fp = fopen($this->_file, 'a');
}
else
{
$fp = fopen($this->_file, 'r+');
}
$dataAfter = '';
if($offset !== null)
{
fseek($fp, $this->_seekLine($offset));
$dataAfter = fread($fp, filesize($this->_file));
fseek($fp, $this->_seekLine($offset));
}
if(fwrite($fp, $data . $dataAfter) === false)
{
throw new Exception("Could not write data to file {$this->_file}");
}
fclose($fp);
}
/**
* Same as BigFile::write(), but inserts a linebreak at the end of $data
* @var: $data str Content
* @var: $offset int Line
*/
public function writeLine($data, $offset = null)
{
$data = substr($data, 0, -2) == "\n" ? $data : $data . "\n";
$this->write($data, $offset);
}
/**
* Deletes data beginning at $offset. If $lines is set to null, one line will be deleted
* @var: $offset int Line
* @var: $lines int Number of lines to delete
*/
public function delete($offset = null, $lines = null)
{
$dataBefore = $this->read(0, $offset - 1);
$dataAfter = $this->read($offset + $lines);
$fp = fopen($this->_file, 'w');
fwrite($fp, $dataBefore . $dataAfter);
fclose($fp);
}
/**
* Based on a version by mhinks at gmail dot com @ http://www.php.net/fseek
*/
protected function _seekLine($offset)
{
$fp = fopen($this->_file, 'r');
flock($fp, LOCK_SH);
fseek($fp, 0, SEEK_END);
$high = ftell($fp);
$low = 0;
while($low < $high)
{
$half = floor(($low + $high) / 2);
// Let's seek to the middle of the file
fseek($fp, $half);
if($half != 0)
{
// Just jumping at the end of the actual line
fgets($fp);
}
// Get the actual position for later fseeks
$position = ftell($fp);
fseek($fp, 0);
// Let's see, where we are
$actLine = $position > 0 ? substr_count(fread($fp, $position), "\n") : 0;
// Seek back to the old position
fseek($fp, $position);
// Check, if we got the line
if($actLine == $offset)
{
// Return the position and close the filehandler
fclose($fp);
return $position;
}
// Not the expected line
else
{
// We are behind the offset line
if ($offset < $actLine)
{
$high = $half - 1;
}
// We are before the offset line
else
{
$low = $half + 1;
}
}
}
// Line not found
flock($fp, LOCK_UN);
fclose($fp);
return false;
}
}
?>