Ferreteria/archive/data.php: Difference between revisions

From Woozle Writes Code
Jump to navigation Jump to search
(12/24 code update)
(3/26 version)
Line 2: Line 2:
/* ===========================
/* ===========================
  *** DATA UTILITY CLASSES ***
  *** DATA UTILITY CLASSES ***
 
HISTORY:
(2007-05-20 Wzl) These classes have been designed to be db-engine agnostic, but I wasn't able
  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
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.
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-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
  2007-12-24 Wzl Some changes seem to have been made as recently as 12/17, so posting updated version
  2008-02-06 Wzl Modified to use either mysqli or (standard) mysql library depending on flag; the latter isn't working yet
  2009-03-10 Wzl adding some static functions to gradually get rid of the need for object factories
  2009-03-18 Wzl debug constants now have defaults
  2009-03-26 Wzl clsDataSet.Query() no longer fetches first row; this will require some rewriting
    NextRow() now returns TRUE if data was fetched; use if (data->NextRow()) {..} to loop through data.
*/
*/
// Select which DB library to use --
// exactly one of the following must be true:
define('KF_USE_MYSQL',TRUE); // in progress
define('KF_USE_MYSQLI',FALSE); // complete & tested
define('KF_USE_DBX',false); // not completely written; stalled
if (!defined('KDO_DEBUG')) { define('KDO_DEBUG',FALSE); }
if (!defined('KDO_DEBUG_STACK')) { define('KDO_DEBUG_STACK',FALSE); }
if (!defined('KDO_DEBUG_IMMED')) { define('KDO_DEBUG_IMMED',FALSE); }
if (!defined('KS_DEBUG_HTML')) { define('KS_DEBUG_HTML',FALSE); }
if (!defined('KDO_DEBUG_DARK')) { define('KDO_DEBUG_DARK',FALSE); }


class clsDatabase {
class clsDatabase {
/* =============
| STATIC SECTION
*/
// nothing yet
/* ==============
| DYNAMIC SECTION
*/
   private $cntOpen; // count of requests to keep db open
   private $cntOpen; // count of requests to keep db open
   private $strType; // type of db (MySQL etc.)
   private $strType; // type of db (MySQL etc.)
Line 24: Line 51:


   public function __construct($iConn) {
   public function __construct($iConn) {
    CallEnter('clsDatabase('.get_class($iConn).')');
     $this->Init($iConn);
     $this->Init($iConn);
    CallExit('clsDatabase()');
   }
   }
   public function Init($iConn) {
   public function Init($iConn) {
// $iConn format: type:user:pass@server/dbname
// $iConn format: type:user:pass@server/dbname
    CallEnter('clsDatabase.Init('.get_class($iConn).')');
     $this->cntOpen = 0;
     $this->cntOpen = 0;
     list($part1,$part2) = split('@',$iConn);
     list($part1,$part2) = split('@',$iConn);
Line 36: Line 60:
     list($this->strHost,$this->strName) = explode('/',$part2);
     list($this->strHost,$this->strName) = explode('/',$part2);
     $this->strType = strtolower($this->strType); // make sure it is lowercased, for comparison
     $this->strType = strtolower($this->strType); // make sure it is lowercased, for comparison
    CallExit('clsDatabase.Init()');
   }
   }
   public function Open() {
   public function Open() {
     CallEnter('clsDatabase.Open()');
     CallEnter($this,__LINE__,'clsDatabase.Open()');
     if ($this->cntOpen == 0) {
     if ($this->cntOpen == 0) {
// then actually open the db
// then actually open the db
      if ($this->strType == 'mysql') {
    if (KF_USE_MYSQL) {
        $this->Conn = new mysqli($this->strHost,$this->strUser,$this->strPass,$this->strName);
    $this->Conn = mysql_connect( $this->strHost, $this->strUser, $this->strPass, false );
      } else {
    assert('is_resource($this->Conn)');
        $this->Conn = dbx_connect($this->strType,$this->strHost,$this->strName,$this->strUser,$this->strPass);
    $ok = mysql_select_db($this->strName, $this->Conn);
       }
    if (!$ok) {
    $this->getError();
    }
    }
    if (KF_USE_MYSQLI) {
    $this->Conn = new mysqli($this->strHost,$this->strUser,$this->strPass,$this->strName);
    }
    if (KF_USE_DBX) {
    $this->Conn = dbx_connect($this->strType,$this->strHost,$this->strName,$this->strUser,$this->strPass);
    }
    }
    if (!$this->isOk()) {
       $this->getError();
     }
     }
     $this->cntOpen++;
     $this->cntOpen++;
Line 52: Line 87:
   }
   }
   public function Shut() {
   public function Shut() {
     CallEnter('clsDatabase.Shut()');
     CallEnter($this,__LINE__,'clsDatabase.Shut()');
     $this->cntOpen--;
     $this->cntOpen--;
     if ($this->cntOpen == 0) {
     if ($this->cntOpen == 0) {
      if ($this->strType == 'mysql') {
    if (KF_USE_MYSQL) {
        $this->Conn->close();
  mysql_close($this->Conn);
      } else {
    }
        dbx_close($this->Conn);
    if (KF_USE_MYSQLI) {
      }
  $this->Conn->close();
    }
    if (KF_USE_DBX) {
  dbx_close($this->Conn);
    }
     }
     }
     CallExit('clsDatabase.Shut() - '.$this->cntOpen.' lock'.Pluralize($this->cntOpen));
     CallExit('clsDatabase.Shut() - '.$this->cntOpen.' lock'.Pluralize($this->cntOpen));
   }
   }
   public function _api_query($iSQL) {
   public function GetHost() {
// USAGE: should only be used by clsDataItem
return $this->strHost;
     $this->sql = $iSQL;
  }
    if ($this->strType == 'mysql') {
  public function GetUser() {
      $this->Conn->real_query($iSQL);
return $this->strUser;
      return $this->Conn->store_result();
  }
     } else {
  public function isOpened() {
      return dbx_query($this->Conn,$iSQL,DBX_RESULT_ASSOC);
/*
  PURPOSE: For debugging, mainly
  RETURNS: TRUE if database connection is supposed to be open
*/
     return ($this->cntOpen > 0);
  }
  public function isOk() {
/*
  PURPOSE: For debugging, mainly
  RETURNS: TRUE if database connection was actually opened successfully
*/
if ($this->strErr) {
return FALSE;
} elseif ($this->Conn == FALSE) {
return FALSE;
} else {
return TRUE;
}
  }
  public function getError() {
    if (KF_USE_MYSQL) {
  $this->strErr = mysql_error();
     }
    if (KF_USE_MYSQLI) {
  $this->strErr = $this->Conn->error;
     }
     }
 
    return $this->strErr;
   }
   }
   public function Exec($iSQL) {
   public function Exec($iSQL) {
// MYSQL only
     CallEnter($this,__LINE__,__CLASS__.'.'.__METHOD__.'('.$iSQL.')');
     CallEnter('clsDataTable_noID.Exec('.$iSQL.')');
     $this->sql = $iSQL;
     $this->sql = $iSQL;
     $objQry = $this->Conn->prepare($iSQL);
     if (KF_USE_MYSQL) {
    if (is_object($objQry)) {
$ok = mysql_query($iSQL);
      $ok = $objQry->execute();
    }
      if (!$ok) {
    if (KF_USE_MYSQLI) {
        $this->txtErr = $this->Conn->error;
$objQry = $this->Conn->prepare($iSQL);
      }
if (is_object($objQry)) {
    } else {
  $ok = $objQry->execute();
      $ok = false;
} else {
      echo '<br>SQL error: '.$iSQL.'<br>';
  $ok = false;
  echo '<br>SQL error: '.$iSQL.'<br>';
}
    }
    if (!$ok) {
      $this->getError();
    }
    if (KF_USE_MYSQL) {
    // no need to do anything; no resource allocated as long as query SQL was non-data-fetching
    }
    if (KF_USE_MYSQLI) {
$objQry->close();
     }
     }
    $objQry->close();
     CallExit(__CLASS__.'.'.__METHOD__.'()');
     CallExit('clsDataTable_noID.Exec()');
     return $ok;
     return $ok;
   }
   }
   public function NewID() {
   public function NewID() {
     return $this->Conn->insert_id;
     if (KF_USE_MYSQL) {
mysql_insert_id($this->Conn);
    }
    if (KF_USE_MYSQLI) {
return $this->Conn->insert_id;
    }
   }
   }
   public function SafeParam($iString) {
   public function SafeParam($iString) {
     return $this->Conn->escape_string($iString);
     CallEnter($this,__LINE__,__CLASS__.'.SafeParam("'.$iString.'")');
    if (KF_USE_MYSQL) {
if (is_resource($this->Conn)) {
    $out = mysql_real_escape_string($iString,$this->Conn);
} else {
    $out = '<br>'.get_class($this).'.SafeParam("'.$iString.'") has no connection.';
}
    }
    if (KF_USE_MYSQLI) {
$out = $this->Conn->escape_string($iString);
    }
    CallExit('SafeParam("'.$iString.'")');
    return $out;
   }
   }
   public function ErrorText() {
   public function ErrorText() {
     return $this->txtErr;
     if ($this->strErr == '') {
      $this->_api_getError();
    }
    return $this->strErr;
  }
 
/* === API WRAPPER FUNCTIONS === */
// some of these could be static, but for now it seems simpler to keep them all together here
 
  public function _api_query($iSQL) {
    global $dbgSQL;
 
    $this->sql = $iSQL;
    $dbgSQL = $iSQL;
    if (KF_USE_MYSQL) {
return mysql_query($iSQL);
    }
    if (KF_USE_MYSQLI) {
$this->Conn->real_query($iSQL);
return $this->Conn->store_result();
    }
    if (KF_USE_DBX) {
return dbx_query($this->Conn,$iSQL,DBX_RESULT_ASSOC);
    }
 
  }
  public function _api_rows_rewind($iRes) {
    if (KF_USE_MYSQL) {
mysql_data_seek($iRes, 0);
    }
  }
  public function _api_fetch_row($iRes) {
  // ACTION: Fetch the first/next row of data from a result set
    if (KF_USE_MYSQL) {
assert('is_resource($iRes)');
return mysql_fetch_assoc($iRes);
    }
    if (KF_USE_MYSQLI) {
return $iRes->fetch_assoc();
    }
  }
  public function _api_count_rows($iRes) {
  // ACTION: Return the number of rows in the result set
    if (KF_USE_MYSQL) {
if ($iRes === FALSE) {
    return FALSE;
} else {
    assert('is_resource($iRes)');
    return mysql_num_rows($iRes);
}
    }
    if (KF_USE_MYSQLI) {
return $iRes->num_rows;
    }
  }
  public function _api_row_filled($iRow) {
    if (KF_USE_MYSQL) {
return ($iRow !== FALSE) ;
    }
  }
 
/* === OBJECT FACTORY === */
  public function DataSet($iSQL = NULL,$iClass = NULL) {
    CallEnter($this,__LINE__,__CLASS__.'.DataSet("'.$iSQL.'","'.$iClass.'")');
    if (is_string($iClass)) {
$objData = new $iClass($this);
assert('is_object($objData)');
if (!($objData instanceof clsDataSet)) {
    LogError($iClass.' is not a clsDataSet subclass.');
}
    } else {
$objData = new clsDataSet($this);
assert('is_object($objData)');
    }
    if (!is_null($iSQL)) {
      if (is_object($objData)) {
$objData->Query($iSQL);
      }
    }
    CallExit(__CLASS__.'.DataSet()');
    return $objData;
  }
 
}
 
class clsDataSet {
  protected $objDB;
  public $Res; // native result set
  public $Row; // data from the active row
 
  public function __construct(clsDatabase $iDB=NULL, $iRes=NULL, array $iRow=NULL) {
    CallEnter($this,__LINE__,__CLASS__.'.'.__FUNCTION__.'(['.get_class($iDB).'])');
    $this->objDB = $iDB;
    $this->Res = $iRes;
    $this->Row = $iRow;
    CallExit(__CLASS__.'.'.__FUNCTION__.'()');
  }
// -- loading and navigating through a data set
  public function Query($iSQL) {
    global $sql;
 
    CallEnter($this,__LINE__,__CLASS__.'.'.__FUNCTION__.'('.$iSQL.')');
    $sql = $iSQL;
    $this->Res = $this->objDB->_api_query($iSQL);
    //$sqlEsc = $this->objDB->SafeParam($sql);
    assert('is_resource($this->Res)'); // && ('$sqlEsc' != '')");
//    $this->NextRow(); // load the first row without wasting time rewinding
    CallExit(__CLASS__.'.'.__FUNCTION__.'()');
  }
  public function hasRows() {
// RETURNS: # of rows iff result has rows, otherwise FALSE
    $rows = $this->objDB->_api_count_rows($this->Res);
    if ($rows === FALSE) {
return FALSE;
    } elseif ($rows == 0) {
return FALSE;
    } else {
return $rows;
    }
  }
  public function hasRow() {
    return $this->objDB->_api_row_filled($this->Row);
  }
  public function RowCount() {
    return $this->objDB->_api_count_rows($this->Res);
  }
  public function FirstRow() {
    if ($this->hasRows()) {
$this->objDB->_api_rows_rewind($this->Res);
        $this->NextRow(); // get the first row of data
    }
  }
  public function NextRow() {
/* ACTION: Fetch the next row of data into $this->Row.
    If no data has been fetched yet, then fetch the first row.
  RETURN: TRUE if row was fetched; FALSE if there were no more rows
    or the row could not be fetched.
*/
    $this->Row = $this->objDB->_api_fetch_row($this->Res);
    return $this->hasRow();
  }
 
// -- accessing individual fields
  public function __set($iName, $iValue) {
    $this->Row[$iName] = $iValue;
  }
  public function __get($iName) {
    return $this->Row[$iName];
   }
   }
}
}


/*
class clsDataLine extends clsDataSet {
}
*/
/*=============
| NAME: clsTable
| PURPOSE: objects for operating on particular tables
*/
class clsTable {
    protected $objDB;
    protected $vTblName;
    protected $vKeyName;
    protected $vSngClass; // name of singular class
    public function __construct($iDB,$iTblName,$iKeyName,$iSngClass=NULL) {
$this->objDB = $iDB;
$this->vTblName = $iTblName;
$this->vKeyName = $iKeyName;
$this->vSngClass = $iSngClass;
    }
    public function Name() {
return $this->vTblName;
    }
    public function SingularName(string $iName=NULL) {
if (!is_null($iName)) {
    $this->vSngClass = $iName;
}
return $this->vSngClass;
    }
    public function GetItem($iID,$iClass=NULL) {
/* $sql = 'SELECT * FROM `'.$this->vTblName.'` WHERE '.$this->vKeyName.'='.$iID;
if (is_null($iClass)) {
    $strClass = $this->vSngClass;
} else {
    $strClass = $iClass;
}
return $this->objDB->DataSet($sql,$strClass);
*/
$objItem = $this->GetData($this->vKeyName.'='.$iID,$iClass);
$objItem->NextRow();
return $objItem;
    }
    public function GetData($iWhere,$iClass=NULL,$iSort=NULL) {
global $sql; // for debugging
CallEnter($this,__LINE__,__CLASS__.'.'.__METHOD__.'("'.$iWhere.'","'.$iClass.'")');
$sql = 'SELECT * FROM `'.$this->vTblName.'` WHERE '.$iWhere;
if (!is_null($iSort)) {
    $sql .= ' ORDER BY '.$iSort;
}
if (is_null($iClass)) {
    $strClass = $this->vSngClass;
} else {
    $strClass = $iClass;
}
CallExit('GetData() - SQL: '.$sql);
return $this->objDB->DataSet($sql,$strClass);
    }
}
// OLDER CLASSES -- DEPRECATED
/*
class clsData {
class clsData {
   protected $objDB; // clsDatabase
   protected $objDB; // clsDatabase
Line 120: Line 419:


   public function __construct($iDB,$iSQL) {
   public function __construct($iDB,$iSQL) {
     CallEnter('clsDataQuery('.get_class($iDB).','.$iSQL.')');
     CallEnter($this,__LINE__,'clsDataQuery('.get_class($iDB).','.$iSQL.')');
     parent::__construct($iDB);
     parent::__construct($iDB);
     $this->sqlSelect = $iSQL;
     $this->sqlSelect = $iSQL;
Line 141: Line 440:
   protected $strName;
   protected $strName;
   public function __construct($iDB,$iName) {
   public function __construct($iDB,$iName) {
     CallEnter('clsDataTable_noID('.get_class($iDB).','.$iName.')');
     CallEnter($this,__LINE__,'clsDataTable_noID('.get_class($iDB).','.$iName.')');
     parent::__construct($iDB);
     parent::__construct($iDB);
     $this->strName = $iName;
     $this->strName = $iName;
Line 150: Line 449:
   }
   }
   protected function _newItem() {
   protected function _newItem() {
     CallStep('('.get_class($this).')clsDataTable_noID._newItem()');
     CallStep('('.get_class($this).')'.__CLASS__.'.'.__FUNCTION__.'()');
     return new clsDataItem_noID($this);
     return new clsDataItem_noID($this);
   }
   }
Line 157: Line 456:
   }
   }
   public function GetData($iFilt='',$iSort='') {
   public function GetData($iFilt='',$iSort='') {
     CallEnter('clsDataTable.GetData(filt="'.$iFilt.'",sort="'.$iSort.'")');
     CallEnter($this,__LINE__,'clsDataTable.GetData(filt="'.$iFilt.'",sort="'.$iSort.'")');
     $sql = 'SELECT * FROM '.$this->strName;
     $sql = 'SELECT * FROM '.$this->strName;
     if ($iFilt != '') {
     if ($iFilt != '') {
Line 165: Line 464:
       $sql .= ' ORDER BY '.$iSort;
       $sql .= ' ORDER BY '.$iSort;
     }
     }
     CallStep('SQL = ['.$sql.']');
     CallStep('line '.__LINE__.' ['.get_class($this).'] SQL = ['.$sql.']');
     $objItem = $this->_newItem();
     $objItem = $this->_newItem();
     $objItem->Query($sql);
     $objItem->Query($sql);
Line 179: Line 478:


   public function __construct($iDB,$iName,$iIDname='ID') {
   public function __construct($iDB,$iName,$iIDname='ID') {
     CallEnter('clsDataTable('.get_class($iDB).','.$iName.')');
     CallEnter($this,__LINE__,'clsDataTable('.get_class($iDB).','.$iName.')');
     parent::__construct($iDB,$iName);
     parent::__construct($iDB,$iName);
     $this->strIDname = $iIDname;
     $this->strIDname = $iIDname;
Line 187: Line 486:
     return $this->strIDname;
     return $this->strIDname;
   }
   }
public function IDName() {
  return $this->strIDname;
}
   protected function _newItem() {
   protected function _newItem() {
     CallStep('('.get_class($this).')clsDataTable._newItem()');
     CallStep('('.get_class($this).')clsDataTable._newItem()');
Line 192: Line 496:
   }
   }
   public function GetItem($iID) {
   public function GetItem($iID) {
     CallEnter('('.get_class($this).')clsDataTable.GetItem('.$iID.')');
     CallEnter($this,__LINE__,'clsDataTable.GetItem('.$iID.')');
     $sql = 'SELECT * FROM '.$this->strName.' WHERE '.$this->strIDname.'="'.$iID.'"';
     $sql = 'SELECT * FROM '.$this->strName.' WHERE '.$this->strIDname.'="'.$iID.'"';
     CallStep('SQL = ['.$sql.']');
     CallStep('SQL = ['.$sql.']');
Line 209: Line 513:
   public $Table; // clsDataTable object
   public $Table; // clsDataTable object
   public function __construct($iTable=NULL) {
   public function __construct($iTable=NULL) {
     CallEnter('clsDataItem_noID('.get_class($iTable).')');
     CallEnter($this,__LINE__,'clsDataItem_noID('.get_class($iTable).')');
     $this->Init($iTable);
     $this->Init($iTable);
     CallExit('clsDataItem_noID()');
     CallExit('clsDataItem_noID()');
   }
   }
   public function Init($iTable=NULL) {
   public function Init($iTable=NULL) {
     CallEnter('('.get_class($this).')clsDataItem_noID.Init('.get_class($iTable).')');
     CallEnter($this,__LINE__,'clsDataItem_noID.Init('.get_class($iTable).')');
     $this->Table = $iTable;  
     $this->Table = $iTable;  
// this works for mysql only:
// this works for mysql only:
Line 221: Line 525:
     CallExit('clsDataItem_noID.Init()');
     CallExit('clsDataItem_noID.Init()');
   }
   }
   public function Query($iSQL) {
   public function isResOk() {
    CallEnter('('.get_class($this).')clsDataItem_noID.Query('.$iSQL.')');
if (KF_USE_MYSQL) {
    assert($this->Table);
return $this->Res !== FALSE;
    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() {
   protected function LoadResults() {
Line 244: Line 536:
   public function GetValue($iName) {
   public function GetValue($iName) {
// this works for mysql only
// this works for mysql only
     CallEnter('clsDataTable_noID.GetValue('.$iName.')');
     CallEnter($this,__LINE__,'clsDataTable_noID.GetValue('.$iName.')');
//    DumpArray($this->Row);
//    DumpArray($this->Row);
     $val = $this->Row[$iName];
     $val = $this->Row[$iName];
Line 251: Line 543:
   }
   }
   public function RowCount() {
   public function RowCount() {
if (KF_USE_MYSQL) {
    if (is_resource($this->Res)) {
      $cntRows = mysql_num_rows($this->Res);
    } else {
      echo '<br>BAD RESOURCE in '.get_class($this).'.RowCount()';
    }
}
if (KF_USE_MYSQLI) {
     $cntRows = $this->Res->num_rows;
     $cntRows = $this->Res->num_rows;
}
     CallStep('('.get_class($this).')clsDataItem_noID.RowCount() -> '.$cntRows);
     CallStep('('.get_class($this).')clsDataItem_noID.RowCount() -> '.$cntRows);
     return $cntRows;
     return $cntRows;
Line 260: Line 561:
   public function HasRows($iMin=1) {
   public function HasRows($iMin=1) {
     if ($this->HasData()) {
     if ($this->HasData()) {
      assert('is_resource($this->Res)');
       return ($this->RowCount() >= $iMin);
       return ($this->RowCount() >= $iMin);
     } else {
     } else {
Line 266: Line 568:
   }
   }
   private function FirstRow() {
   private function FirstRow() {
     CallEnter('('.get_class($this).')clsDataItem_noID.FirstRow()');
     CallEnter($this,__LINE__,'clsDataItem_noID.FirstRow()');
     if (!is_null($this->Res)) {
     if (is_resource($this->Res)) {
       if ($this->RowCount()) {
       if ($this->RowCount()) {
         $this->NextRow(); // get the first row of data
         $this->NextRow(); // get the first row of data
Line 277: Line 579:
// this works for mysql only:
// this works for mysql only:
// fetch the NEXT row of the results as an associative array:
// fetch the NEXT row of the results as an associative array:
if (KF_USE_MYSQL) {
    $this->Row = mysql_fetch_assoc($this->Res);
}
if (KF_USE_MYSQLI) {
     $this->Row = $this->Res->fetch_assoc();
     $this->Row = $this->Res->fetch_assoc();
}
     if (is_array($this->Row)) {
     if (is_array($this->Row)) {
       $this->LoadResults();
       $this->LoadResults();
Line 295: Line 602:
   protected function LoadResults() {
   protected function LoadResults() {
// USAGE: Descendants do not have to call this function
// USAGE: Descendants do not have to call this function
     CallEnter('('.get_class($this).')clsDataItem.LoadResults()');
     CallEnter($this,__LINE__,'clsDataItem.LoadResults()');
     assert($this->Table);
     assert($this->Table);
     assert($this->Table->NameOfID());
     $strID = $this->Table->NameOfID();
     $this->ID = $this->GetValue($this->Table->NameOfID());
    assert(($strID != '') && ($strID != FALSE));
     $this->ID = $this->GetValue($strID);
     assert($this->ID);
     assert($this->ID);
if (KDO_DEBUG) {
     if (!isset($this->ID)) {
     if (!isset($this->ID)) {
       echo '<br>TABLE: ['.$this->Table->Name().'] ID name: ['.$this->Table->NameOfID().']<br>';
       echo '<br>TABLE: ['.$this->Table->Name().'] ID name: ['.$strID.']<br>';
     }
     }
}
     CallExit('clsDataItem.LoadResults()');
     CallExit('clsDataItem.LoadResults()');
   }
   }
}
}
 
*/
/* ========================
/* ========================
  *** UTILITY FUNCTIONS ***
  *** UTILITY FUNCTIONS ***
Line 323: Line 633:
// these could later be expanded to create a call-path for errors, etc.
// these could later be expanded to create a call-path for errors, etc.


function CallEnter($iName) {
function CallEnter($iObj,$iLine,$iName) {
   global $intCallDepth, $debug;
   global $intCallDepth, $debug;
   if (KDO_DEBUG_STACK) {
   if (KDO_DEBUG_STACK) {
    $strDescr =  ' line '.$iLine.' ('.get_class($iObj).')'.$iName;
    _debugLine('enter','&gt;',$strDescr);
     $intCallDepth++;
     $intCallDepth++;
     if (KDO_DEBUG_HTML) {
     _debugDump();
      $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 = '';
    }
   }
   }
}
}
Line 342: Line 646:
   if (KDO_DEBUG_STACK) {
   if (KDO_DEBUG_STACK) {
     $intCallDepth--;
     $intCallDepth--;
     if (KDO_DEBUG_HTML) {
     _debugLine('exit','&lt;',$iName);
      $debug .= '<br><span class="debug-exit"><b>'.str_repeat('&gt;&gt; ',$intCallDepth).'&lt;&lt; </b>'.$iName.'</span>';
     _debugDump();
     } else {
  }
      $debug .= "\n\n".str_repeat('*',$intCallDepth).'<';
}
    }
function CallStep($iDescr) {
    if (KDO_DEBUG_IMMED) {
  global $intCallDepth, $debug;
      DoDebugStyle();
  if (KDO_DEBUG_STACK) {
      echo $debug;
    _debugLine('step',':',$iDescr);
      $debug = '';
     _debugDump();
     }
   }
   }
}
}
function CallStep($iName) {
function LogError($iDescr) {
   global $intCallDepth, $debug;
   global $intCallDepth, $debug;
   if (KDO_DEBUG_STACK) {
   if (KDO_DEBUG_STACK) {
    _debugLine('error',':',$iDescr);
    _debugDump();
  }
}
function _debugLine($iType,$iSfx,$iText) {
    global $intCallDepth, $debug;
     if (KDO_DEBUG_HTML) {
     if (KDO_DEBUG_HTML) {
       $debug .= '<br><span class="debug-step"><b>'.str_repeat('&gt;&gt; ',$intCallDepth).'++ </b>'.$iName.'</span>';
       $debug .= '<span class="debug-'.$iType.'"><b>'.str_repeat('&mdash;',$intCallDepth).$iSfx.'</b> '.$iText.'</span><br>';
     } else {
     } else {
       $debug .= "\n\n".str_repeat('*',$intCallDepth).'+';
       $debug .= str_repeat('*',$intCallDepth).'++ '.$iText."\n";
     }
     }
}
function _debugDump() {
    global $debug;
     if (KDO_DEBUG_IMMED) {
     if (KDO_DEBUG_IMMED) {
      DoDebugStyle();
DoDebugStyle();
      echo $debug;
echo $debug;
      $debug = '';
$debug = '';
     }
     }
  }
}
}
function DumpArray($iArr) {
function DumpArray($iArr) {
Line 413: Line 727:
     echo '<style type="text/css"><!--';
     echo '<style type="text/css"><!--';
     if (KDO_DEBUG_DARK) {
     if (KDO_DEBUG_DARK) {
       echo '.debug-enter { background: #ffff00; }';
       echo '.debug-enter { background: #666600; }'; // dark highlight
     } else {
     } else {
       echo '.debug-enter { background: #666600; }';
       echo '.debug-enter { background: #ffff00; }'; // regular yellow highlight
     }
     }
     echo '--></style>';
     echo '--></style>';
Line 421: Line 735:
   }
   }
}
}
 
</php>
?></php>

Revision as of 12:04, 5 April 2009

<php><?php /* ===========================

*** DATA UTILITY CLASSES ***

HISTORY:

 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
 2008-02-06 Wzl Modified to use either mysqli or (standard) mysql library depending on flag; the latter isn't working yet
 2009-03-10 Wzl adding some static functions to gradually get rid of the need for object factories
 2009-03-18 Wzl debug constants now have defaults
 2009-03-26 Wzl clsDataSet.Query() no longer fetches first row; this will require some rewriting
   NextRow() now returns TRUE if data was fetched; use if (data->NextRow()) {..} to loop through data.
  • /

// Select which DB library to use -- // exactly one of the following must be true: define('KF_USE_MYSQL',TRUE); // in progress define('KF_USE_MYSQLI',FALSE); // complete & tested define('KF_USE_DBX',false); // not completely written; stalled

if (!defined('KDO_DEBUG')) { define('KDO_DEBUG',FALSE); } if (!defined('KDO_DEBUG_STACK')) { define('KDO_DEBUG_STACK',FALSE); } if (!defined('KDO_DEBUG_IMMED')) { define('KDO_DEBUG_IMMED',FALSE); } if (!defined('KS_DEBUG_HTML')) { define('KS_DEBUG_HTML',FALSE); } if (!defined('KDO_DEBUG_DARK')) { define('KDO_DEBUG_DARK',FALSE); }

class clsDatabase {

/* ============= | STATIC SECTION

  • /

// nothing yet

/* ============== | DYNAMIC SECTION

  • /
 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) {
   $this->Init($iConn);
 }
 public function Init($iConn) {

// $iConn format: type:user:pass@server/dbname

   $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
 }
 public function Open() {
   CallEnter($this,__LINE__,'clsDatabase.Open()');
   if ($this->cntOpen == 0) {

// then actually open the db

   if (KF_USE_MYSQL) {

$this->Conn = mysql_connect( $this->strHost, $this->strUser, $this->strPass, false ); assert('is_resource($this->Conn)'); $ok = mysql_select_db($this->strName, $this->Conn); if (!$ok) { $this->getError(); }

   }
   if (KF_USE_MYSQLI) {

$this->Conn = new mysqli($this->strHost,$this->strUser,$this->strPass,$this->strName);

   }
   if (KF_USE_DBX) {

$this->Conn = dbx_connect($this->strType,$this->strHost,$this->strName,$this->strUser,$this->strPass);

   }
   }
   if (!$this->isOk()) {
     $this->getError();
   }
   $this->cntOpen++;
   CallExit('clsDatabase.Open() - '.$this->cntOpen.' lock'.Pluralize($this->cntOpen));
 }
 public function Shut() {
   CallEnter($this,__LINE__,'clsDatabase.Shut()');
   $this->cntOpen--;
   if ($this->cntOpen == 0) {
   if (KF_USE_MYSQL) {

mysql_close($this->Conn);

   }
   if (KF_USE_MYSQLI) {

$this->Conn->close();

   }
   if (KF_USE_DBX) {

dbx_close($this->Conn);

   }
   }
   CallExit('clsDatabase.Shut() - '.$this->cntOpen.' lock'.Pluralize($this->cntOpen));
 }
 public function GetHost() {

return $this->strHost;

 }
 public function GetUser() {

return $this->strUser;

 }
 public function isOpened() {

/*

 PURPOSE: For debugging, mainly
 RETURNS: TRUE if database connection is supposed to be open
  • /
   return ($this->cntOpen > 0);
 }
 public function isOk() {

/*

 PURPOSE: For debugging, mainly
 RETURNS: TRUE if database connection was actually opened successfully
  • /

if ($this->strErr) { return FALSE; } elseif ($this->Conn == FALSE) { return FALSE; } else { return TRUE; }

 }
 public function getError() {
   if (KF_USE_MYSQL) {

$this->strErr = mysql_error();

   }
   if (KF_USE_MYSQLI) {

$this->strErr = $this->Conn->error;

   }
   return $this->strErr;
 }
 public function Exec($iSQL) {
   CallEnter($this,__LINE__,__CLASS__.'.'.__METHOD__.'('.$iSQL.')');
   $this->sql = $iSQL;
   if (KF_USE_MYSQL) {

$ok = mysql_query($iSQL);

   }
   if (KF_USE_MYSQLI) {

$objQry = $this->Conn->prepare($iSQL); if (is_object($objQry)) { $ok = $objQry->execute(); } else { $ok = false; echo '
SQL error: '.$iSQL.'
'; }

   }
   if (!$ok) {
     $this->getError();
   }
   if (KF_USE_MYSQL) {
   // no need to do anything; no resource allocated as long as query SQL was non-data-fetching
   }
   if (KF_USE_MYSQLI) {

$objQry->close();

   }
   CallExit(__CLASS__.'.'.__METHOD__.'()');
   return $ok;
 }
 public function NewID() {
   if (KF_USE_MYSQL) {

mysql_insert_id($this->Conn);

   }
   if (KF_USE_MYSQLI) {

return $this->Conn->insert_id;

   }
 }
 public function SafeParam($iString) {
   CallEnter($this,__LINE__,__CLASS__.'.SafeParam("'.$iString.'")');
   if (KF_USE_MYSQL) {

if (is_resource($this->Conn)) { $out = mysql_real_escape_string($iString,$this->Conn); } else { $out = '
'.get_class($this).'.SafeParam("'.$iString.'") has no connection.'; }

   }
   if (KF_USE_MYSQLI) {

$out = $this->Conn->escape_string($iString);

   }
   CallExit('SafeParam("'.$iString.'")');
   return $out;
 }
 public function ErrorText() {
   if ($this->strErr == ) {
     $this->_api_getError();
   }
   return $this->strErr;
 }

/* === API WRAPPER FUNCTIONS === */ // some of these could be static, but for now it seems simpler to keep them all together here

 public function _api_query($iSQL) {
   global $dbgSQL;
   $this->sql = $iSQL;
   $dbgSQL = $iSQL;
   if (KF_USE_MYSQL) {

return mysql_query($iSQL);

   }
   if (KF_USE_MYSQLI) {

$this->Conn->real_query($iSQL); return $this->Conn->store_result();

   }
   if (KF_USE_DBX) {

return dbx_query($this->Conn,$iSQL,DBX_RESULT_ASSOC);

   }
 }
 public function _api_rows_rewind($iRes) {
   if (KF_USE_MYSQL) {

mysql_data_seek($iRes, 0);

   }
 }
 public function _api_fetch_row($iRes) {
 // ACTION: Fetch the first/next row of data from a result set
   if (KF_USE_MYSQL) {

assert('is_resource($iRes)'); return mysql_fetch_assoc($iRes);

   }
   if (KF_USE_MYSQLI) {

return $iRes->fetch_assoc();

   }
 }
 public function _api_count_rows($iRes) {
 // ACTION: Return the number of rows in the result set
   if (KF_USE_MYSQL) {

if ($iRes === FALSE) { return FALSE; } else { assert('is_resource($iRes)'); return mysql_num_rows($iRes); }

   }
   if (KF_USE_MYSQLI) {

return $iRes->num_rows;

   }
 }
 public function _api_row_filled($iRow) {
   if (KF_USE_MYSQL) {

return ($iRow !== FALSE) ;

   }
 }

/* === OBJECT FACTORY === */

 public function DataSet($iSQL = NULL,$iClass = NULL) {
   CallEnter($this,__LINE__,__CLASS__.'.DataSet("'.$iSQL.'","'.$iClass.'")');
   if (is_string($iClass)) {

$objData = new $iClass($this); assert('is_object($objData)'); if (!($objData instanceof clsDataSet)) { LogError($iClass.' is not a clsDataSet subclass.'); }

   } else {

$objData = new clsDataSet($this); assert('is_object($objData)');

   }
   if (!is_null($iSQL)) {
     if (is_object($objData)) {

$objData->Query($iSQL);

     }
   }
   CallExit(__CLASS__.'.DataSet()');
   return $objData;
 }

}

class clsDataSet {

 protected $objDB;
 public $Res;		// native result set
 public $Row;		// data from the active row
 public function __construct(clsDatabase $iDB=NULL, $iRes=NULL, array $iRow=NULL) {
   CallEnter($this,__LINE__,__CLASS__.'.'.__FUNCTION__.'(['.get_class($iDB).'])');
   $this->objDB = $iDB;
   $this->Res = $iRes;
   $this->Row = $iRow;
   CallExit(__CLASS__.'.'.__FUNCTION__.'()');
 }

// -- loading and navigating through a data set

 public function Query($iSQL) {
   global $sql;
   CallEnter($this,__LINE__,__CLASS__.'.'.__FUNCTION__.'('.$iSQL.')');
   $sql = $iSQL;
   $this->Res = $this->objDB->_api_query($iSQL);
   //$sqlEsc = $this->objDB->SafeParam($sql);
   assert('is_resource($this->Res)'); // && ('$sqlEsc' != )");

// $this->NextRow(); // load the first row without wasting time rewinding

   CallExit(__CLASS__.'.'.__FUNCTION__.'()');
 }
 public function hasRows() {

// RETURNS: # of rows iff result has rows, otherwise FALSE

   $rows = $this->objDB->_api_count_rows($this->Res);
   if ($rows === FALSE) {

return FALSE;

   } elseif ($rows == 0) {

return FALSE;

   } else {

return $rows;

   }
 }
 public function hasRow() {
   return $this->objDB->_api_row_filled($this->Row);
 }
 public function RowCount() {
   return $this->objDB->_api_count_rows($this->Res);
 }
 public function FirstRow() {
   if ($this->hasRows()) {

$this->objDB->_api_rows_rewind($this->Res);

       $this->NextRow();	// get the first row of data
   }
 }
 public function NextRow() {

/* ACTION: Fetch the next row of data into $this->Row.

   If no data has been fetched yet, then fetch the first row.
 RETURN: TRUE if row was fetched; FALSE if there were no more rows
   or the row could not be fetched.
  • /
   $this->Row = $this->objDB->_api_fetch_row($this->Res);
   return $this->hasRow();
 }

// -- accessing individual fields

 public function __set($iName, $iValue) {
   $this->Row[$iName] = $iValue;
 }
 public function __get($iName) {
   return $this->Row[$iName];
 }

}

/* class clsDataLine extends clsDataSet { }

  • /

/*============= | NAME: clsTable | PURPOSE: objects for operating on particular tables

  • /

class clsTable {

   protected $objDB;
   protected $vTblName;
   protected $vKeyName;
   protected $vSngClass;	// name of singular class
   public function __construct($iDB,$iTblName,$iKeyName,$iSngClass=NULL) {

$this->objDB = $iDB; $this->vTblName = $iTblName; $this->vKeyName = $iKeyName; $this->vSngClass = $iSngClass;

   }
   public function Name() {

return $this->vTblName;

   }
   public function SingularName(string $iName=NULL) {

if (!is_null($iName)) { $this->vSngClass = $iName; } return $this->vSngClass;

   }
   public function GetItem($iID,$iClass=NULL) {

/* $sql = 'SELECT * FROM `'.$this->vTblName.'` WHERE '.$this->vKeyName.'='.$iID; if (is_null($iClass)) { $strClass = $this->vSngClass; } else { $strClass = $iClass; } return $this->objDB->DataSet($sql,$strClass);

  • /

$objItem = $this->GetData($this->vKeyName.'='.$iID,$iClass); $objItem->NextRow(); return $objItem;

   }
   public function GetData($iWhere,$iClass=NULL,$iSort=NULL) {

global $sql; // for debugging

CallEnter($this,__LINE__,__CLASS__.'.'.__METHOD__.'("'.$iWhere.'","'.$iClass.'")'); $sql = 'SELECT * FROM `'.$this->vTblName.'` WHERE '.$iWhere; if (!is_null($iSort)) { $sql .= ' ORDER BY '.$iSort; } if (is_null($iClass)) { $strClass = $this->vSngClass; } else { $strClass = $iClass; } CallExit('GetData() - SQL: '.$sql); return $this->objDB->DataSet($sql,$strClass);

   }

}

// OLDER CLASSES -- DEPRECATED /* 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($this,__LINE__,'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($this,__LINE__,'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).')'.__CLASS__.'.'.__FUNCTION__.'()');
   return new clsDataItem_noID($this);
 }
 public function Name() {
   return $this->strName;
 }
 public function GetData($iFilt=,$iSort=) {
   CallEnter($this,__LINE__,'clsDataTable.GetData(filt="'.$iFilt.'",sort="'.$iSort.'")');
   $sql = 'SELECT * FROM '.$this->strName;
   if ($iFilt != ) {
     $sql .= ' WHERE ('.$iFilt.')';
   }
   if ($iSort != ) {
     $sql .= ' ORDER BY '.$iSort;
   }
   CallStep('line '.__LINE__.' ['.get_class($this).'] 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($this,__LINE__,'clsDataTable('.get_class($iDB).','.$iName.')');
   parent::__construct($iDB,$iName);
   $this->strIDname = $iIDname;
   CallExit('clsDataTable()');
 }
 public function NameOfID() {
   return $this->strIDname;
 }

public function IDName() {

 return $this->strIDname;

}

 protected function _newItem() {
   CallStep('('.get_class($this).')clsDataTable._newItem()');
   return new clsDataItem($this);
 }
 public function GetItem($iID) {
   CallEnter($this,__LINE__,'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($this,__LINE__,'clsDataItem_noID('.get_class($iTable).')');
   $this->Init($iTable);
   CallExit('clsDataItem_noID()');
 }
 public function Init($iTable=NULL) {
   CallEnter($this,__LINE__,'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 isResOk() {

if (KF_USE_MYSQL) { return $this->Res !== FALSE; }

 }
 protected function LoadResults() {

// USAGE: Abstract

   CallStep('('.get_class($this).')clsDataItem_noID.LoadResults()');
 }
 public function GetValue($iName) {

// this works for mysql only

   CallEnter($this,__LINE__,'clsDataTable_noID.GetValue('.$iName.')');

// DumpArray($this->Row);

   $val = $this->Row[$iName];
   CallExit('clsDataTable_noID.GetValue('.$iName.') -> ['.$val.']');
   return $val;
 }
 public function RowCount() {

if (KF_USE_MYSQL) {

   if (is_resource($this->Res)) {
     $cntRows = mysql_num_rows($this->Res);
   } else {
     echo '
BAD RESOURCE in '.get_class($this).'.RowCount()'; }

} if (KF_USE_MYSQLI) {

   $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()) {
     assert('is_resource($this->Res)');
     return ($this->RowCount() >= $iMin);
   } else {
     return 0;
   }
 }
 private function FirstRow() {
   CallEnter($this,__LINE__,'clsDataItem_noID.FirstRow()');
   if (is_resource($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: if (KF_USE_MYSQL) {

   $this->Row = mysql_fetch_assoc($this->Res);

} if (KF_USE_MYSQLI) {

   $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($this,__LINE__,'clsDataItem.LoadResults()');
   assert($this->Table);
   $strID = $this->Table->NameOfID();
   assert(($strID != ) && ($strID != FALSE));
   $this->ID = $this->GetValue($strID);
   assert($this->ID);

if (KDO_DEBUG) {

   if (!isset($this->ID)) {
     echo '
TABLE: ['.$this->Table->Name().'] ID name: ['.$strID.']
'; }

}

   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($iObj,$iLine,$iName) {

 global $intCallDepth, $debug;
 if (KDO_DEBUG_STACK) {
   $strDescr =  ' line '.$iLine.' ('.get_class($iObj).')'.$iName;
   _debugLine('enter','>',$strDescr);
   $intCallDepth++;
   _debugDump();
 }

} function CallExit($iName) {

 global $intCallDepth, $debug;
 if (KDO_DEBUG_STACK) {
   $intCallDepth--;
   _debugLine('exit','<',$iName);
   _debugDump();
 }

} function CallStep($iDescr) {

 global $intCallDepth, $debug;
 if (KDO_DEBUG_STACK) {
   _debugLine('step',':',$iDescr);
   _debugDump();
 }

} function LogError($iDescr) {

 global $intCallDepth, $debug;
 if (KDO_DEBUG_STACK) {
   _debugLine('error',':',$iDescr);
   _debugDump();
 }

} function _debugLine($iType,$iSfx,$iText) {

   global $intCallDepth, $debug;
   if (KDO_DEBUG_HTML) {
     $debug .= ''.str_repeat('—',$intCallDepth).$iSfx.' '.$iText.'
'; } else { $debug .= str_repeat('*',$intCallDepth).'++ '.$iText."\n"; }

} function _debugDump() {

   global $debug;
   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 .= '
'.str_repeat('-- ',$intCallDepth+1).''; $debug .= " $key => $val"; $debug .= ''; } 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 .= '
'.str_repeat('-- ',$intCallDepth+1); $debug .= " $iName: [$iVal]"; $debug .= ''; } else { $debug .= "/ $iName => $iVal /"; } if (KDO_DEBUG_IMMED) { DoDebugStyle(); echo $debug; $debug = ; } }

} function DoDebugStyle() {

 static $isStyleDone = false;
 if (!$isStyleDone) {
   echo '<style type="text/css"></style>';
   $isStyleDone = true;
 }

} </php>