<?php
require_once 'DBTree.php';
class DBApi {
private $dbfile;
private $dbtree;
private $DATA_DEF; // first entry in data_def is used as primary key
private $ENTRY_SIZE;
function __construct( $file) {
$this->dbfile = $file;
$treefile = preg_replace( '/\..*/', '.tree', $file);
$this->dbtree = new DBTree( $treefile);
$this->ENTRY_SIZE = 0;
if( file_exists( $file)) {
// read data definition
$i = 0;
$dd = preg_split( '/;/', $this->dbtree->getValue( 'data_def'));
foreach( $dd as $def) {
if( strlen( $def)>0) {
$split = preg_split( '/:/', $def);
$l = strpos( $split[1], '[');
$r = strlen( $split[1])-1;
$size = substr( $split[1], $l+1, $r-$l-1);
$this->ENTRY_SIZE += $size;
$this->DATA_DEF[$i] = array( $split[0] => $split[1]);
$i++;
}
}
}
else {
$this->DATA_DEF = array( );
}
}
public function __set( $name, $value) {
switch( $name) {
case 'dbfile':
$this->dbfile = $value;
break;
case 'dbtree':
$this->dbtree = $value;
break;
case 'DATA_DEF':
$this->DATA_DEF = $value;
break;
case 'ENTRY_SIZE':
$this->ENTRY_SIZE = $value;
break;
}
}
public function __get( $name) {
switch( $name) {
case 'dbfile':
return $this->dbfile;
case 'dbtree':
return $this->dbtree;
case 'DATA_DEF':
return $this->DATA_DEF;
case 'ENTRY_SIZE':
return $this->ENTRY_SIZE;
}
}
public function exists( ) {
return count( $this->DATA_DEF)>0;
}
private function getDD( $i) {
if( !$this->exists( ))
return array( );
$j = 0;
foreach( $this->DATA_DEF as $dd) {
if( $i==$j) {
foreach( $dd as $name => $value) {
$l = strpos( $value, '[');
$r = strpos( $value, ']');
$size = substr( $value, $l+1, $r-$l-1);
$type = substr( $value, 0, $l);
return array( 'name' => $name, 'type' => $type, 'size' => $size);
}
}
$j++;
}
return array( );
}
public function createDB( $data_definition) {
if( $this->exists( ))
return;
$dd = '';
$this->ENTRY_SIZE = 0;
$i = 0;
foreach( $data_definition as $name => $value) {
if( $i>0) {
$dd .= ';';
}
$value = strtoupper( str_replace( ' ', '', $value));
$dd .= $name . ':' . $value;
$l = strpos( $value, '[');
$r = strpos( $value, ']');
$size = substr( $value, $l+1, $r-$l-1);
$this->ENTRY_SIZE += $size;
array_push( $this->DATA_DEF, array( $name => $value));
$i++;
}
$treefile = preg_replace( '/\.db/', '.tree', $this->dbfile);
if( file_exists( $treefile)) {
unlink( $treefile);
}
if( file_exists( $this->dbfile)) {
unlink( $this->dbfile);
}
// save data def in root of tree
$this->dbtree->addKey( 'data_def', $dd);
}
public function getEntry( $key) {
if( !$this->exists( )) {
return array( );
}
$keyDD = $this->getDD( 0);
if( strlen( $key)>$keyDD['size'])
$key = substr( $key, 0, $keyDD['size']);
$value = $this->dbtree->getValue( $key);
$entry = array( );
if( $value!==false) {
$BLOCKSTART = $value * $this->ENTRY_SIZE;
$fh = fopen( $this->dbfile, 'r') or die( $php_errormsg);
fseek( $fh, $BLOCKSTART, SEEK_SET);
$i = 0;
$dd = $this->getDD( $i);
while( !empty( $dd)) {
$data = rtrim( fread( $fh, $dd['size']));
$hlp = array( $dd['name'] => $data);
$entry = array_merge( $entry, $hlp);
$i++;
$dd = $this->getDD( $i);
}
fclose( $fh);
}
return $entry;
}
public function updateEntry( $entry) {
if( $this->exists( )) {
$keyDD = $this->getDD( 0);
if( empty( $keyDD))
return;
$key = $entry[$keyDD['name']];
if( strlen( $key)>$keyDD['size'])
$key = substr( $key, 0, $keyDD['size']);
$value = $this->dbtree->getValue( $key);
if( $value!==false) {
$i = 0;
$dd = $this->getDD( $i);
$str = '';
$data = '';
while( !empty( $dd)) {
$str = $entry[$dd['name']];
if( strlen( $str)>$dd['size']) { // input is too long -- trim to correct size
$str = substr( $str, 0, $dd['size']);
}
else { // append ' ' until str has correct size
while( strlen( $str)<$dd['size']) {
$str .= ' '; // fill rest with blanks
}
}
$data .= $str;
$i++;
$dd = $this->getDD( $i);
}
$BLOCKSTART = $value * $this->ENTRY_SIZE;
$filesize = filesize( $this->dbfile);
$fh = fopen( $this->dbfile, 'r+') or die( $php_errormsg);
fseek( $fh, $BLOCKSTART, SEEK_SET);
fwrite( $fh, $data);
fflush( $fh);
fclose( $fh);
}
}
}
public function addEntry( $entry) {
if( $this->exists( )) {
$keyDD = $this->getDD( 0);
if( empty( $keyDD))
return;
$key = $entry[$keyDD['name']];
if( strlen( $key)>$keyDD['size'])
$key = substr( $key, 0, $keyDD['size']);
$value = $this->dbtree->getValue( $key);
if( $value===false) {
$fh = fopen( $this->dbfile, 'a') or die( $php_errormsg);
fseek( $fh, 0, SEEK_END);
$BLOCKSTART = ftell( $fh);
$i = 0;
$dd = $this->getDD( $i);
$loc = 0;
$str = '';
$data = '';
while( !empty( $dd)) {
fseek( $fh, $loc, $BLOCKSTART);
$str = $entry[$dd['name']];
if( strlen( $str)>$dd['size']) { // input is too long -- trim to correct size
$str = substr( $str, 0, $dd['size']);
}
else { // append ' ' until str has correct size
while( strlen( $str)<$dd['size']) {
$str .= ' '; // fill rest with blanks
}
}
$data .= $str;
$loc += $dd['size'];
$i++;
$dd = $this->getDD( $i);
}
fwrite( $fh, $data);
fflush( $fh);
fclose( $fh);
$this->dbtree->addKey( $key, $BLOCKSTART/$this->ENTRY_SIZE);
}
else {
// ignore operation
}
}
}
/* bearbeiten */
public function getKeysByRow( $id) {
if( !$this->exists( )) {
return array( );
}
$i = 0;
$dd = $this->getDD( $i);
$row = -1;
$row_name = '';
$row_type = '';
while( !empty( $dd) && $row==-1) {
if( strcmp( $id, $dd['name'])==0) {
$row = $i;
$row_name = $dd['name'];
$row_type = $dd['type'];
}
$i++;
$dd = $this->getDD( $i);
}
if( strlen( $row)==0 || $row==-1) {
return array( );
}
$allKeys = $this->dbtree->getAllKeys( );
$allKeysByRow = array( );
$dd = $this->getDD( 0);
$key_type = $dd['type'];
if( $row==0) { // requested keys in ascending order
foreach( $allKeys as $k) {
if( strcmp( $k, 'data_def')!=0) {
$allKeysByRow[$k] = $k;
}
}
}
else {
$dd = $this->getDD( $row);
$SIZE = $dd['size'];
$BLOCKSTART = 0;
$REL = 0;
for( $i = 0; $i<$row; $i++) {
$dd = $this->getDD( $i);
$REL += $dd['size'];
}
$fh = fopen( $this->dbfile, 'r') or die( $php_errormsg);
foreach( $allKeys as $k) {
if( strcmp( $k, 'data_def')!=0) {
$value = $this->dbtree->getValue( $k);
if( $value!==false) {
$BLOCKSTART = $value * $this->ENTRY_SIZE + $REL;
fseek( $fh, $BLOCKSTART, SEEK_SET);
$data = rtrim( fread( $fh, $SIZE));
if( isset( $allKeysByRow[$data])) {
if( is_array( $allKeysByRow[$data])) {
$allKeysByRow[$data] = array_merge( $allKeysByRow[$data], array( $k));
}
else {
$hlp = $allKeysByRow[$data];
$allKeysByRow[$data] = array( $hlp, $k);
}
switch( $key_type) {
case 'NUM':
sort( $allKeysByRow[$data], SORT_NUMERIC);
break;
case 'CHR':
sort( $allKeysByRow[$data], SORT_STRING);
break;
}
}
else {
$allKeysByRow[$data] = $k;
}
}
}
}
fclose( $fh);
}
switch( $row_type) {
case 'NUM':
ksort( $allKeysByRow, SORT_NUMERIC);
break;
case 'CHR':
ksort( $allKeysByRow, SORT_STRING);
break;
}
return $allKeysByRow;
}
public function removeEntry( $key) {
if( $this->exists( )) {
$keyDD = $this->getDD( 0);
if( strlen( $key)>$keyDD['size'])
$key = substr( $key, 0, $keyDD['size']);
$this->dbtree->removeKey( $key);
}
}
public function printDataDef( ) {
if( !$this->exists( )) {
echo 'the specified db does not even exist.';
return;
}
echo 'data definition of "' . $this->dbfile . '":<br />';
$i = 0;
$dd = $this->getDD( $i);
while( !empty( $dd)) {
echo ($i+1) . '. ' . $dd['name'] . ' : ' . $dd['type'] . '[' . $dd['size'] .']<br />';
$i++;
$dd = $this->getDD( $i);
}
echo '<br />';
}
};
?>