Location: PHPKode > projects > OpenNitro > trunk/Nitro/NitroDB.inc.php
<?php
//
// +---------------------------------------------------------------------------+
// | Nitro :: NitroDB                                                               |
// +---------------------------------------------------------------------------+
// | Copyright (c) 2003 June Systems BV                                        |
// +---------------------------------------------------------------------------+
// | This library is free software; you can redistribute it and/or modify it   |
// | under the terms of the GNU Lesser General Public License as published by  |
// | the Free Software Foundation; either version 2.1 of the License, or (at   |
// | your option) any later version.                                           |
// |                                                                           |
// | This library is distributed in the hope that it will be useful, but       |
// | WITHOUT ANY WARRANTY; without even the implied warranty of                |
// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser   |
// | General Public License for more details.                                  |
// |                                                                           |
// | You should have received a copy of the GNU Lesser General Public License  |
// | along with this library; if not, write to the Free Software Foundation,   |
// | Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA            |
// +---------------------------------------------------------------------------+
// | Authors: Siggi Oskarsson <hide@address.com>                          |
// +---------------------------------------------------------------------------+
//
// $Id: NitroDB.inc.php 229 2008-04-17 09:20:31Z oli $
//
// Nitro database base class
//

/**
 * This file contains the main DB class of Nitro
 *
 * @package	Nitro
 * @subpackage	DB
 * @author 		Siggi Oskarsson
 * @version 	$Revision: 1.19 $
 * @copyright	2004 June Systems BV
 */

/**
 * Define fetchmodes used in the DB connection
 */
define("NITRODB_NUM", 1);
define("NITRODB_ASSOC", 2);
define("NITRODB_BOTH", 3);
define("DB_FETCHMODE_NUM", NITRODB_NUM);
define("DB_FETCHMODE_ASSOC", NITRODB_ASSOC);
define("DB_FETCHMODE_BOTH", NITRODB_BOTH);
include_once "Nitro/NitroDB/Nitro.inc.php";
include_once "Nitro/Transaction.inc.php";

/**
 * Main DB class definition
 *
 * This is the prototype DB class definition that is actually called when
 * the DB layer is used. This class initializes and uses sub classes (like MySQL) for
 * the actual calls to the database.
 *
 * <CODE>
 * $DBConnection = new DB('mysql://root:@localhost/Nitro');
 * if ($DBConnection->Error) {
 *   echo "ERROR: Failed connecting to database";
 * } else {
 *   $Result = $DBConnection->query('SELECT * FROM User');
 *   while($Row = $Result->FetchArray()) {
 *     echo $Row['Name'].'<BR>';
 *   }
 *   $Result->free();
 * }
 *
 * $UserName     = $DBConnection->getOne('SELECT Name FROM User WHERE UserID = 1');
 * $AllUserNames = $DBConnection->getCol('SELECT Name FROM User');
 * $UserArray    = $DBConnection->getRow('SELECT * FROM User WHERE UserID = 1');
 * </CODE>
 *
 * @package	Nitro
 * @subpackage	DB
 */
class DB {
	/**
	 * @var	array	Array of DB DSN (i.e. database_type://username:[password]@host/database_name)
	 */
	var $DSN = Array();
	/**
	 * @var	string	Error string
	 */
	var $Error;
	
	/**
	 * @var	array	Database type (i.e. MySQL)
	 */
	var $DBType = Array();
	/**
	 * @var	array	Username for connection
	 */
	var $Username = Array();
	/**
	 * @var	array	Password for connection
	 */
	var $Password = Array();
	/**
	 * @var	array	Host for connection
	 */
	var $Host = Array();
	/**
	 * @var	array	Port on Host for connection
	 */
	var $Port = Array();
	/**
	 * @var	array	Database for connection
	 */
	var $Database = Array();
	/**
	 * @var	array	Database connection options
	 */
	var $Options = Array();
	/**
	 * @var	array	Force the creation of a new link when connectiong
	 */
	var $NewLink = Array();
	/**
	 * @var	array	Array of Database handler objects
	 */
	var $DBHandler = Array();
	/**
	 * @var	array	Database connection
	 */
	var $ConnectionID = Array();
	/**
	 * @var	int	Index of handler used for last query
	 */
	var $LastIndex;
	/**
	 * @var	string	Load Balance Method to use in determining server to use (Random, RandomFixed (default), RoundRobin, Load, Weighted)
	 */
	var $BalanceMethod = 'RandomFixed';

	/**
	 * @var	array	Cache array of list of tables in database
	 */
	var $TableList;

	/**
	 * Class constructor function
	 *
	 * @param	string	$DSN	DSN to use for connection (database_type://username:[password]@host/database_name)
	 * @param	boolean	$NewLink	Force new link or allow to reuse link if host and login the same
	 * @param	boolean	$AutoConnect	Automatically connect to the database
	 * @access	public
	 */
	function DB($DSN, $NewLink = FALSE, $AutoConnect = FALSE)
	{
		DebugGroup("DB", "DB", "Initializing DB connection from DSN", __FILE__, __LINE__, DEBUG_SQL_OK);

		$this->AddHandler($DSN, $NewLink, $AutoConnect);
		if ($this->Options[0]['BalanceMethod']) $this->BalanceMethod = $this->Options[0]['BalanceMethod'];
		if (!$this->Options[0]['Mode']) $this->Options[0]['Mode'] = 'RW'; // first server is Master/RW unless otherwise specified

		DebugCloseGroup(DEBUG_SQL_OK);
		
		return $RV;
	}

	/**
	 * Add a database Handler to database object
	 *
	 * The database handler added can be type of RO (read-only) or RW (read-write).
	 *
	 * @param	string	$DSN	DSN to use for connection (database_type://username:[password]@host/database_name)
	 * @param	boolean	$NewLink	Force new link or allow to reuse link if host and login the same
	 * @param	boolean	$AutoConnect	Automatically connect to the database
	 * @access	public
	 */
	function AddHandler($DSN, $NewLink = FALSE, $AutoConnect = FALSE)
	{
		$Index = (int)count($this->DSN);

		$this->DSN[$Index] = $DSN;
		$this->NewLink[$Index] = $NewLink;
		if ($this->SetDSN($DSN, $Index)) {
			if ($AutoConnect) {
				$this->Connect($Index);
			} else {
				$this->ConnectionID[$Index] = -1;
			}
		} else {
			Debug("DB", "DB", "DSN is not valid $DSN ($this->Error)", __FILE__, __LINE__, DEBUG_SQL_ERR|DEBUG_APPL_ERR);
			$this->Error = "DSN is not valid!";
			$RV = FALSE;
		}
		
		$this->AllIndexes = array_keys($this->DSN);
		
		return $RV;
	}
	
	/**
	 * Remove handler from AllIndex list which is used for balancing
	 *
	 * @param	int	$Index	Index of server to remove
	 * @access	public
	 */
	function RemoveHandler($Index)
	{
		$newAllIndexes = Array();
		foreach($this->AllIndexes AS $id) {
			if ($id != $Index) {
				$newAllIndexes[] = $id;
			}
		}
		
		$this->AllIndexes = $newAllIndexes;
	}

	/**
	 * Database connection function
	 *
	 * @param	boolean	$NewLink	Force new link or allow to reuse link if host and login the same
	 * @access	public
	 */
	function Connect($Index = 0)
	{
		DebugGroup("DB", "DB", "Connection to database ".$this->Host[$Index], __FILE__, __LINE__, DEBUG_SQL_OK);
		include_once "Nitro/NitroDB/".$this->DBType[$Index].".inc.php";
		eval('$this->DBHandler[$Index] = new DB_'.$this->DBType[$Index].'($this->Host[$Index], $this->Username[$Index], $this->Password[$Index], $this->Database[$Index], $this->Port[$Index]);');
		if(!$this->ConnectionID[$Index] = $this->DBHandler[$Index]->Connect($this->NewLink[$Index])) {
			Debug("DB", "DB", "Failed to create database connection to ".$this->Database[$Index]." on ".$this->Host[$Index], __FILE__, __LINE__, DEBUG_SQL_ERR);
			$this->Error = "Failed to create database connection";
			$RV = FALSE;
		} else {
			Debug("DB", "DB", "Connected to database ".$this->Database[$Index]." on ".$this->Host[$Index], __FILE__, __LINE__, DEBUG_SQL_OK);
			if (array_key_exists('CheckSlaveStatus', $this->Options[$Index]) && $this->Options[$Index]['CheckSlaveStatus']) {
				// TODO: Check whether slave is up2date and running
				if (method_exists($this->DBHandler[$Index], 'CheckSlave') && $this->DBHandler[$Index]->CheckSlave()) {
					$RV = TRUE;
				} else {
					$RV = FALSE;
				}
			} else {
				$RV = TRUE;
			}
		}
		DebugCloseGroup(DEBUG_SQL_OK);

		return $RV;
	}

	/**
	 * Set DSN
	 *
	 * This function sets the class variables to the values matched by ParsedDSN().
	 * If the parsing failes this function will return FALSE and not set any class
	 * variables.
	 *
	 * @see	ParseDSN()
	 * @param	string	$ParsedDSN	Parsed DSN array to use for connection
	 * @return	boolean	Valid DSN string or not
	 * @access	private
	 */
	function SetDSN($DSN, $Index = 0)
	{
		if ($ParsedDSN = $this->ParseDSN($DSN)) {
			if (include_once("Nitro/NitroDB/".strtolower($ParsedDSN['DBType']).".inc.php")) {
				$this->DBType[$Index]		= strtolower($ParsedDSN['DBType']);
				$this->Username[$Index]	= $ParsedDSN['Username'];
				$this->Password[$Index]	= $ParsedDSN['Password'];
				$this->Host[$Index]			= $ParsedDSN['Host'];
				$this->Port[$Index]			= $ParsedDSN['Port'];
				$this->Database[$Index]	= $ParsedDSN['Database'];
				$this->Options[$Index]	= $ParsedDSN['Options'];
				return TRUE;
			} else {
				$this->Error = "Database type not supported";
				return FALSE;
			}
		} else {
			$this->Error = "DSN not valid";
			return FALSE;
		}
	}

	/**
	 * Parse DSN
	 *
	 * This function parses a valid DSN string and returns it in an associative array.
	 *
	 * @param	string	$DSN	DSN to use for connection (database_type://username:[password]@host/database_name)
	 * @return	mixed	Parsed DSN string in array or FALSE
	 * @access	public
	 */
	function ParseDSN($DSN)
	{
		$RV = Array();
		
		//TODO: better DSN parser with support for all options (i.e. ports etc.)
		if (preg_match("'([^:]+)://(([^:^@]+)?(:([^@]*))?@)?([^/^:]+)(:([0-9]+))?/([^\?]*)\??(.*)?'", $DSN, $match)) {
			$RV['DBType']			= strtolower($match[1]);
			$RV['Username']		= $match[3];
			$RV['Password']		= $match[5];
			$RV['Host']				= $match[6];
			$RV['Port']				= $match[8];
			$RV['Database']		= $match[9];
			$RV['Options']		= Array();
			if ($match[10]) {
				$tmp = explode('&', $match[10]);
				foreach($tmp AS $t) {
					$tmp2 = explode('=', $t);
					$RV['Options'][$tmp2[0]] = $tmp2[1];
				}
			}
			return $RV;
		} else {
			return FALSE;
		}
	}

	/**
	 * Query database
	 *
	 * This function calls the query function of the database handle (DBHandler)
	 * and returns a Result object.
	 *
	 * <CODE>
	 * $Result = $DBConnection->query('SELECT * FROM User WHERE UserID = 1');
	 * </CODE>
	 *
	 * @param	string	$Query	Query to run on database
	 * @param	int			$Limit	Limit number of rows returned
	 * @param	int			$Offset	Offset of rows limited by $Limit
	 * @param	string	$Mode		Mode of query (RO or RW)
	 * @param	int	$Index	Index of handler to use
	 * @return	object	Result object
	 */
	function query($Query, $Limit = 0, $Offset = 0, $Mode = FALSE, $Index = FALSE)
	{
		if ($Index === FALSE) {
			if ($Mode === FALSE) $Mode = (ereg('^SELECT', $Query) ? 'RO' : 'RW'); // Try to guess mode based on query
			$Indexes2Use	= $this->GetIndexes($Mode);
			$Index				= $this->GetIndex2Use($Indexes2Use, $this->BalanceMethod);
		}

		// TODO: build in failover on error on server
		if (DB::isDBHandler($this->DBHandler[$Index])) {
			$Result = $this->DBHandler[$Index]->query($Query, $Limit, $Offset);
			if ($Result === FALSE OR DB::isError($Result)) {
				Debug(__CLASS__, __FUNCTION__, "Query failed: ".mysql_error(), __FILE__, __LINE__, DEBUG_SQL_ERR);
				Debug(__CLASS__, __FUNCTION__, ereg_replace("[\r\n]+", ' ', $Query), __FILE__, __LINE__, DEBUG_SQL_ERR);
				//$this->Error();
			}
			Debug(__CLASS__, __FUNCTION__, "Query Done on ".$this->DBHandler[$Index]->Host, __FILE__, __LINE__, DEBUG_SQL_OK);

			return $Result;
		} else {
			$this->Error = "Not a valid DB Handler initalized";
			return FALSE;
		}
	}
	
	/**
	 * Retreive insert id of last insert
	 *
	 * This function returns the id of the row added by the last insert
	 * query done through the current DB handler. This must be called after the
	 * insert query itself.
	 *
	 * <CODE>
	 * $DBConnection->query("INSERT INTO User SET Name = 'FirstName LastName'");
	 * $UserID = $DBConnection->InsertID();
	 * </CODE>
	 *
	 * @return	int	Insert id
	 */
	function InsertID($Index = 0)
	{
		if (DB::isDBHandler($this->DBHandler[$Index])) {
			return $this->DBHandler[$Index]->InsertID();
		} else {
			$this->Error = "Not a valid DB Handler initalized";
			return FALSE;
		}
	}

	/**
	 * Prepare Query for usage in db handler
	 *
	 * This function will prepare the query given to comply with the
	 * SQL syntax of the DB handler compared to MySQL. All MySQL specifics
	 * will be changed to comply.
	 *
	 * @param		string	$Query	Query to prepare
	 * @param		int			$Index	Index of handler to use (default = 0)
	 * @return	string	$Query	Prepared query
	 */
	function PrepareQuery($Query, $Index = 0)
	{
		if (DB::isDBHandler($this->DBHandler[$Index])) {
			return $this->DBHandler[$Index]->PrepareQuery($Query);
		} else {
			$this->Error = "Not a valid DB Handler initalized";
			return FALSE;
		}
	}

	/**
	 * Get exactly one value from the database
	 *
	 * This function queries the database and returns a single value. This value is by
	 * default the value in the first column of the first row. It is also possible to
	 * retreive a value from a different row or column by using the arguments.
	 *
	 * <CODE>
	 * $UserName = $DBConnection->getOne('SELECT Name FROM User WHERE UserID = 1');
	 * // Get name of 5th user found
	 * $UserName = $DBConnection->getOne('SELECT Name FROM User', 5);
	 * // Get value of 3rd column of 5th user found
	 * $UserName = $DBConnection->getOne('SELECT * FROM User', 5, 3);
	 * </CODE>
	 *
	 * @param	string	$Query	Query to run on database
	 * @param	int	$row	Row of result set to return (default 1)
	 * @param	int	$col	Column of result set to return (default 1)
	 * @return	mixed	Value found in database or (boolean) FALSE on failure
	 */
	function getOne($Query, $row = 1, $col = 1)
	{
		DebugGroup("DB", "getOne", $Query, __FILE__, __LINE__, DEBUG_SQL_OK);
		$Result = $this->query($Query);
		if ($Result === FALSE OR DB::isError($Result)) {
			$RV = FALSE;
		} else {
			if ($Result->NumRows()) {
				$RV = (string)$Result->fetchResult($row - 1,$col - 1);
				Debug(__CLASS__, "getOne", "Result fetched for row $row and column $col", __FILE__, __LINE__, DEBUG_SQL_OK);
			} else {
				$RV = FALSE;
				Debug(__CLASS__, "getOne", "No result", __FILE__, __LINE__, DEBUG_SQL_NOROWS);
			}
		}
		if ($Result) $Result->free();

		DebugCloseGroup(DEBUG_SQL_OK);
		return $RV;
	}

	/**
	 * Get exactly one row from the database
	 *
	 * This function queries the database and returns a single row. This row is by
	 * default the first in the result. It is also possible to retreive a different row
	 * by using the row argument. It is also possible to control the type of array returned
	 * by using the FetchMode parameter.
	 *
	 * <CODE>
	 * $UserArray = $DBConnection->getRow('SELECT * FROM User WHERE UserID = 1');
	 * // Get 5th row
	 * $UserArray = $DBConnection->getRow('SELECT * FROM User WHERE UserID = 1', 5);
	 * // Get 5th row in an associative array (using column names)
	 * $UserArray = $DBConnection->getRow('SELECT * FROM User WHERE UserID = 1', 5, NITRODB_ASSOC);
	 * // Get 5th row in an indexed array
	 * $UserArray = $DBConnection->getRow('SELECT * FROM User WHERE UserID = 1', 5, NITRODB_NUM);
	 * // Get 5th row in an indexed and associative array (returns twice the amount of columns found!)
	 * $UserArray = $DBConnection->getRow('SELECT * FROM User WHERE UserID = 1', 5, NITRODB_BOTH);
	 * </CODE>
	 *
	 * @param	string	$Query	Query to run on database
	 * @param	int	$row	Row of result set to return (default 1)
	 * @param	int	$FetchMode	Fetchmode to use in returning the row (default NITRODB_ASSOC)
	 * @return	mixed	Array of columns in row found in database or (boolean) FALSE on failure
	 */
	function getRow($Query, $row = 1, $FetchMode = NITRODB_ASSOC)
	{
		DebugGroup("DB", "getRow", $Query, __FILE__, __LINE__, DEBUG_SQL_OK);
		$Result = $this->query($Query);
		if ($Result === FALSE OR DB::isError($Result)) {
			$RV = FALSE;
		} else {
			Debug(__CLASS__, "getRow", "Query Done", __FILE__, __LINE__, DEBUG_SQL_OK);
			$n = 1;
			$RV = FALSE;
			while($Row = $Result->fetchRow($FetchMode)) {
				if ($n == $row) {
					Debug(__CLASS__, "getRow", "Row $row fetched", __FILE__, __LINE__, DEBUG_SQL_OK);
					$RV = $Row;
					break;
				}
				$n++;
			}
		}
		if ($Result) $Result->free();

		DebugCloseGroup(DEBUG_SQL_OK);
		return $RV;
	}

	/**
	 * Get exactly one column from the database
	 *
	 * This function queries the database and returns a single column from multiple rows.
	 * This column is by default the first in the result. It is also possible to retreive
	 * a different column by using the column argument.
	 *
	 * <CODE>
	 * $AllUserNames = $DBConnection->getCol('SELECT Name FROM User');
	 * // Get the 3rd column
	 * $AllUserNames = $DBConnection->getCol('SELECT * FROM User', 3);
	 * </CODE>
	 *
	 * @param	string	$Query	Query to run on database
	 * @param	int	$col	Column of result set to return (default 1)
	 * @return	mixed	Array of column for rows found in database or (boolean) FALSE on failure
	 */
	function getCol($Query, $col = 1)
	{
		DebugGroup("DB", "getCol", $Query, __FILE__, __LINE__, DEBUG_SQL_OK);

		$Result = $this->query($Query);
		if ($Result === FALSE OR DB::isError($Result)) {
			$RV = FALSE;
		} else {
			$RV = Array();
			if (is_int($col)) {
				$FetchMode = NITRODB_NUM;
				$col = ($col > 0 ? $col - 1 : 0);
			} else {
				$FetchMode = NITRODB_ASSOC;
			}
			while($Row = $Result->fetchRow($FetchMode)) {
				$RV[] = $Row[$col];
			}
			Debug(__CLASS__, "getCol", "Column $col fetched", __FILE__, __LINE__, DEBUG_SQL_OK);
		}
		if ($Result) $Result->free();

		DebugCloseGroup(DEBUG_SQL_OK);
		return $RV;
	}

	/**
	 * Get function to use in SQL for inserting date/time automatically
	 *
	 * @return	string	Function for use in SQL
	 */
	function getDateTimeFunction()
	{
		if (DB::isDBHandler($this->DBHandler[0])) {
			return $this->DBHandler[0]->getDateTimeFunction();
		} else {
			$this->Error = "Not a valid DB Handler initalized";
			return FALSE;
		}
	}

	/**
	 * Coming soon
	 */
	function getFunction($Function, $Parameters = FALSE)
	{
		if ($Parameters !== FALSE && is_array($Parameters)) {
			$RV = $Function . "('" . implode("', '", array_values($Parameters)) . "')";
		} elseif($Parameters !== FALSE && strlen($Parameters)) {
			$RV = $Function . "('" . $this->escapeString($Parameters) . "')";
		} else {
			$RV = $Function . "()";
		}
		
		Debug(__CLASS__, __FUNCTION__, "DB::getFunction(" . $Function . ") with parameters: '" . (is_array($Parameters) ? implode("':'", array_values($Parameters)) : $Parameters) . "', result: " . $RV, __FILE__, __LINE__, DEBUG_SQL_OK);
		
		return $RV;
	}

	/** 
	 * Start a new transaction
	 *
	 * This function will use the database specific transaction commands to start
	 * a new transaction.
	 *
	 * @return	boolean	Transaction successfully started
	 */
	function TransactionStart()
	{
		return false; // no transaction support yet
	}
	
	/** 
	 * Commit transaction
	 *
	 * This function will use the database specific transaction commands to commit
	 * the transaction started.
	 *
	 * @return	boolean	Transaction successfully commited
	 */
	function TransactionCommit()
	{
		return false; // no transaction support yet
	}

	/** 
	 * Rollback transaction
	 *
	 * This function will use the database specific transaction commands to rollback
	 * the transaction started.
	 *
	 * @return	boolean	Transaction successfully rolled back
	 */
	function TransactionRollback()
	{
		return false; // no transaction support yet
	}

	/**
	 * List all tables in the database
	 *
	 * This function will return an array of the names of all the tables in the database
	 * connected to.
	 *
	 * <CODE>
	 * $Tables = $DBConnection->listTables();
	 * </CODE>
	 *
	 * Notice that this function caches the list of tables in the database for fast retrieval
	 * later. If you create a new table or delete a table and want to check the list again, set
	 * the $useCache variable to FALSE when calling the function.
	 *
	 * @param	boolean	$useCache	Use cache list if available
	 * @param	int	$Index	Index of handler to use
	 * @return	array	Array of table names in database
	 */
	function listTables($useCache = TRUE, $Index = FALSE)
	{
		if (isset($this->TableList) && $useCache === TRUE) {
			return $this->TableList;
		} else {
			if ($Index === FALSE) {
				$Mode = 'RO';
				$Indexes2Use	= $this->GetIndexes($Mode);
				$Index				= $this->GetIndex2Use($Indexes2Use, $this->BalanceMethod);
			}
	
			if (DB::isDBHandler($this->DBHandler[$Index])) {
				$tables = $this->DBHandler[$Index]->listTables();
				$this->TableList = $tables;
				return $tables;
			} else {
				$this->Error = "Not a valid DB Handler initalized";
				return FALSE;
			}
		}
	}

	/**
	 * List all fields of the given table in the database
	 *
	 * This function will return an array of all the fields in the given table in the
	 * database connected to. The returned array is an associative array with the name of
	 * the field as a key and the value is another associative array with the properties
	 * of the table.
	 *
	 * <CODE>
	 * $Fields = $DBConnection->listFields($Table);
	 * </CODE>
	 *
	 * Notice that this function caches the list of fields in the table for fast retrieval
	 * later. If you create a new field or delete a field and want to check the list again, set
	 * the $useCache variable to FALSE when calling the function.
	 *
	 * @param	string	$Table	Table name
	 * @param	boolean	$useCache	Use cache list if available
	 * @param	int	$Index	Index of handler to use
	 * @return	array	Array of fields in table
	 */
	function listFields($Table, $useCache = FALSE, $Index = FALSE)
	{
		if (isset($this->FieldList[$Table]) && $useCache === TRUE) {
			return $this->FieldList[$Table];
		} else {
			if ($Index === FALSE) {
				$Mode = 'RO';
				$Indexes2Use	= $this->GetIndexes($Mode);
				$Index				= $this->GetIndex2Use($Indexes2Use, $this->BalanceMethod);
			}
	
			if (DB::isDBHandler($this->DBHandler[$Index])) {
				$fields = $this->DBHandler[$Index]->listFields($Table);
				$this->FieldList[$Table] = $fields;
				return $fields;
			} else {
				$this->Error = "Not a valid DB Handler initalized";
				return FALSE;
			}
		}
	}

	/** 
	 * Escapes special characters in a string for use in a SQL statement
	 *
	 * This function will use the specific database escape funtion to escape the 
	 * string before use in a query.
	 *
	 * <CODE>
	 * $Query = "INSERT INTO Table SET Field = '".$DBConnection->escapeString("test string")."'";
	 * $DBConnection->query($Query);
	 * </CODE>
	 *
	 * @param	string	$String	String to escape
	 */
	function escapeString($String)
	{
		if (DB::isDBHandler($this->DBHandler[0])) {
			return $this->DBHandler[0]->escapeString($String);
		} else {
			$this->Error = "Not a valid DB Handler initalized";
			return FALSE;
		}
	}

	/**
	 * Check whether object is a database error
	 *
	 * This function can be used to check whether the result returned by
	 * the query function is an error (an erro occurred during query).
	 *
	 * <CODE>
	 * $Result = $DBConnection->query('SELECT * FROM User WHERE UserID = 1');
	 * if ($Result === FALSE OR DB::isError($Result)) {
	 *   echo 'ERROR: query failed.';
	 *   exit;
	 * }
	 * </CODE>
	 *
	 * @param	object	$value	Object to test for error
	 * @return	boolean	(TRUE|FALSE) for error
	 */
  function isError($value)
  {
      return (is_object($value) && $value->Error);
  }

	/**
	 * Check whether variable is a database connection
	 *
	 * This function can be used to check whether the given variable
	 * is a database connection.
	 *
	 * <CODE>
	 * if (!DB::isDBCon($DBConnection)) {
	 *   echo 'ERROR: Not a database connection.';
	 *   exit;
	 * }
	 * </CODE>
	 *
	 * @param	object	$value	Variable to test for connection
	 * @return	boolean	(TRUE|FALSE) for database connection
	 */
  function isDBCon($value)
  {
      return (is_object($value) && $value->ConnectionID && $value->DSN);
  }

	/**
	 * Check whether variable is a database handler
	 *
	 * This function can be used to check whether the given variable
	 * is a valid database handler
	 *
	 * <CODE>
	 * if (!DB::isDBHandler($DBHandler)) {
	 *   echo 'ERROR: Not a database handler.';
	 *   exit;
	 * }
	 * </CODE>
	 *
	 * @param	object	$value	Variable to test for hander
	 * @return	boolean	(TRUE|FALSE) for database handler
	 * @access	private
	 */
  function isDBHandler($value)
  {
      return (is_object($value) && $value->ConnectionID && $value->DBHandlerID);
  }

	/**
	 * Get all indexes in this object with specified MOde
	 *
	 * @param	string	$Mode	Mode of handler indexes to return (RO or RW)
	 * @return	array	Indexes with mode
	 * @access	public
	 */
	function GetIndexes($Mode = 'RO')
	{
		if ($Mode == 'RW') {
			// Read-write, can only be sent to RW servers!
			foreach($this->AllIndexes AS $id) {
				if ($this->Options[$id]['Mode'] == 'RW') $Indexes2Use[] = $id;
			}
		} else {
			// All servers may be used
			$Indexes2Use = $this->AllIndexes;
		}
		
		return $Indexes2Use;
	}

	/**
	 * Get index 2 use using BalanceMethod
	 *
	 * @param	array	$Indexes2Use	Array of indexes to use in balancing
	 * @param	string	$BalanceMethod	Method to use in balancing (Random, RandomFixed (default), RoundRobin, Load, Weighted)
	 */
	function GetIndex2Use($Indexes2Use, $BalanceMethod = 'RandomFixed')
	{
		if (count($Indexes2Use) > 1) {
			// Index not set, select connection handler for query
			// BalanceMethod: Random, RandomFixed (default), RoundRobin, Load, Weighted
			// Types: Slave, Master, MasterAndSlave (writes will always be sent to a master)
			switch($BalanceMethod) {
				case 'Backup':
					foreach($Indexes2Use AS $id) {
						if ($this->CheckServer($id)) {
							$Index = $id;
							break;
						}
					}
					break;
				case 'Random':
					for($i = 0; $i <= count($Indexes2Use); $i++) {
						mt_srand();
						$id = $Indexes2Use[(int)mt_rand(0, count($Indexes2Use) - 1)];
						if ($this->CheckServer($id)) {
							$Index = $id;
							break;
						} else {
							// pop Indexes2Use
							$newIndexes2Use = Array();
							foreach($Indexes2Use AS $id) {
								if ($id != $Index) {
									$newIndexes2Use[] = $id;
								}
							}
							$Indexes2Use = $newIndexes2Use;
						}
					}
					break;
				case 'Load': // Not implemented
				case 'Weighted': // Weighted Round Robin
				case 'WeightedRoundRobin': // Not implemented
				case 'RoundRobin':
					for($i = 0; $i <= count($Indexes2Use); $i++) {
						$idt = (int)(($this->LastIndex >= count($Indexes2Use) - 1) ? 0 : $this->LastIndex + 1);
						$id = $Indexes2Use[$idt];
						if ($this->CheckServer($id)) {
							$Index = $id;
							break;
						} else {
							$this->LastIndex++;
						}
					}
					$this->LastIndex = $idt;
					break;
				case 'RandomFixed':
				default:
					if ($this->LastIndex) {
						$Index = $this->LastIndex;
					} else {
						for($i = 0; $i <= count($Indexes2Use); $i++) {
							mt_srand();
							$id = $Indexes2Use[(int)mt_rand(0, count($Indexes2Use) - 1)];
							if ($this->CheckServer($id)) {
								$Index = $id;
								break;
							} else {
								// pop Indexes2Use
								$newIndexes2Use = Array();
								foreach($Indexes2Use AS $idt) {
									if ($id != $idt) {
										$newIndexes2Use[] = $id;
									}
								}
								$Indexes2Use = $newIndexes2Use;
							}
						}
					}
					break;
			}
		} elseif (count($Indexes2Use) == 1) {
			$Index = $Indexes2Use[0];
			if (!$this->CheckServer($Index)) {
				DB::RaiseFatalError($this);
			}
		} else {
			$Index = FALSE;
		}
		
		return $Index;
	}

	/**
	 * Check server connection and remove from AllIndexes if down
	 *
	 * @param	int	$Index	Index of server to check
	 * @return	boolean	True on server OK, False on server down
	 * @access	public
	 */
	function CheckServer($Index)
	{
		if ($this->ConnectionID[$Index] == -1) $this->Connect($Index);
		if ($this->DBHandler[$Index]->ConnectionID) {
			$RV = TRUE;
		} else {
			// Server seems down remove from AllIndexes!
			$this->RemoveHandler($Index);
			$RV = FALSE;
		}
		
		return $RV;
	}


	/**
	 * Raise database/query error
	 *
	 * This function looks in the ini file for the "nitrodb_email_on_query_error" and
	 * "log_on_query_error" parameters in the Debug section and emails to those
	 * addresses, respectively, logs to the given file, the error.
	 *
	 * <CODE>
	 * if ($SomethingWentWrong) {
	 *   DB::Error($DB['Nitro']);
	 * }
	 * 
	 * if ($SomethingWentWrong) {
	 *   $$DB['Nitro']->Error();
	 * }
	 * </CODE>
	 * 
	 * @param	object	$link	Database object
	 */
	function Error($link = FALSE)
	{
		global $__NitroConf;

		if (is_object($__NitroConf) && $__NitroConf->CONF && ($__NitroConf->CONF['Debug']['nitrodb_email_query_error'] OR $__NitroConf->CONF['Debug']['nitrodb_log_query_error'])) {
			if (!$link) $link =& $this;
			$error = '';
			if ($link->Error) $error.= "ERROR: ".$link->Error."\n";
			if (is_array($link->DBHandler)) {
				foreach($link->DBHandler AS $index => $handler) {
					if (DB::isDBHandler($handler)) {
						$error.= "Handler ($index): ".$link->DBHandler[0]->Error."\n";
					}
				}
			} else {
				$error.= "No handler found\n";
			}

			if ($error) {
				if ($__NitroConf->CONF['Debug']['nitrodb_email_query_error']) {
					$recipients = $__NitroConf->CONF['Debug']['nitrodb_email_query_error'];
					$subject = "Database error on ".$this->DSN;
					$body = $error;
					@mail($recipients, $subject, $body);
				}
				if ($__NitroConf->CONF['Debug']['nitrodb_log_query_error']) {
					if ($fp = @fopen($this->Conf['Debug']['nitrodb_log_query_error'], 'w')) {
						fwrite($fp, $error, strlen($error));
						fclose($fp);
					}
				}
			}
		}
	}

	/**
	 * Raise fatal database error
	 *
	 * This function searches for the database error files, displays them and
	 * exits. First it looks for a file called DBError.php in document root, then
	 * for a file called DBError.html in document root and lastly for DBError.html
	 * in the defaults/text directory of Nitro. If none of these exist, it will echo
	 * the error directly.
	 *
	 * <CODE>
	 * if ($SomethingWentWrong) {
	 *   DB::RaiseFatalError($DB['Nitro']);
	 * }
	 * </CODE>
	 * 
	 * @param	object	$link	Database object
	 */
	function RaiseFatalError($link)
	{
		if (file_exists($_SERVER["DOCUMENT_ROOT"]."/DBError.php")) {
			/* Include error page /DBError.php if error found and file exists */
			include $_SERVER["DOCUMENT_ROOT"]."/DBError.php";
		} elseif (file_exists($_SERVER["DOCUMENT_ROOT"]."/DBError.html")) {
			/* Include error page /DBError.html if error found and file exists */
			include $_SERVER["DOCUMENT_ROOT"]."/DBError.html";
		} elseif (file_exists(NITRO_PATH."/Defaults/Texts/DBError.html")) {
			/* Include error page Defaults/Texts/DBError.html if error found and file exists */
			include NITRO_PATH."Defaults/Texts/DBError.html";
		} else {
			echo "ERROR: ".$link->Error;
			if (is_array($link->DBHandler)) {
				foreach($link->DBHandler AS $index => $handler) {
					if (DB::isDBHandler($handler)) {
						echo " (Handler ($index): ".$handler->Error.")";
					}
				}
			} else {
				echo "No handler found";
			}
		}
		exit;
	}
}
?>
Return current item: OpenNitro