User:Woozle/data.php

From HTYP, the free directory anyone can edit

Jump to: navigation, search
<?php
/* ===========================
 *** DATA UTILITY CLASSES ***
 
(2007-05-20 Wzl) These classes have been designed to be db-engine agnostic, but I wasn't able
	to test against	anything other than MySQL nor was I able to implement the usage of
	the dbx_ functions, as the system that I was using didn't have them installed.
(2007-08-30 Wzl) posting this version at http://htyp.org/User:Woozle/data.php
(2007-12-24 Wzl) Some changes seem to have been made as recently as 12/17, so posting updated version
*/
 
class clsDatabase {
  private $cntOpen;	// count of requests to keep db open
  private $strType;	// type of db (MySQL etc.)
  private $strUser;	// database user
  private $strPass;	// password
  private $strHost;	// host (database server domain-name or IP address)
  private $strName;	// database (schema) name
 
  private $Conn;	// connection object
// status
  private $strErr;	// latest error message
  public $sql;	// last SQL executed (or attempted)
 
  public function __construct($iConn) {
    CallEnter('clsDatabase('.get_class($iConn).')');
    $this->Init($iConn);
    CallExit('clsDatabase()');
  }
  public function Init($iConn) {
// $iConn format: type:user:pass@server/dbname
    CallEnter('clsDatabase.Init('.get_class($iConn).')');
    $this->cntOpen = 0;
    list($part1,$part2) = split('@',$iConn);
    list($this->strType,$this->strUser,$this->strPass) = split(':',$part1);
    list($this->strHost,$this->strName) = explode('/',$part2);
    $this->strType = strtolower($this->strType);	// make sure it is lowercased, for comparison
    CallExit('clsDatabase.Init()');
  }
  public function Open() {
    CallEnter('clsDatabase.Open()');
    if ($this->cntOpen == 0) {
// then actually open the db
      if ($this->strType == 'mysql') {
        $this->Conn = new mysqli($this->strHost,$this->strUser,$this->strPass,$this->strName);
      } else {
        $this->Conn = dbx_connect($this->strType,$this->strHost,$this->strName,$this->strUser,$this->strPass);
      }
    }
    $this->cntOpen++;
    CallExit('clsDatabase.Open() - '.$this->cntOpen.' lock'.Pluralize($this->cntOpen));
  }
  public function Shut() {
    CallEnter('clsDatabase.Shut()');
    $this->cntOpen--;
    if ($this->cntOpen == 0) {
      if ($this->strType == 'mysql') {
        $this->Conn->close();
      } else {
        dbx_close($this->Conn);
      }
    }
    CallExit('clsDatabase.Shut() - '.$this->cntOpen.' lock'.Pluralize($this->cntOpen));
  }
  public function _api_query($iSQL) {
// USAGE: should only be used by clsDataItem
    $this->sql = $iSQL;
    if ($this->strType == 'mysql') {
      $this->Conn->real_query($iSQL);
      return $this->Conn->store_result();
    } else {
      return dbx_query($this->Conn,$iSQL,DBX_RESULT_ASSOC);
    }
 
  }
  public function Exec($iSQL) {
// MYSQL only
    CallEnter('clsDataTable_noID.Exec('.$iSQL.')');
    $this->sql = $iSQL;
    $objQry = $this->Conn->prepare($iSQL);
    if (is_object($objQry)) {
      $ok = $objQry->execute();
      if (!$ok) {
        $this->txtErr = $this->Conn->error;
      }
    } else {
      $ok = false;
      echo '<br>SQL error: '.$iSQL.'<br>';
    }
    $objQry->close();
    CallExit('clsDataTable_noID.Exec()');
    return $ok;
  }
  public function NewID() {
    return $this->Conn->insert_id;
  }
  public function SafeParam($iString) {
    return $this->Conn->escape_string($iString);
  }
  public function ErrorText() {
    return $this->txtErr;
  }
}
 
class clsData {
  protected $objDB;	// clsDatabase
  public function __construct($iDB) {
    $this->objDB = $iDB;
    $this->objDB->Open();
    assert (isset($this->objDB));
  }
  public function DB() {
    CallStep('('.get_class($this).')clsDataTable.DB()');
    return $this->objDB;
  }
}
 
class clsDataQuery extends clsData {
  protected $sqlSelect;
 
  public function __construct($iDB,$iSQL) {
    CallEnter('clsDataQuery('.get_class($iDB).','.$iSQL.')');
    parent::__construct($iDB);
    $this->sqlSelect = $iSQL;
    CallExit('clsDataQuery()');
  }
  public function __destruct() {
    $this->objDB->Shut();
  }
  protected function _newItem() {
    return new clsDataItem_noID($this);
  }
  public function GetData() {
    $objItem = $this->_newItem();
    $objItem->Query($this->sqlSelect);
    return $objItem;
  }
}
 
class clsDataTable_noID extends clsData {
  protected $strName;
  public function __construct($iDB,$iName) {
    CallEnter('clsDataTable_noID('.get_class($iDB).','.$iName.')');
    parent::__construct($iDB);
    $this->strName = $iName;
    CallExit('clsDataTable_noID()');
  }
  public function __destruct() {
    $this->objDB->Shut();
  }
  protected function _newItem() {
    CallStep('('.get_class($this).')clsDataTable_noID._newItem()');
    return new clsDataItem_noID($this);
  }
  public function Name() {
    return $this->strName;
  }
  public function GetData($iFilt='',$iSort='') {
    CallEnter('clsDataTable.GetData(filt="'.$iFilt.'",sort="'.$iSort.'")');
    $sql = 'SELECT * FROM '.$this->strName;
    if ($iFilt != '') {
      $sql .= ' WHERE ('.$iFilt.')';
    }
    if ($iSort != '') {
      $sql .= ' ORDER BY '.$iSort;
    }
    CallStep('SQL = ['.$sql.']');
    $objItem = $this->_newItem();
    $objItem->Query($sql);
    CallExit('clsDataTable.GetData() -> '.get_class($objItem));
    return $objItem;
  }
}
//
// clsDataTable_noID with functions based on autonumbered ID field
//
class clsDataTable extends clsDataTable_noID {
  protected $strIDname;	// name of unique ID field
 
  public function __construct($iDB,$iName,$iIDname='ID') {
    CallEnter('clsDataTable('.get_class($iDB).','.$iName.')');
    parent::__construct($iDB,$iName);
    $this->strIDname = $iIDname;
    CallExit('clsDataTable()');
  }
  public function NameOfID() {
    return $this->strIDname;
  }
  protected function _newItem() {
    CallStep('('.get_class($this).')clsDataTable._newItem()');
    return new clsDataItem($this);
  }
  public function GetItem($iID) {
    CallEnter('('.get_class($this).')clsDataTable.GetItem('.$iID.')');
    $sql = 'SELECT * FROM '.$this->strName.' WHERE '.$this->strIDname.'="'.$iID.'"';
    CallStep('SQL = ['.$sql.']');
//    $objQry = dbx_query($this->objDB,$sql,DBX_RESULT_ASSOC);
    $objItem = $this->_newItem();
    $objItem->Query($sql);
//    $objItem->Eat($objQry);
    CallExit('clsDataTable.GetItem('.$iID.') -> '.get_class($objItem));
    return $objItem;
  }
}
 
class clsDataItem_noID {
  public $Res;		// result set
  public $Row;		// first (presumably the *only*) row data
  public $Table;	// clsDataTable object
  public function __construct($iTable=NULL) {
    CallEnter('clsDataItem_noID('.get_class($iTable).')');
    $this->Init($iTable);
    CallExit('clsDataItem_noID()');
  }
  public function Init($iTable=NULL) {
    CallEnter('('.get_class($this).')clsDataItem_noID.Init('.get_class($iTable).')');
    $this->Table = $iTable; 
// this works for mysql only:
// fetch the first row of the results as an associative array:
//    $this->Row = $iResults->fetch_assoc();
    CallExit('clsDataItem_noID.Init()');
  }
  public function Query($iSQL) {
    CallEnter('('.get_class($this).')clsDataItem_noID.Query('.$iSQL.')');
    assert($this->Table);
    assert($this->Table->DB());
    $this->Res = $this->Table->DB()->_api_query($iSQL);
/*
    if ($this->strType == 'mysql') {
      $this->Conn()->real_query($iSQL);
      $this->Res = $this->Conn()->store_result();
    } else {
      $this->Res = dbx_query($this->Conn(),$iSQL,DBX_RESULT_ASSOC);
    }
*/
// later, we might want a flag for *not* doing this automatically... maybe.
    $this->FirstRow();
    CallExit('clsDataTable_noID.Query()');
  }
  protected function LoadResults() {
// USAGE: Abstract
    CallStep('('.get_class($this).')clsDataItem_noID.LoadResults()');
  }
  public function GetValue($iName) {
// this works for mysql only
    CallEnter('clsDataTable_noID.GetValue('.$iName.')');
//    DumpArray($this->Row);
    $val = $this->Row[$iName];
    CallExit('clsDataTable_noID.GetValue('.$iName.') -> ['.$val.']');
    return $val;
  }
  public function RowCount() {
    $cntRows = $this->Res->num_rows;
    CallStep('('.get_class($this).')clsDataItem_noID.RowCount() -> '.$cntRows);
    return $cntRows;
  }
  public function HasData() {
    return (is_array($this->Row));
  }
  public function HasRows($iMin=1) {
    if ($this->HasData()) {
      return ($this->RowCount() >= $iMin);
    } else {
      return 0;
    }
  }
  private function FirstRow() {
    CallEnter('('.get_class($this).')clsDataItem_noID.FirstRow()');
    if (!is_null($this->Res)) {
      if ($this->RowCount()) {
        $this->NextRow();	// get the first row of data
      }
    }
    CallExit('('.get_class($this).')clsDataItem_noID.FirstRow()');
  }
  public function NextRow() {
// this works for mysql only:
// fetch the NEXT row of the results as an associative array:
    $this->Row = $this->Res->fetch_assoc();
    if (is_array($this->Row)) {
      $this->LoadResults();
    }
  }
}
//
// clsDataItem with functions based on autonumbered ID field
//
class clsDataItem extends clsDataItem_noID {
  public $ID;
  protected $strIDname;	// name of unique ID field
 
//TO DO: set $strIDname at construct time; use it instead of hard-coded "ID"
// or maybe clsTitleExt should descend from clsDataItem_noID? Or need clsTitleIttyp class instead?
 
  protected function LoadResults() {
// USAGE: Descendants do not have to call this function
    CallEnter('('.get_class($this).')clsDataItem.LoadResults()');
    assert($this->Table);
    assert($this->Table->NameOfID());
    $this->ID		= $this->GetValue($this->Table->NameOfID());
    assert($this->ID);
    if (!isset($this->ID)) {
      echo '<br>TABLE: ['.$this->Table->Name().'] ID name: ['.$this->Table->NameOfID().']<br>';
    }
    CallExit('clsDataItem.LoadResults()');
  }
}
 
/* ========================
 *** UTILITY FUNCTIONS ***
*/
function Pluralize($iQty,$iSingular='',$iPlural='s') {
	if ($iQty == 1) {
		return $iSingular;
	} else {
		return $iPlural;
	}
}
/* ========================
 *** DEBUGGING FUNCTIONS ***
*/
 
// these could later be expanded to create a call-path for errors, etc.
 
function CallEnter($iName) {
  global $intCallDepth, $debug;
  if (KDO_DEBUG_STACK) {
    $intCallDepth++;
    if (KDO_DEBUG_HTML) {
      $debug .= '<br><span class="debug-enter"><b>'.str_repeat('&gt;&gt; ',$intCallDepth).'</b>'.$iName.'</span>';
    } else {
      $debug .= "\n\n".str_repeat('*',$intCallDepth).$iName;
    }
    if (KDO_DEBUG_IMMED) {
      echo $debug;
      $debug = '';
    }
  }
}
function CallExit($iName) {
  global $intCallDepth, $debug;
  if (KDO_DEBUG_STACK) {
    $intCallDepth--;
    if (KDO_DEBUG_HTML) {
      $debug .= '<br><span class="debug-exit"><b>'.str_repeat('&gt;&gt; ',$intCallDepth).'&lt;&lt; </b>'.$iName.'</span>';
    } else {
      $debug .= "\n\n".str_repeat('*',$intCallDepth).'<';
    }
    if (KDO_DEBUG_IMMED) {
      DoDebugStyle();
      echo $debug;
      $debug = '';
    }
  }
}
function CallStep($iName) {
  global $intCallDepth, $debug;
  if (KDO_DEBUG_STACK) {
    if (KDO_DEBUG_HTML) {
      $debug .= '<br><span class="debug-step"><b>'.str_repeat('&gt;&gt; ',$intCallDepth).'++ </b>'.$iName.'</span>';
    } else {
      $debug .= "\n\n".str_repeat('*',$intCallDepth).'+';
    }
    if (KDO_DEBUG_IMMED) {
      DoDebugStyle();
      echo $debug;
      $debug = '';
    }
  }
}
function DumpArray($iArr) {
  global $intCallDepth, $debug;
 
  if (KDO_DEBUG) {
    while (list($key, $val) = each($iArr)) {
      if (KS_DEBUG_HTML) {
        $debug .= '<br><span class="debug-dump"><b>'.str_repeat('-- ',$intCallDepth+1).'</b>';
        $debug .= " $key => $val";
        $debug .= '</span>';
      } else {
        $debug .= "/ $key => $val /";
      }
      if (KDO_DEBUG_IMMED) {
        DoDebugStyle();
        echo $debug;
        $debug = '';
      }
    }
  }
}
function DumpValue($iName,$iVal) {
  global $intCallDepth, $debug;
 
  if (KDO_DEBUG) {
    if (KS_DEBUG_HTML) {
      $debug .= '<br><span class="debug-dump"><b>'.str_repeat('-- ',$intCallDepth+1);
      $debug .= " $iName</b>: [$iVal]";
      $debug .= '</span>';
    } else {
      $debug .= "/ $iName => $iVal /";
    }
    if (KDO_DEBUG_IMMED) {
      DoDebugStyle();
      echo $debug;
      $debug = '';
    }
  }
}
function DoDebugStyle() {
  static $isStyleDone = false;
 
  if (!$isStyleDone) {
    echo '<style type="text/css"><!--';
    if (KDO_DEBUG_DARK) {
      echo '.debug-enter { background: #ffff00; }';
    } else {
      echo '.debug-enter { background: #666600; }';
    }
    echo '--></style>';
    $isStyleDone = true;
  }
}
 
?>
Personal tools