<?php
/* Name: dbff.php
Author: Jerry Mattsson
Version: 0.99 See the Text-file, dbff.doc for Usage and License info.
Created: March-2005, Updated july-Aug 2005, Performance changes May 2006
Copyright(©) 2005 Jerry Mattsson, www.Timehole.com, Released under GPL2
Oct-06 v0.97 fixed int,number,date check bug, added natsort variants,
some function name changes, more array tests, error texts and encryption trace msg. /jm
Oct-06 v0.98 Another date bug fixed, mktime gen err monthlist in parse,
changed default logswitch to monthly, Added LCMP, "like compare" Case ignore begin/like search /jm
Nov-06 v0.99 date2str accepts datetime without errors, More filetests and change filetime chk
and reRead in read/write functions. Minor code cleanup.
*/
require_once 'dbff_errm.php';
$dbfferr = '';
//$dbff_dir = /// Set globally somewhere else if desired
/// //////////////////////
/// "DB" File functions //
/// //////////////////////
class dbff {
/* *********************************************************************************
Reads and writes file(s) in and out of an record array in a relational fashion.
Use it with care and at your own risk, do not blame me if it fails. I do not take any
responsible for any consequences or problems that might be caused by errors in this code.
Uses flatfile(s) tagged + delimiter/csv style, non quoted format, See Doc file for more info
Public functions that can be used in the application is:
read, insert, delete, update, commit, rollback, getRec and reRead, updByRn, delByRn, GetByRn
Functions that needs a record definition:
select, selectSort, reselect, aselect, selectGet, getSelectedRn,
updateSelected, deleteSelected, getRnByKey
Utility Functions: NN, PK, UK, FK, size, type, usage
********************************************************************************* */
var $datetimefmt= 'Y-M-d H:i:s';/// DateTime "Display" format stored as yyyymmddHHMMSS, date() syntax
var $datefmt = 'Y-M-d'; /// Date "Display" format stored as yyyymmdd, date() syntax
var $file = NULL; /// File name
var $delimiter = ';'; /// Field delimiter in file records (or good choice ... §)
var $dir_delim = '/'; /// Directory delimiter, linux !
var $keyfld = 0; /// Key field number, Default = 0 if pk not defined
var $recdef = FALSE; /// Optional record specification
var $changelog = TRUE; /// Log all changes to log-file xxx_log ( + optional date str )
var $logswitch = 'M'; /// Set to m,w,d or h for a switch of logfile at month, week, day or hour
var $tblname = NULL; /// Tablename (set if defined in record def )
var $maxline = 4096; /// Max file length, greater than sum of all fields of biggest record
var $maxlockwait= 10; /// Max time to wait for a lock until fail ( seconds )
var $tag = 'R'; /// Record tag string
var $rectype = FALSE; /// 2=Tag each rec with op and timestamp, 1=No timestamp, False=def =>2
var $errstk = array (); /// Stack of Error messages
/// Variables below is maintined by the class functions
var $comments = array (); /// Comment lines in beginning of file
var $recs = array (); /// Holds all records of a file
var $recmap = array (); /// One element / record with status, O(riginal),I(nserted),U(pdated)
var $tsrecs = array (); /// Holds old timestamp for record in fmt I=1234567890
var $rowcnt = 0; /// Record count in array "recs"
var $lastInsRn = NULL;
var $fp = FALSE; /// File pointer
var $maxkey = 0; /// Max id/key for record in insert
var $seqkey = FALSE; /// Set if PK has USAGE=SEQUENCE for auto incremented numeric key
var $filetime = NULL; /// File modification time at read
var $operation = NULL; /// INSERT or MIXED
var $mode = NULL; /// Lock mode
var $is_read = FALSE; /// File is read into array
var $is_open = FALSE;
var $ctlcnt = 0; /// Count of Control Records
/// Used if record def is used
var $uc_recdef = NULL; /// Upper Case recdef, Set when recdef is parsed
var $selrecs = array (); /// Pointer array to selected records
var $selptr = array (); /// Pointer to next "select record" to get
var $recflds = array (); /// Upper case field names for table as fldname=>pos
var $fldnames = array (); /// Holds fieldnames as entered after record definition is parsed
var $pkkeys = array (); /// Inverted array for pk-values, points to rec in array
var $ukkeys = array (); /// As pkkeys but for uk and with fldname as added key
var $trace = FALSE; /// Internal trace, prints trace stuff
var $trace_level= 1; /// Trace level, prints more or less trace stuff. 1(less)-3(more)
var $month3name = array (); /// Locale short month names, jan, feb ...
/// wrapper functions for an "external" encryption functions
var $pw = '';
var $crypt_class = 'rc4crypt.php';
var $is_encrypted = FALSE;
/// Use your favorite encryption or the gpl rc4 class from sourceforge.net,
/// Assumes that there are one encrypt and one decrypt function in the class
function scramble_init ($pwd) {
if (empty($pwd)) return;
if (!$this->is_encrypted) {
require_once $this->crypt_class; // check for fail/exist and if return;
$this->scramble = new rc4crypt;
$this->pw = $pwd;
$this->is_encrypted = TRUE;
}
}
function scrambleData ($data) {
if ($this->is_encrypted) return base64_encode($this->scramble->encrypt($this->pw,$data));
return $data;
}
function descrambleData ($data) {
if ($this->is_encrypted) return $this->scramble->decrypt($this->pw,base64_decode($data));
return $data;
}
/// //////////////////////////////////////////////////
/// //// RECORD DEFINITION DEPENDANT FUNCTIONS ///////
/// //// Requires a record definition to work ///////
/// //////////////////////////////////////////////////
function select ($fname=NULL, $value=NULL, $op=NULL) {
/// Returns number of records found with a field name search, Requires a record definition
if ($this->trace) $this->errm('T',802);
if (!$this->is_read) $this->read();
if (!$this->hasRecDef()) { $this->errm('E',105); return NULL; }
$allrecs = $this->selectAll();
$this->selptr = 0;
if (is_null($fname)) {
$this->selrecs = $allrecs; return count($allrecs); } /// Select all records
if (is_null($fno = $this->fldname2fldno($fname)))
{ $this->errm('E',103,$fname); return NULL; }
if (is_null($value)) { $this->errm('E',104,$fname); return NULL; }
$this->selrecs = $this->searchRecs($fname, $value, $allrecs, $op);
if ($this->selrecs==NULL) return 0;
else return count($this->selrecs);
} /// select
function selectSort ($fname, $sort=NULL) {
/// Sorts selected records, sort can be ASC or DESC
if (is_null($fno = $this->fldname2fldno($fname))) { $this->errm('E',110,$fname); return FALSE; }
if (($j=count($this->selrecs))==0) return TRUE;
for ($i=0;$i<$j; $i++) $r[$this->selrecs[$i]] = $this->recs[$this->selrecs[$i]][$fno];
if (!is_null($sort)) $sort = strtoupper($sort);
if ($sort=='DESC') arsort($r);
elseif ($sort=='NATSORT') natsort($r);
elseif ($sort=='NATCASESORT') natcasesort($r);
else asort($r);
reset ($r);
for ($i=0;$i<$j; $i++) { $q[$i] = key($r); next($r); }
$this->selrecs = $q;
$this->selptr = 0;
return TRUE;
} /// selectSort
function reselect ($fname, $value, $op=NULL) {
/// "And function" select, selects from allready selected records, works as select
if (is_null($this->selrecs)) { if ($this->trace) $this->errm('T',803); return 0; }
if (is_null($this->fldname2fldno($fname))) { $this->errm('E',121,$fname); return 0; }
if ($this->trace) $in = count($this->selrecs);
$recs = $this->searchRecs($fname, $value, $this->selrecs, $op);
$out = count($recs);
$this->selrecs = $recs;
$this->selptr = 0;
if ($this->trace) $this->errm('T',804,$in,$out,$value,$fname);
if (is_null($this->selrecs)) return 0;
else return $out;
} /// reselect
function aselect ($fname, $value, $op=NULL) {
/// "Or function" select, selects from all records, and adds result set to selected records
if (is_null($this->fldname2fldno($fname))) { $this->errm('E',130,$fname); return 0; }
$allrecs = $this->selectAll();
$recs = $this->searchRecs($fname, $value, $allrecs, $op);
$in = count($this->selrecs);
for ($i=0; $i<count($recs); $i++) {
if ($in>0 && in_array($recs[$i],$this->selrecs)) continue; /// Already in set
$this->selrecs[] = $recs[$i]; /// Add found rec
}
$out = count($this->selrecs);
$this->selptr = 0;
if ($this->trace) $this->errm('T',806, $in, $out, $value, $fname);
if (is_null($this->selrecs)) return 0;
else return $out;
} /// aselect
function selectGet ($namedIdx=TRUE) {
/// Returns "next" record from a previous select search or NULL
$max = count($this->selrecs);
if ($this->selptr >= $max || $max < 1) {
$this->selptr = 0;
if ($this->trace) $this->errm('T',808);
return NULL;
}
$rec = $this->formatRec ($this->selrecs[$this->selptr], $namedIdx);
$this->selptr++;
if ($this->trace) $this->errm('T',810,$this->selptr,NULL,NULL,NULL,2);
return $rec;
} /// selectGet
function getSelectedRn () {
/// Returns current record ptr from a previous select search or NULL
$max = count($this->selrecs);
if (is_null($this->selptr) || $this->selptr > $max || $max < 1) return NULL;
$ptr = $this->selptr - 1;
return $this->selrecs[$ptr];
} /// getSelectedRn
function getLastInsRec ($namedIdx=TRUE) {
/// Returns last inserted record number or NULL
return $this->getRecByRn ($this->lastInsRn, $namedIdx);
} /// getLastInsRec
function updateSelected ($fname, $value) {
/// Updates all records selected with new value for fld, Returns no of records updated
if (is_null($this->selrecs)) { $this->errm('E',140); return 0; }
if (is_null($fno = $this->fldname2fldno($fname))) { $this->errm('E',141,$fname); return 0; }
if (is_string($value)) $value = trim($value);
for ($i=0; $i<count($this->selrecs); $i++) {
$rec = $this->recs[$this->selrecs[$i]];
$rec[$fno] = $value;
$this->updByRn ($this->selrecs[$i], $rec);
}
return $i;
} /// updateSelected
function deleteSelected () {
/// Deletes all records selected, returns no of records deleted
if (is_null($this->selrecs)) { $this->errm('E',150); return 0; }
for ($i=0; $i<count($this->selrecs); $i++)
if (!$this->delByRn($this->selrecs[$i])) { $this->errm('E',151); return NULL; }
return $i;
} /// deleteSelected
function selectAll () {
/// Selects all records
$r = array();
if ($this->trace) $this->errm('T',812);
for ($i=0; $i<$this->rowcnt; $i++) if ($this->recmap[$i]<>'D') $r[] = $i;
if ($this->trace) $this->errm('T',814,count($r));
return $r;
}
function getRnByKey ($value, $fno=NULL, $fname=NULL) {
/// Is there a pk or uk to search on? Return recno if found.
if (!is_null($fname)) $fn = $fname;
else {
if (is_null($fno)) $fn = $this->fldno2fldname($this->keyfld);
else $fn = $this->fldno2fldname($fno);
}
$i = NULL;
if (is_string($value)) $value = trim($value);
if ($this->pk($fn) || $this->uk($fn)) {
if ($this->type($fn)=='DATE')
if (!$value=$this->str2date($value)) return NULL; /// InValid date
if ($this->type($fn)=='DATETIME' || $this->type($fn)=='DATETIME')
if (!$value=$this->str2datetime($value)) return NULL; /// InValid date
//settype($value,gettype(current($this->pkkeys))); ?? what key/value ?
if ($this->pk($fn) && isset($this->pkkeys[$value])) $i = $this->pkkeys[$value];
if ($this->uk($fn) && isset($this->ukkeys[$fn][$value])) $i = $this->ukkeys[$fn][$value];
if ($this->trace && !is_null($i)) $this->errm('T',828,$fn);
if (is_null($i) || $this->recmap[$i]=='D') return NULL;
else return $i;
}
return NULL;
} /// getRnByKey
/// Privates ///
function searchRecs ($fname, $value, $selmap=NULL, $op=NULL) {
/// Search fieldname for value, returns matching record(s) in array
if ($this->trace) {
if (empty($value)) $v = ' (NULL) ';
else $v = $value;
$this->errm('T',816,$v,$fname,$op);
}
if (is_null($fno = $this->fldname2fldno($fname))) { $this->errm('E',111,$fname); return NULL; }
if (is_string($value)) $value = trim($value);
$op = $this->chkOp($op);
if (is_null($op)) return NULL;
if ($this->isAttSet($fname,'UPPER')) $value = strtoupper($value);
if ($this->isAttSet($fname,'LOWER')) $value = strtolower($value);
if ($op =='EQUAL') { /// and pk or fk
$i = $this->getRnByKey($value, NULL, $fname);
if (!is_null($i)) {
$recs[] = $i;
if ($this->trace) $this->errm('T',818,$i);
return $recs;
}
}
$recs = $this->scanRecs($value, $fno, $selmap, $op);
if ($this->trace) $this->errm('T',820,count($recs),count($selmap));
return $recs;
} /// searchRecs
function parseRecDef () {
/// Scans definition and makes all keywords upper case and stores field names
global $dbff_dir;
global $dir_delim;
$new = array ();
$fname = "";
$val = NULL;
if ($this->trace) $this->errm('T',822);
if (substr($dbff_dir,strlen($dbff_dir)-1)==$dir_delim) $dir_delim = NULL;
if ($this->hasRecDef()) return TRUE;
if (!is_array($this->recdef)) { $this->errm('E',155); return FALSE; }
// make locale short month names
for ($i=1; $i<13; $i++) $this->month3name[$i] = strtolower(date('M',mktime( 1,1,1,$i,1)));
reset ($this->recdef); // unset ??
while (list($fname, $val) = each ($this->recdef)) {
if (!is_array($val)) {
if (strtoupper($fname)=='TABLE_NAME') $this->tblname = strtoupper($val);
if (strtoupper($fname)=='FILE_NAME')
if (isset($dbff_dir)) $this->file = $dbff_dir.$dir_delim.$val;
else $this->file = $val;
if (strtoupper($fname)=='MINSEQ') { $this->minseq = $val; $this->maxkey = $val; }
if (strtoupper($fname)=='MAXSEQ') $this->maxseq = $val;
if (strtoupper($fname)=='PASSWORD') {
if ($this->trace) $this->errm('T',823);
$this->scramble_init(trim($val));
}
continue;
}
$new[$fname] = $val;
$this->fldnames[] = $fname;
}
if (isset($this->maxseq) && isset($this->minseq) && $this->maxseq < $this->minseq)
$this->errm('E',550,$this->maxseq);
$this->recdef = $new; /// replace with stripped, parsed def
if (!isset($this->tblname)) $this->tblname = 0;
$this->uc_recdef = $this->cnv2uc($this->recdef);
$this->recflds = $this->cnv2uc($this->fldnames);
$this->recflds = array_flip($this->recflds); /// Inversed field list
if (!is_null($pk = $this->getPk())) { /// Find Pk
$this->keyfld = $pk[1];
$atts = $this->getAtts($pk[0]);
if (isset($atts['PK']) && strcmp($atts['PK'],'SEQUENCE')==0) $this->seqkey = TRUE;
}
if ($this->trace) $this->errm('T',824,$this->keyfld);
if ($this->trace && isset($this->maxseq)) $this->errm('T',865,$this->maxseq);
if ($this->trace && isset($this->minseq)) $this->errm('T',866,$this->minseq);
if ($this->trace && $this->hasRecDef()) {
$str = NULL; foreach ($this->fldnames as $k=>$f) $str .= "$k=$f "; $this->errm('T',826,$str); }
return TRUE;
}
function validateRec($vrec, $operation='INSERT', $currec=NULL) {
/// Expects a record with "numeric keys", aka 0=>'value, ....
if ($this->trace) $this->errm('T',830);
if (!$this->hasRecDef() && !$this->parseRecDef() ) return FALSE;
if (count($this->recdef) <> count($vrec)) {
$this->errm('E',240,count($this->recdef),count($vrec));
return FALSE;
}
$i = 0;
foreach ($this->uc_recdef as $fname => $val) {
$type = $this->type($fname);
$fldsz = $this->size($fname);
$atts = $this->getAtts($fname);
if (!is_array($val)) continue; /// Skipp table prop Table_name, file_name, etc
if (isset($atts['UPPER'])) $vrec[$i] = strtoupper($vrec[$i]); /// To UpperCase
if (isset($atts['LOWER'])) $vrec[$i] = strtolower($vrec[$i]); /// To LowerCase
if (isset($atts['DEFAULT']) && !$this->hasVarValue($vrec[$i]))
$vrec[$i] = $atts['DEFAULT']; /// Set the default value
if ($this->NN($fname) && !$this->hasVarValue($vrec[$i])) {
$this->errm('E',270,$fname); return FALSE;
}
if ($this->PK($fname)) { /// Primary key tests
if (!$this->hasVarValue($vrec[$i])) { $this->errm('E',242,$fname); return FALSE; }
if ( $operation=='INSERT' ) {
if (isset($this->pkkeys[$vrec[$i]])) {
$j = $this->pkkeys[$vrec[$i]];
if ($this->recmap[$j]<>'D') { $this->errm('E',244,$fname,$vrec[$i]); return FALSE; }
}
$this->pkkeys[$vrec[$i]] = $currec; /// Pk, add to pk-vals
} else { /// $operation=='UPDATE') Is there is another key value ??
if ($vrec[$i]<>$this->recs[$currec][$i]) { $this->errm('E',246,$fname); return FALSE; }
if (isset($this->pkkeys[$vrec[$i]])) {
$j = $this->pkkeys[$vrec[$i]];
if (!$j==$currec || !$this->recmap[$j]=='D') {
$this->errm('E',248,$fname,$vrec[$i]); return FALSE; }
}
}
}
if ($this->UK($fname)) { /// Unique key tests
if (!$this->hasVarValue($vrec[$i])) { $this->errm('E',250,$fname); return FALSE; }
if ( $operation=='INSERT' ) {
if (isset($this->ukkeys[$fname][$vrec[$i]])) {
$j = $this->ukkeys[$fname][$vrec[$i]];
if ($this->recmap[$j]<>'D') { $this->errm('E',252,$fname,$vrec[$i]); return FALSE; }
}
$this->ukkeys[$fname][$vrec[$i]] = $currec; /// Uk, add to uk-vals
} else { /// $operation=='UPDATE') Is there is allready a key value?
if (isset($this->ukkeys[$fname][$vrec[$i]])) {
$j = $this->ukkeys[$fname][$vrec[$i]];
if (!$j==$currec || !$this->recmap[$j]=='D') {
$this->errm('E',254,$fname,$vrec[$i]); return FALSE; }
}
$this->ukkeys[$fname][$vrec[$i]] = $currec;
}
}
if ($this->hasVarValue($vrec[$i]) ) { /// Anything to test ?
if ($type=='NUMBER') {
if (!is_numeric($vrec[$i])) { $this->errm('E',260,$fname,$vrec[$i]); return FALSE; }
} elseif ($type=='INT') {
$s = trim($vrec[$i],'0123456789');
if (!empty($s) || floor($vrec[$i])<>$vrec[$i]) {
$this->errm('E',262,$fname,$vrec[$i]); return FALSE;
}
} elseif ($type=='DATE') {
if (!$this->isDate($vrec[$i])) {
$newdt = $this->str2date($vrec[$i]);
if (is_null($newdt)) { $this->errm('E',264,$fname,$vrec[$i]); return FALSE; }
$vrec[$i] = $newdt;
}
} elseif ($type=='DATETIME') {
if (!$this->isDateTime($vrec[$i])) {
$newdt = $this->str2datetime($vrec[$i]);
if (is_null($newdt)) { $this->errm('E',265,$fname,$vrec[$i]); return FALSE; }
$vrec[$i] = $newdt;
}
} elseif ($type=='EMAIL') { /// simple pattern check
$m = $vrec[$i]; // min hide@address.com
if (strlen($m)<7 || strlen(strstr($m,'@'))<6 || strlen(strstr($m,'.'))<3) {
$this->errm('E',266,$fname,$m); return FALSE; }
} elseif ($type=='BINARY') { /// Base 64 encoded
$vrec[$i] = chunk_split(base64_encode($vrec[$i]));
}
/// Other tests
if (!is_null($fldsz) && $type<>'DATE' && $type<>'DATETIME')
if (strlen($vrec[$i])>$fldsz) { $this->errm('E',272,$fname,$fldsz); return FALSE; }
if (isset($atts['CHKMIN']) && $vrec[$i]<$atts['CHKMIN']) {
$this->errm('E',274,$fname,$atts['CHKMIN']); return FALSE;
}
if (isset($atts['CHKMAX']) && $vrec[$i]>$atts['CHKMAX']) {
$this->errm('E',276,$fname,$atts['CHKMAX']); return FALSE;
}
if (isset($atts['CHKMOD10']) && !chkMod10($atts['CHKMOD10'])) {
$this->errm('E',280,$fname); return FALSE;
}
if (isset($atts['CHKFMT']) && sprintf($vrec[$i],$atts['CHKFMT'])<>$vrec[$i]) {
$this->errm('E',282,$fname); return FALSE; /// Match num/string to printf format mask
}
if (isset($atts['CHKMINLEN']) && strlen($vrec[$i])<$atts['CHKMINLEN']) {
$this->errm('E',284,$fname,$atts['CHKMINLEN']); return FALSE;
}
if (isset($atts['CHKLIST'])) {
$found = FALSE;
if (is_array($atts['CHKLIST'])) {
while ( list ($subattribute, $subattval) = each ($atts['CHKLIST'])) {
if ($vrec[$i]==$subattval) { $found = TRUE; break; }
}
if (!$found) {
$this->errm('E',286,$fname,$vrec[$i], implode(', ', $atts['CHKLIST']));
return FALSE;
}
}
}
/* 'CHKDATEFMT': /// $this->errm('E',288,$fname,$atts['CHKDATEFMT']); break; */
}
$i++;
} // foreach
if ($this->trace) $this->errm('T',831);
return TRUE;
} /// validateRec
/// ////////////////////////////////////
/// U T I L I T Y F U N C T I O N S ///
/// ////////////////////////////////////
function formatRec ($recno, $namedIdx=TRUE) {
/// Format and Convert record to output format with or without fldnames
if (!$this->hasRecDef()) return $this->recs[$recno];
if ($this->trace) $this->errm('T',832,$recno,NULL,NULL,NULL,2);
$rec = array ();
reset ($this->recflds);
while (list ($fn, $pos ) = each ($this->recflds)) {
if (isset($this->recs[$recno][$pos])) $value = $this->recs[$recno][$pos];
else $value = NULL;
if ($namedIdx) $ix = $this->tblname.'.'.$fn;
else $ix = $pos;
$type = $this->type($fn);
if ($type=='DATE') $rec[$ix] = $this->date2str($value);
elseif ($type=='DATETIME') $rec[$ix] = $this->datetime2str($value);
elseif ($type=='BINARY') $rec[$ix] = base64_decode($value);
else $rec[$ix] = $value;
}
// if ($this->trace) $this->errm('T',834);
return $rec;
}
function hasRecDef () {
if (is_null($this->uc_recdef)) return FALSE;
return TRUE;
}
function isAttSet ($fname, $att) { /// Case sensitive
if (!is_null($a = $this->getAtts($fname)))
if (isset($a[$att])) return TRUE;
return FALSE;
}
function getAtts ($fname) {
if (!$this->hasRecDef()) return NULL;
$fname = $this->cnv2uc($fname); // case ??
if (isset($this->uc_recdef[$fname])) return $this->uc_recdef[$fname];
return NULL;
}
function fldno2fldname ($fno) {
if (!isset($this->fldnames[$fno])) { $this->errm('E',201, $fno); return NULL; }
$fn = $this->fldnames[$fno];
if (!is_null($fn)) return strtoupper($fn);
return NULL;
}
function fldname2fldno ($fname) {
if (!$this->hasRecDef()) return NULL;
if (is_string($fname)) $fname = strtoupper($fname);
else return NULL;
if (isset($this->recflds[$fname])) return $this->recflds[$fname];
$this->errm('E',202, $fname);
return NULL;
}
function getPk () {
if (!$this->hasRecDef()) return NULL;
$i = 0;
foreach ($this->uc_recdef as $fname => $val) {
if (isset($val['PK'])) return array ($fname, $i);
$i++;
}
return NULL;
}
function NN ($fname) {
return ($this->isAttSet($fname,'CHKNN') || $this->isAttSet($fname,'PK'));
}
function PK ($fname) {
return $this->isAttSet($fname,'PK');
}
function UK ($fname) {
return $this->isAttSet($fname,'UK');
}
function FK ($fname) { /// returns array with table name, field name or FALSE
if (!is_null($a = $this->getAtts($fname)))
if (isset($a['FK'])) return array ($a['FK'][0],$a['FK'][1]);
return NULL;
}
function size ($fname) {
if (!is_null($a = $this->getAtts($fname)))
if (isset($a['SIZE'])) return $a['SIZE'];
return NULL;
}
function type ($fname) {
if (!is_null($a = $this->getAtts($fname)))
if (isset($a['TYPE'])) return $a['TYPE'];
return 'STRING';
}
function usage ($fname) {
if (!is_null($a = $this->getAtts($fname)))
if (isset($a['USAGE'])) return $a['USAGE'];
return NULL;
}
function reGenKeys () { /// Add Pk+UK
if ($this->trace) $this->errm('T',836);
$this->pkkeys = NULL;
$this->ukkeys = NULL;
if (!$this->hasRecDef()) return NULL;
$j = 0;
foreach ($this->uc_recdef as $fld_name => $value) {
if (isset($value['PK']))
for ($i=0; $i<$this->rowcnt; $i++)
if ($this->recmap[$i]<>'D') $this->pkkeys[$this->recs[$i][$j]] = $i;
if (isset($value['UK']))
for ($i=0; $i<$this->rowcnt; $i++)
if ($this->recmap[$i]<>'D') $this->ukkeys[$fld_name][$this->recs[$i][$j]] = $i;
$j++;
}
if ($this->trace) $this->errm('T',837);
}
function get_ctldata ($str) {
if ($this->trace) $this->errm('T',863);
$rec = $this->line2rec($str);
foreach ( $rec as $pos => $val) {
if (strstr($val,'minseq=')) $this->minseq = substr($val,7);
if (strstr($val,'maxseq=')) $this->maxseq = substr($val,7);
if (strstr($val,'maxkey=')) $this->maxkey = substr($val,7);
if (strpos($val,'crypt=')) $this->pwkey = substr($val,6);
}
if ($this->trace && isset($this->maxkey)) $this->errm('T',864,$this->maxkey);
if ($this->trace && isset($this->maxseq)) $this->errm('T',865,$this->maxseq);
if ($this->trace && isset($this->minseq)) $this->errm('T',866,$this->minseq);
if ($this->trace) $this->errm('T',867);
if (isset($this->pwkey)) {
$s = $this->scrambleData($this->pw);
if ($s<>$this->pwkey) return FALSE;
}
$this->ctlcnt++;
return TRUE;
}
function set_ctldata () {
if ($this->trace) $this->errm('T',870);
$str = NULL;
$d = NULL;
if (isset($this->minseq)) $str .= 'minseq='.$this->minseq;
if (!is_null($str)) $d = ';';
if (isset($this->maxseq)) $str .= $d.'maxseq='.$this->maxseq;
if (!is_null($str)) $d = ';';
if (isset($this->maxkey)) $str .= $d.'maxkey='.$this->maxkey;
if ($this->trace && isset($this->maxkey)) $this->errm('T',864,$this->maxkey);
if ($this->trace && isset($this->maxseq)) $this->errm('T',865,$this->maxseq);
if ($this->trace && isset($this->minseq)) $this->errm('T',866,$this->minseq);
if (!is_null($str)) $delim = ';';
if ($this->is_encrypted) $str .= $d.'crypt='. $this->scrambleData($this->pw);
if (!is_null($str)) $d = ';';
$str .= $d.'LastWrite='.date('Y-m-d H:i:s');
$str = '<'.$this->tag.' CONTROL DATA>'.$str.'</'.$this->tag.'>';
if ($this->trace) $this->errm('T',872);
return $str;
}
function stripNamedKeys($rec) {
/// Makes a record with "named keys" to numeric keys only
/// First check if numeric keys only, if so .. return it
if ($this->trace) $this->errm('T',868);
if (!($this->hasRecDef() && is_array($rec))) return $rec;
$numericKeys = true;
foreach ($rec as $key => $value) {
if (is_numeric($key)) continue;
$numericKeys = false;
break;
}
if ($numericKeys) return $rec;
$newrec = array ();
/// Rearanges a named record into fld order as well
if ($this->trace) $this->errm('T',869);
reset($this->recflds);
$i = 0;
while ( list($fname, $pos) = each($this->recflds)) {
$nk = $this->tblname.'.'.$fname;
if (isset($rec[$nk])) {
$newrec[$i++] = $rec[$nk];
} else {
$newrec[$i++] = NULL;
if ($this->trace) $this->errm('T',857, $nk, $i);
}
unset($rec[$nk]);
}
if (isset($rec[$nk])) unset($rec[$nk]);
if (count($rec)>0) { /// Record contains illegal field names
$this->errm('E',230, implode (', ',array_keys($rec)));
return FALSE;
}
return $newrec;
}
/// /////////////////////////////////////////////////
/// B A S E F U N C T I O N S ///
/// Does not require a record definition to work ///
/// /////////////////////////////////////////////////
function getRec ($keyvalue, $searchfld=NULL) {
/// Returns first record found with searched key in keyfield or NULL
if ($this->trace) $this->errm('T',838,$keyvalue);
$ptr = NULL;
if (is_null($searchfld)) $fno = $this->keyfld;
else $fno = $searchfld;
if ($this->hasRecDef()) /// Is there a pk or uk to search on?
$ptr = $this->getRnByKey ($keyvalue, $fno);
if (!is_null($ptr)) return $this->formatRec($ptr,FALSE);
else {
$recset = $this->scanRecs($keyvalue, $fno);
if (isset($recset)) return $this->formatRec($recset[0],FALSE);
}
return NULL;
} /// getRec
function scanRecs ($value, $searchfld=NULL, $recset=NULL, $op=NULL) {
/// Returns set of pointers to records found with searched key in keyfield, or NULL
/// Searches THIS SET ONLY
if ($this->trace) {
if (empty($value)) $v = ' (NULL) ';
else $v = $value;
$this->errm('T',848,$v,$searchfld,$op);
}
if (!$this->is_read) $this->read();
if (is_null($searchfld)) $fno = $this->keyfld;
else $fno = $searchfld;
$op = $this->chkOp($op);
if (is_null($op)) return NULL;
if (is_string($value)) $value = trim($value);
if ($this->trace && empty($value)) $this->errm('T',849);
if ($this->hasRecDef()) {
$type = $this->type($this->fldno2fldname($fno));
if ($type=='DATE' && !$value=$this->str2date($value)) return NULL; /// Inv date
if ($type=='DATETIME' && !$value=$this->str2datetime($value)) return NULL; /// Inv date
if ($type=='BINARY') { $this->errm('E',308,$fno); return NULL; }
}
if (is_null($recset)) $recset = $this->selectAll();
$rec_count = count($recset);
if (!is_array($recset) || $rec_count==0) return NULL;
$selected = NULL;
for ($i=0; $i<$rec_count; $i++ ) {
$ptr = $recset[$i];
if (!isset($this->recs[$ptr][$fno])) { $this->errm('W',310,$ptr); return NULL; }
$fldtype = gettype($this->recs[$ptr][$fno]);
settype($value,$fldtype);
if ($op=='EQUAL' && $this->recs[$ptr][$fno]==$value)
{ $selected[] = $ptr; continue; }
if ($op=='LIKE' && substr($this->recs[$ptr][$fno],0,strlen($value))==$value)
{ $selected[] = $ptr; continue; }
if ($op=='CMP' && strcasecmp($this->recs[$ptr][$fno],$value)==0) // Str compare ignores case.
{ $selected[] = $ptr; continue; }
if ($op=='LCMP') { // Like ignore case.
$s = substr($this->recs[$ptr][$fno],0,strlen($value));
if (strcasecmp($s,$value)==0) $selected[] = $ptr;
continue;
}
if ($op=='LT' && $this->recs[$ptr][$fno]<$value)
{ $selected[] = $ptr; continue; }
if ($op=='GT' && $this->recs[$ptr][$fno]>$value)
{ $selected[] = $ptr; continue; }
if ($op=='LE' && $this->recs[$ptr][$fno]<=$value)
{ $selected[] = $ptr; continue; }
if ($op=='GE' && $this->recs[$ptr][$fno]>=$value)
{ $selected[] = $ptr; continue; }
if ($op=='NE' && $this->recs[$ptr][$fno]<>$value)
{ $selected[] = $ptr; continue; }
}
if ($this->trace) $this->errm('T',850,count($selected),count($recset));
return $selected;
} /// scanRecs
function delete ($keyvalue, $searchfld=NULL) {
/// Deletes all records with matching keyvalue in keyfield, returns FALSE or TRUE
if ($this->trace) $this->errm('T',840,$keyvalue,$searchfld);
$r = $this->scanRecs($keyvalue, $searchfld);
if (is_null($r)) { $this->errm('W',300); return FALSE; }
$j = count($r);
if ($this->trace) $this->errm('T',842,$j);
for ($i=0; $i<$j; $i++) if (!$this->delByRn($r[$i])) return FALSE;
return TRUE;
}
function update ($rec, $keyvalue, $searchfld=NULL) {
/// Updates all records with matching key, returns FALSE or TRUE
if ($this->trace) $this->errm('T',844,$keyvalue,$searchfld);
if ($this->hasRecDef()) $rec = $this->stripNamedKeys($rec); /// Convert rec
if (is_null($rec)) { $this->errm('W',301); return FALSE; }
$r = $this->scanRecs($keyvalue, $searchfld);
if (is_null($r)) { $this->errm('W',302); return FALSE; }
$j = count($r);
if ($this->trace) $this->errm('T',846,$j);
for ($i=0; $i<$j; $i++) if (!$this->updByRn($r[$i], $rec)) return FALSE;
return TRUE;
}
function insert ($newrec) {
/// New record "newrec" is an array of all fields.
$this->lastInsRn = NULL;
if ($this->trace) $this->errm('T',852);
if (!is_array($newrec)) { $this->errm('E',320); return FALSE; }
if (!$this->is_read) $this->read();
if (!$this->open('a')) { $this->errm('E',321); return FALSE; } /// Open and lock
if ($this->hasRecDef()) {
$newrec = $this->stripNamedKeys($newrec); /// Check and convert rec
if (is_null($newrec)) { $this->errm('W',301); return FALSE; }
if ($this->seqkey) {
if (isset($this->maxkey)) ++$this->maxkey; /// Increase Sequence key
if (isset($this->maxkey) && isset($this->maxseq)) /// Check Min and max sequence
if ($this->maxkey>=$this->maxseq) {
if (isset($this->minseq)) $this->maxkey = $this->minseq;
else $this->maxkey = 0;
}
$newrec[$this->keyfld] = $this->maxkey;
}
if(!$this->validateRec(&$newrec, 'INSERT', $this->rowcnt)) return FALSE;
} else { /// No rec def,,,,
$kfld = gettype($newrec[$this->keyfld]); /// If keyfld = num assume key
if (!($kfld=='integer'||$kfld=='double'||$kfld=='float')) /// Non Numeric, add NUM key
array_unshift($newrec,++$this->maxkey); /// push key onto record as fld 0
}
$this->recs[$this->rowcnt] = $newrec;
$this->lastInsRn = $this->rowcnt;
$this->recmap[$this->rowcnt] = 'I';
$this->rowcnt++;
if (!isset($this->operation)) $this->operation='INSERT';
return TRUE;
} /// insert
function updByRn ($rowno, $updrec) {
/// Updates the record in the rec-array by row number ( array index )
if ($this->trace) $this->errm('T',858,$rowno);
if (!$this->is_read) $this->read();
if (!is_array($updrec)) { $this->errm('E',335); return FALSE; }
if ($this->recmap[$rowno]=='D') { $this->errm('E',340,$rowno); return FALSE; }
if (!$this->recs[$rowno]) { $this->errm('E',345,$rowno); return FALSE; }
if (is_null($updrec)) { $this->errm('W',301); return FALSE; }
if ($this->hasRecDef()) $updrec = $this->stripNamedKeys($updrec); /// Convert rec
if (!$this->open('a')) { $this->errm('W',322); return FALSE; } /// Open&lock
$updrec[$this->keyfld] = $this->recs[$rowno][$this->keyfld]; /// Conserve pk
if ($this->hasRecDef() && !$this->validateRec(&$updrec, 'UPDATE', $rowno)) return FALSE;
$this->recs[$rowno] = $updrec;
if ($this->trace) $this->errm('T',860,$rowno);
if ($this->recmap[$rowno]=='O') $this->recmap[$rowno] = 'U';
$this->operation = 'MIXED'; /// does not handle update of newly inserted rec
return TRUE;
} /// updByRn
function delByRn ($rowno) {
/// Marks the record for Delete in the rec-array by row number ( array index )
if ($this->trace) $this->errm('T',853,$rowno);
if (!$this->is_read) $this->read();
if (!$this->open('a')) { $this->errm('W',323); return FALSE; } /// Open and lock
if (!$this->hasVarValue(($this->recs[$rowno]))) { $this->errm('E',330,$rowno); return FALSE; }
if ($this->trace) $this->errm('T',854,$rowno);
$this->recmap[$rowno] = 'D';
$this->operation = 'MIXED'; /// does not handle delete of newly inserted rec
return TRUE;
} /// delByRn
function getRecByRn ($rowno, $namedIdx=TRUE) {
/// Returns record by Row number or NULL
if ($this->trace) $this->errm('T',856,$rowno);
if (!$this->is_read) $this->read();
if (!isset($this->recs[$rowno])) { $this->errm('E',338,$rowno); return NULL; }
if ($this->recmap[$rowno]=='D') { $this->errm('E',337,$rowno); return NULL; }
if ($namedIdx && $this->hasRecDef()) $rec = $this->formatRec ($rowno, $namedIdx);
else $rec = $this->recs[$rowno];
return $rec;
} /// getRecByRn
function commit () {
return $this->write();
}
function rollback () {
if ($this->fp) $this->close();
$this->reset_recs();
return $this->read();
}
///////////////////////
/// File functions ///
/////////////////////
function reRead () {
if ($this->trace) $this->errm('T',862);
if (!is_null($this->operation)) { $this->errm('E',350); return FALSE; }
$this->reset_recs();
return $this->read();
}
function reset_recs () {
$this->is_read = FALSE;
$this->recs = NULL;
$this->comments = NULL;
$this->recmap = NULL;
$this->selrecs = NULL;
$this->selptr = NULL;
$this->pkkeys = NULL;
$this->ukkeys = NULL;
$this->operation = NULL;
$this->filetime = NULL;
}
function mk_newrec ($str, $i ) {
/// Private, used in read only
if ($this->is_encrypted) $str = $this->descrambleData($str);
$r = $this->line2rec($str);
if ($r[$this->keyfld]>$this->maxkey) $this->maxkey = $r[$this->keyfld];
$this->recs[$i] = $r;
$this->recmap[$i] ='O';
if (is_null($this->trace_level) && $this->trace) $this->errm('T',881,$i);
return;
}
function read () {
if ($this->trace) $this->errm('T',873);
if ($this->is_read) return TRUE;
if (!$this->hasRecDef()) $this->parseRecDef();
$this->reset_recs();
if (file_exists($this->file)) {
if (!$this->open('r')) return FALSE;
} else {
if ($this->trace) $this->errm('T',874);
$this->is_read = TRUE;
return TRUE;
}
$stag = '<'.$this->tag; /// Open tag test close later
$etag = '</'.$this->tag.'>';
$taglen = strlen($stag);
$str = NULL;
$i = 0;
if ($this->trace) {
$this->errm('T',875);
if ($this->is_encrypted) $this->errm('T',876);
}
while (!feof($this->fp)) {
$line = fgets($this->fp, $this->maxline);
if (substr($line,0,1)=='#') { /// Commentlines must start with #
$this->comments[] = strstr($line,'#');
continue;
}
$op = NULL;
$startpos = strpos($line,$stag);
$endpos = strpos($line,$etag);
if (!($startpos === false )) { /// Beginning of StartTag
//if (is_null($this->trace_level) && $this->trace) $this->errm('T',877,$i);
$clpos = strpos($line,'>'); /// End of Starttag, close pos
if ($clpos==$taglen+13) { /// <R I=1234567890>fld1... Type 2
$startpos += $taglen + 14;
$op = substr($line,$taglen+1,1);
if ($op=='I'||$op=='U'||$op=='D'||$op=='O') {
$this->tsrecs[$i] = substr($line,$taglen+1,12); /// Timestamp for each rec
if (!$this->rectype) $this->rectype = 2;
} else if ($op!='C') $this->errm('E',401,$op);
} elseif ($clpos==$taglen) { /// <R>fld1... Type 1
$startpos += $taglen+1;
if (!$this->rectype) $this->rectype = 1;
} else $this->errm('E',213,substr($line,$startpos,$clpos));
$line = substr($line,$startpos);
}
if (!($endpos === false)) {
//if (is_null($this->trace_level) && $this->trace) $this->errm('T',878,$i);
$str .= substr($line,0, $endpos-$startpos);
if ($op=='C') {
if (!$this->get_ctldata($str)) { $this->errm('E',405); $this->reset_recs(); break; }
$str = NULL;
continue;
}
$this->mk_newrec($str, $i);
$str = NULL;
$i++;
} else if (trim($line)!='') $str .= $line;
} //while
if ($this->mode=='r') $this->close(); /// this is only a read op
$this->rowcnt = count($this->recs);
if ($this->hasRecDef()) $this->reGenKeys(); /// Create uk & pk array
$this->is_read = TRUE;
if ($this->ctlcnt > 100) $operation = 'MIXED'; /// Rewrite file if many ctl-recs
if ($this->trace) $this->errm('T',882,count($this->rowcnt),$this->maxkey);
return TRUE;
} /// read
/// Functions below is not made private even if they could be
function write () {
/// Checks writemode and does a write of all records or appendwrite to file if only inserts
if ($this->trace) $this->errm('T',883);
if (!isset($this->operation)) { /// No Changes made to records
if ($this->trace) $this->errm('T',884);
return TRUE;
}
if (!$this->is_read) { $this->errm('E',410); return FALSE; } /// File Not read, do not write!
if ($this->is_encrypted) {
if ($this->trace) $this->errm('T',885);
if (!isset($this->pw)) { if ($this->trace) $this->errm('T',886); return FALSE; }
if (isset($this->pwkey) && $this->pwkey<>$this->scrambleData($this->pw)) {
$this->errm('E',420);
return FALSE;
}
}
if (!$this->rectype) $this->rectype = 2;
$stag = '<'.$this->tag.'>';
$etag = '</'.$this->tag.'>';
$newrecs = array ();
$ts = time();
if ($this->changelog) $logfile = $this->file.'_log'; /// Set LogFile Name
if (!is_null($this->logswitch)) {
$logfmt = array ('y'=>'y', 'm'=>'ym', 'd'=>'ymd', 'h'=>'ymdH', 'w'=>'ymW');
$lt = strtolower(substr(trim($this->logswitch),0)); /// ... if set, can be ymdhw
if (isset($logfmt[$lt])) $logfile = $this->file.'_log_'.date($logfmt[$lt],$ts);
else $this->errm('W',502,$lt);
}
if (!$this->open('a')) return FALSE; /// Open and lock
$comment_cnt = count($this->comments);
if ($this->trace) $this->errm('T',888,$comment_cnt);
if ($this->changelog && !($lfp = fopen($logfile, 'a'))) {
$this->errm('E',501);
$this->changelog = FALSE;
}
if ($this->operation=='INSERT') { /// Only Append new record(s)
$only_new_recs = TRUE; /// Recmap maintained at I/U/D ops
for ($i=0; $i<count($this->recmap); $i++) { /// Check if only Insert records
if ($this->recmap[$i++]=='I') { next($this->recmap); continue; }
$only_new_recs = FALSE;
break; /// Old record found
}
if ($this->trace) $this->errm('T',892);
if ($only_new_recs && $comment_cnt==0) /// New file, Insert header info
$this->writeLine($this->fp, "# Created: ".date('Y-m-d H:i:s')."\n");
} else { /// Rewrite all record(s)
if ($this->trace) $this->errm('T',893);
fseek($this->fp, 0); /// Reset filepointer
ftruncate($this->fp, 0); /// Truncate file
for ($i=0; $i<$comment_cnt; $i++) /// Write Comment lines
$this->writeLine($this->fp, $this->comments[$i]);
}
if ($this->seqkey)
$this->writeLine($this->fp, "\n".$this->set_ctldata()); /// Write Control record
$j = 0;
$new_rec_cnt = count($this->recmap);
if ($this->trace) $this->errm('T',894,$new_rec_cnt);
$res = FALSE;
for ($i=0; $i<$new_rec_cnt; $i++) { /// Write record(s)
$op = $this->recmap[$i];
if ($op<>'D') $newrecs[] = $this->recs[$i]; /// New array, Skip deleted recs
if ($this->operation=='INSERT' && $op<>'I') continue; /// Only Append new recs
$line = $this->rec2line($this->recs[$i]);
if ($this->is_encrypted) $line = $this->scrambleData($line);
if ($line) {
if ($this->changelog && $op<>'O') {
if (!$this->writeLine($lfp, "\n".'<'.$this->tag.' '.$op.'='.$ts.'>'.$line.$etag))
$this->errm('E',517);
}
if ($op<>'D') { /// Skip deleted records
$j++;
if ($this->rectype==2) {
if ($op=='O' && isset($this->tsrecs[$i]))
$oline = "\n<".$this->tag.' '.$this->tsrecs[$i].'>'.$line.$etag;
else $oline = "\n<".$this->tag.' '.$op.'='.$ts.'>'.$line.$etag;
} else {
$oline = "\n".$stag.$line.$etag;
}
if (!$this->writeLine($this->fp,$oline)) break;
}
}
$res = TRUE;
} // write loop end
if ($this->trace) $this->errm('T',896,$j);
if ($this->changelog) {
fflush($lfp);
if (!fclose($lfp)) $this->errm('E',531,$this->changelog);
}
$this->close();
if (!$res) { $this->errm('E',516); return FALSE; }
$this->operation = NULL;
$this->reRead();
return TRUE;
} /// write
function writeLine ($fp, $line) {
if (!fputs($fp,$line)) { $this->errm('E',515); return FALSE; }
return TRUE;
}
function lock ($lockmode) {
if ($this->trace) $this->errm('T',900,$lockmode);
$mlw = $this->maxlockwait * 1000;
$lockmode = strtolower($lockmode);
for ($i=100; $i<$mlw; $i +=200) {
/// LOCK_NB is used with "flock" to return immediately instead of waiting for the lock
switch ($lockmode) {
case 'r': $lck = LOCK_SH | LOCK_NB; break;
case 'a':
case 'w': $lck = LOCK_EX | LOCK_NB; break;
case 'u': $lck = LOCK_UN; break;
default: $this->errm('E',510,$lockmode); return FALSE; break;
}
if (flock($this->fp,$lck)) {
if ($lockmode =='u') $this->is_locked = FALSE;
else $this->is_locked = TRUE;
return TRUE;
}
usleep($i); /// linux/unix ok, windows ? sleep(1);
if($this->trace) $this->errm('T',902,$i);
}
$this->errm('E',511);
return FALSE;
} /// lock
function open ($mode) {
if ($this->trace) $this->errm('T',904,$mode);
if (is_null($this->file)) { $this->errm('E',520); return FALSE; }
$mode = strtolower($mode);
$m1 = substr($mode,0,1);
$m2 = substr($mode,1,1);
if ($m1=='w') $m1 = 'a'; /// Write -> Allways open append
if (($m1=='r'||$m1=='a') && ($m2==NULL||$m2=='+'||$m2=='b')) $mode = $m1.$m2;
else { $this->errm('E',524,$mode); return FALSE; }
if ($this->is_open && $this->mode==$m1) return TRUE; /// Allready Open in Req mode
$fd = dirname($this->file);
if (!file_exists($fd)) { $this->errm('E',521,$fd); return FALSE; }
if (file_exists($this->file) && is_dir($this->file)) { $this->errm('E',522,$fd); return FALSE; }
if (file_exists($this->file)) {
if (!is_readable($this->file)) { $this->errm('E',525); return FALSE; }
if ($m1=='a' && !is_writable($this->file)) { $this->errm('E',527); return FALSE;}
$cf = $this->getFileTime();
if (is_null($this->filetime)) $this->filetime = $cf; /// New read, set filetime
if ($this->filetime<>$cf) { /// Check if modified since last read
if ($this->trace) $this->errm('T',889,$this->filetime, $this->getFileTime());
$this->reset_recs();
}
} else {
if ($m1=='r') { $this->errm('E',526); return FALSE; } /// File does not exist for read
}
if ($this->fp = fopen($this->file, $mode)) {
if ($this->lock($m1)) {
$this->is_open = TRUE;
$this->mode = $m1;
if (isset($cf)) $this->filetime = $cf; /// Save last file mod time
else $this->filetime = $this->getFileTime();
return TRUE;
} else {
$this->close();
$this->errm('E',528,$mode);
}
}
$this->errm('E',523,$mode);
return FALSE;
} /// open
function close () {
if ($this->trace) $this->errm('T',906);
if ($this->is_locked) $this->lock('u');
fflush($this->fp);
if ($this->is_open && !fclose($this->fp)) $this->errm('E',530);
$this->is_open = FALSE; /// Do this in any case
$this->mode = NULL;
$this->fp = FALSE;
return TRUE;
}
function getFileTime() {
clearstatcache();
if ($ts = filemtime($this->file)) return $ts; /// File modified since last read
else return NULL;
}
///////////////////////
/// Date Utilities ///
/////////////////////
function date2str($dt) {
if (!$this->hasVarValue($dt)) return NULL;
// Be forgiving and strip time if this is a datetime
if ($this->isDate(substr($dt,0,8))) { /// Internal Format YYYYMMDD -> mktime (HHiissMMDDYYYY)
$ts = mktime(0, 0, 0, substr($dt,4,2), substr($dt,6,2), substr($dt,0,4));
} else {
$this->errm('E',205, $dt);
return NULL;
}
return date($this->datefmt,$ts);
}
function datetime2str($dt) {
if (!$this->hasVarValue($dt)) return NULL;;
if ($this->isDateTime($dt)) { /// Internal Format YYYYMMDDHHiiss -> mktime (HHiissMMDDYYYY)
$ts = mktime(substr($dt,8,2), substr($dt,10,2), substr($dt,12,2),
substr($dt,4,2), substr($dt,6,2), substr($dt,0,4));
} else {
$this->errm('E',203, $dt);
return NULL;
}
return date($this->datetimefmt,$ts);
}
function isDateTime ($str) { /// Check datestr, must be in format yyyymmddHHMiSS
if (strlen($str)<>14) return FALSE;
$hour = substr($str,8,2);
$min = substr($str,10,2);
$sec = substr($str,12,2);
if ($hour<24 && $hour>-1 && $min<60 && $min>-1 && $sec<60 && $sec>-1 &&
$this->isDate(substr($str,0,8))) return TRUE;
return FALSE;
}
function isDate ($str) { /// Check datestr, must be in format yyyymmdd
$year = substr($str,0,4);
if ($year < 1902 || $year > 2037) return FALSE;
if (strlen($str)==8 && checkdate(substr($str,4,2), substr($str,6,2), $year)) return TRUE;
return FALSE;
}
function str2datetime($str) {
$ts = $this->str2ts($str);
if ($ts==-1 || is_null($ts)) { $this->errm('E',204,$str); return NULL; }
return date('YmdHis',$ts);
}
function str2date($str) {
$ts = $this->str2ts($str);
if ($ts==-1 || is_null($ts)) { $this->errm('E',206,$str); return NULL; }
return date('Ymd',$ts);
}
function str2ts($str) {
if (!$this->hasVarValue($str)) return NULL; // null => invalid
$str = strtolower($str);
if ($str=='now') return strtotime('now');
if ($this->isDate($str)) // internal date
return mktime(0,0,0, substr($str,4,2), substr($str,6,2), substr($str,0,4));
if ($this->isDateTime($str)) // internal datetime
return mktime(substr($str,8,2), substr($str,10,2), substr($str,12,2),
substr($str,4,2), substr($str,6,2), substr($str,0,4));
// ? php5 use $ts = strptime ( $str, $this->datetimefmt )
// assume: dateparts has a delimiter, and year is a full 4 digit year,
// date and time are separated by space, timeparts are separated by colon
$date = trim($str);
$time = NULL;
if (strpos($str,':')) { // Asuming : as time delimiter
$t = explode(' ',$str);
for ($i=0; $i<count($t); $i++) {
if (empty($t[$i])) continue;
if (strpos($t[$i],':')) $time = $t[$i];
else $date = $t[$i];
}
}
$s = strtr($date,'+/.,;_ ','-------');
list($year,$month,$day) = split('-', $s, 3); // separate date parts
if (strlen($day)>2 || strlen($year)<3) {
list($day,$month,$year) = split('-',$s,3); // guess reverse date parts
}
if (strlen($year)<3) {
if ($year<40) $year = '20'.$year; // max year is 2037
else $year = '19'.$year;
}
$s = substr($month,0,3);
if (in_array($s,$this->month3name)) {
$nummonth = (int) array_search($s,$this->month3name);
$monthname = $month;
} else {
$nummonth = (int) $month;
if ($nummonth>0 && $nummonth<13) $monthname = $this->month3name[$nummonth];
}
if (!empty($time)) {
$t = explode(':',$time);
$tp = array (0,0,0);
for ($i=0; $i<count($t); $i++) { // handle time with two or three fields
if (empty($t[$i])) continue;
$tp[$i] = $t[$i];
}
list ($hour, $min, $sec) = $tp;
$s = sprintf('%04d%02d%02d%02d%02d%02d',$year,$nummonth,$day,$hour,$min,$sec);
if (!$this->isDateTime($s)) return NULL;
$s = sprintf('%s-%s-%s %s', $day, $monthname, $year, $time);
// return mktime($hour, $min, $sec, $nummonth, $day, $year); // does not handle am/pm
if (($ts = strtotime($s)) !==FALSE) return $ts;
} else {
$s = sprintf('%04d%02d%02d',$year,$nummonth,$day);
if (!$this->isDate($s)) return NULL;
return mktime(0, 0, 0, $nummonth, $day, $year);
}
return NULL;
}
//////////////////////////
/// General Utilities ///
//////////////////////////
function chkOp($op=NULL) { /// Check search op and convert to upper case
if (!isset($op)) return 'EQUAL';
if (is_string($op)) $op = strtoupper($op);
else return NULL;
$op = trim(strtr($op,' ',''));
if (strpos('EQUAL LIKE LT GT LE GE NE CMP LCMP',$op)===FALSE) {
$this->errm('E',210,$op);
return NULL;
}
return $op;
}
function hasVarValue($value) {
if (is_null($value) || ( $value<>'0' && empty($value))) return FALSE;
return TRUE;
}
function rec2line ($newrec) {
$line = NULL;
$fld_count = count($newrec);
if ($fld_count==0) return NULL;
for ( $j=0; $j<$fld_count; $j++ ) { /// Format the fields in the in-record
if (isset($newrec[$j])) $str = trim($newrec[$j]);
else $str = ''; /// Stored with delimiter replaced by |
$str = strtr($str,$this->delimiter,'|'); /// Convert back all replaced chars
$line .= $str;
if ($j<$fld_count-1) $line .= $this->delimiter;
}
return $line;
}
function line2rec ($line) {
if (is_null($line)) return NULL;
if (is_null($this->delimiter)) $arr[] = $line;
else {
$arr = explode($this->delimiter,$line);
for ( $j=0; $j<count($arr); $j++ ) {
$arr[$j] = trim($arr[$j]," "); /// string is stored with delimiter
$arr[$j] = strtr($arr[$j],'|',$this->delimiter); /// Convert all replaced chars
}
}
return $arr;
}
function cnv2uc($arg) { /// string or array to upper case
if (is_string($arg)) return strtoupper($arg);
if (is_array($arg)) {
$newarr = array ();
while ( list($key,$val) = each($arg)) {
if (is_string($key)) $key = strtoupper($key);
if (is_string($val)) { $newarr[$key] = strtoupper($val); continue; }
if (is_array($val)) { $newarr[$key] = $this->cnv2uc($val); continue; }
$newarr[$key] = $val;
}
return $newarr;
}
return $arg;
}
function getuTime() {
list($usec, $sec) = explode(" ",microtime());
return ((float)$usec + (float)$sec);
}
function chkMod10($n) { /// Calculate mod10 check digit
$sum = 0;
$len = strlen($n);
if (strlen($len-1)<2) return FALSE; /// Value must be at least 1 plus 1 check digit
for($i=0; $i<$len-1; $i++) { /// Check digit by digit, last digit is check digit
$digit=substr($n,$i,1);
if ($i%2==0) $digit = $digit * 2; /// multiply by 2 every other digit
if ($digit<10) $sum += $digit;
else $sum += $digit-9;
}
$i = ceil($sum/10);
$i = ($i * 10) - $sum;
if (substr($n,$len,1)==$i) return TRUE;
else return FALSE;
}
function errm ($etype, $ecode, $p1=NULL, $p2=NULL, $p3=NULL, $p4=NULL, $level=0) {
global $dbfferr; // Last error
global $emsg;
$errtype = $emsg[$etype];
$str = NULL;
if (!is_null($p1) && is_array($p1)) { $str .= 'Param p1 is array! '; $p1 = implode(' ',$p1); }
if (!is_null($p2) && is_array($p2)) { $str .= 'Param p2 is array! '; $p2 = implode(' ',$p2); }
if (!is_null($p3) && is_array($p3)) { $str .= 'Param p3 is array! '; $p3 = implode(' ',$p3); }
if (!is_null($p4) && is_array($p4)) { $str .= 'Param p4 is array! '; $p4 = implode(' ',$p4); }
if ($etype=='E' || ($this->trace && $level<$this->trace_level)) {
if (isset($emsg[$ecode])) $str .= $emsg[$ecode];
else $str .= '001, Error: Unknown error code: '.$ecode;
$fn = basename($this->file); // hide path if users may see this
$repstr = array ('%FIL%'=>$fn, '%TBL%'=>$this->tblname,
'%P01%'=>$p1, '%P02%'=>$p2, '%P03%'=>$p3, '%P04%'=>$p4);
foreach ($repstr as $frmstr=>$tostr) {
$pos = strpos($str,$frmstr); // all ident fields is %XXX%
if ($pos===false) continue;
$str = substr_replace($str,$tostr,$pos,5);
}
$str = "DBFF-$ecode $errtype: $str";
if ($etype=='E') $dbfferr = $this->errstk[] = $str; // last error
}
if ($this->trace && $level<$this->trace_level) {
if (!isset($this->startTime)) { $t = 0; $this->startTime = $this->getuTime(); }
else $t = round($this->getuTime() - $this->startTime,3);
$pos = strpos($str,'<br>');
if (!$pos===false) $str = substr_replace($str," (time=$t)<br>",$pos,4);
print $str; // write2file(tracefile,$str)
}
return;
}
} /// dbff class end
?>