|
|
|
|
|
|
| |
This package allows you to switch easily from one db server to another one, from a db connection to another one, keeping the minimum objects necessary, with its factory. It allows a strong errors management thanks to several exception types. Its methods let you, in addition to usual db classes functionnalities, manage transactions, queries preparation, queries results sets limitation...
| <?php
/**
* class aDBException extends Exception
* @author johan <johan.barbier@gmail.com>
* @version 20070524
*/
class aDBException extends Exception {
/**
* Class in which Exception was caught
*
* @var string
*/
private $sCallerClass = null;
/**
* Method in which Exception was caught
*
* @var string
*/
private $sCallerFunc = null;
/**
* public function __construct
* Constructor.
*
* @param string $sMessage : Exception message
* @param string $sClass : Class in which Exception was caught
* @param string $sFunction : Method in which Exception was caught
*/
public function __construct ($sMessage, $iCode, $sClass = 'unknown', $sFunction = 'unknown') {
$this -> sCallerClass = $sClass;
$this -> sCallerFunc = $sFunction;
parent::__construct ($sMessage, $iCode);
}
/**
* public function __toString
* Display Exception message
*
* @return string
*/
public function __toString() {
return $this -> sCallerClass.' :: '.$this -> sCallerFunc.'() : ['.$this -> code.'] '.$this->message;
}
}
/**
* class aDBExceptionIllegalClass extends aDBException
* @author johan <johan.barbier@gmail.com>
* @version 20070524
*/
class aDBExceptionIllegalClass extends aDBException {
/**
* Class constants : Exception messages and codes
*
*/
const ILLEGAL_CLASS_NAME = '{CLASS} is not implemented';
const UNEXPECTED_INSTANCE_ERROR = 'Unexpected instance error';
const CODE_ILLEGAL_CLASS_NAME = 0;
const CODE_UNEXPECTED_INSTANCE_ERROR = 1;
}
/**
* class aDBExceptionTypesError extends aDBException
* @author johan <johan.barbier@gmail.com>
* @version 20070524
*/
class aDBExceptionTypesError extends aDBException {
/**
* Class constants : Exception messages and codes
*
*/
const MUST_BE_AN_ARRAY = '{PARAM} must be an array';
const MUST_BE_AN_INT = '{PARAM} must be an integer';
const CODE_MUST_BE_AN_ARRAY = 0;
const CODE_MUST_BE_AN_INT = 1;
}
/**
* class aDBExceptionInvalidClassCalls extends aDBException
* @author johan <johan.barbier@gmail.com>
* @version 20070524
*/
class aDBExceptionInvalidClassCalls extends aDBException {
/**
* Class constants : Exception messages and codes
*
*/
const NO_QUERY_TO_PREPARE = 'No query has been prepared';
const NEEDLE_NOT_FOUND = '{NEEDLE} was not found in prepared query {QUERY}';
const PARAM_TYPE_NOT_FOUND = 'Parameter type asked for query preparation does not exist';
const PROP_NOT_GETABLE = '{PROP} is not a getable property';
const INVALID_OPTION = '{OPTION} is not a valid option';
const CODE_NO_QUERY_TO_PREPARE = 0;
const CODE_NEEDLE_NOT_FOUND = 1;
const CODE_PARAM_TYPE_NOT_FOUND = 2;
const CODE_PROP_NOT_GETABLE = 3;
const CODE_INVALID_OPTION = 4;
}
/**
* class aDBExceptionDbConnectorErrors extends aDBException
* @author johan <johan.barbier@gmail.com>
* @version 20070524
*/
class aDBExceptionDbConnectorErrors extends aDBException {
/**
* Class constants : Exception messages and codes
*
*/
const CONNEXION_FAILED = 'Connexion failed with message [{MSG}]';
const QUERY_FAILED = 'Query [{QRY}] failed with message [{MSG}]';
const FETCH_FAILED = 'Fetch failed with message [{MSG}]';
const INVALID_SEEK_POSITION = 'Invalid seek position ({OFFSET})';
const INVALID_QRY_RESOURCE = 'Invalid query resource';
const REQUEST_ERROR = 'Undefined error in the request';
const CONNECTION_LINK_MISSING = 'No database connection found';
const COULD_NOT_FREE_RESULT = 'Free result failed with message [{MSG}]';
const CODE_CONNEXION_FAILED = 0;
const CODE_QUERY_FAILED = 1;
const CODE_FETCH_FAILED = 2;
const CODE_INVALID_SEEK_POSITION = 3;
const CODE_INVALID_QRY_RESOURCE = 4;
const CODE_REQUEST_ERROR = 5;
const CODE_CONNECTION_LINK_MISSING = 6;
const CODE_COULD_NOT_FREE_RESULT = 7;
}
/**
* class aDBFactory
* @author johan <johan.barbier@gmail.com>
* @version 20070524
*/
class aDBFactory {
/**
* static property : array of aDB instances
*
* @var array of aDB object
*/
private static $_instance;
/**
* public static getInstance
* Factory.
* Checks that the requested DB type is implemented.
* Then, checks if there is an existing instance of it. If not, creates it with configuration and options if any.
* If yes, gets it! Checks if there is a new configuration to apply and apply it if so. Same story for the options.
* Returns the instance...
*
* @param string $saDBType : name of the requested aDB class
* @param array $aConConf : array of aDB connection configuration
* @param unknown_type $aOptions : array of aDB options
* @return aDB object : the requested aDB instance
*/
public static function getInstance ($saDBType, $aConConf = null, $aOptions = null) {
if (!class_exists($saDBType)) {
throw new aDBExceptionIllegalClass (str_replace ('{CLASS}', $saDBType, aDBExceptionIllegalClass::ILLEGAL_CLASS_NAME), aDBExceptionIllegalClass::CODE_ILLEGAL_CLASS_NAME, get_class($this), __FUNCTION__);
}
if (isset (self::$_instance[$saDBType])) {
if (!self::$_instance[$saDBType] instanceof $saDBType) {
throw new aDBExceptionIllegalClass (aDBExceptionIllegalClass::UNEXPECTED_INSTANCE_ERROR, aDBExceptionIllegalClass::CODE_UNEXPECTED_INSTANCE_ERROR, get_class($this), __FUNCTION__);
} else {
if (!is_null ($aConConf)) {
self::$_instance[$saDBType] -> connect ($aConConf);
}
if (!is_null ($aOptions)) {
foreach ($aOptions as $sOption => $mValue) {
self::$_instance[$saDBType] -> setOption ($sOption, $mValue);
}
}
return self::$_instance[$saDBType];
}
} else {
self::$_instance[$saDBType] = new $saDBType ($aConConf, $aOptions);
return self::$_instance[$saDBType];
}
}
}
/**
* class sqlIterator implements Iterator, SeekableIterator, Countable
* @author johan <johan.barbier@gmail.com>
* @version 20070524
*/
class sqlIterator implements Iterator, SeekableIterator, Countable {
/**
* Number of items to retrieve
*
* @var integer
*/
private $iCount;
/**
* Starting offset
*
* @var integer
*/
private $iOffset = 0;
/**
* Total number of items for the request
*
* @var integer
*/
private $iMax;
/**
* Current internal position
* @var integer
*/
private $iPos = -1;
/**
* aDB object
*
* @var aDB
*/
private $aDB;
/**
* Query resource
*
* @var resource
*/
private $rQry;
/**
* fetch mode
*
* @var aDB class constant
*/
private $aDB_MODE;
private $keptaDB_MODE;
/**
* Output
*
* @var mixed
*/
private $mOutput = false;
/**
* public function __construct
* Constructor
* set some parameters
*
* @param aDB $aDB : aDB object
* @param resource $rQry : query resource
* @param aDB class constant $aDB_MODE : fetch mode
* @param integer $iOffset : starting offset
* @param integer $iCount : fetch length
* @param boolean $bIsSeekable : resource must be rewinded or not
*/
public function __construct (aDB $aDB, $rQry, $aDB_MODE = aDB::BOTH, $iOffset = 0, $iCount = null, $bIsSeekable = true) {
if (!is_int($iOffset)) {
$this -> aDB -> interceptException ('aDBExceptionTypesError', str_replace ('{PARAM}', '4d parameter', aDBExceptionTypesError::MUST_BE_AN_INT), aDBExceptionTypesError::CODE_MUST_BE_AN_INT, get_class($this), __FUNCTION__);
$iOffset = 0;
}
if (!is_int($iCount) && !is_null ($iCount)) {
$this -> aDB -> interceptException ('aDBExceptionTypesError', str_replace ('{PARAM}', '5d parameter', aDBExceptionTypesError::MUST_BE_AN_INT), aDBExceptionTypesError::CODE_MUST_BE_AN_INT, get_class($this), __FUNCTION__);
$iCount = null;
}
$this -> keptaDB_MODE = $aDB_MODE;
$this -> aDB_MODE = (($iMode = aDB::FETCH_GROUP^$aDB_MODE) !== 0 && $iMode !== $aDB_MODE && $iMode < $aDB_MODE)?$iMode:$aDB_MODE;
$this -> rQry = $rQry;
$this -> aDB = $aDB;
$this -> iOffset = $iOffset;
$this -> iCount = $iCount;
$this -> iMax = $this -> count ();
if (true === $bIsSeekable && $this -> aDB_MODE !== aDB::FETCH_EXTRACT) {
$this -> rewind ();
}
}
/**
* publid function current
* returns current result set
*
* @return array
*/
public function current () {
if ($this -> aDB_MODE === aDB::FETCH_EXTRACT) {
if (is_array ($this -> mOutput)) {
foreach ($this -> mOutput as $sK => $sV) {
global $$sK;
$$sK = $sV;
}
}
return true;
} else {
switch ($this -> keptaDB_MODE) {
case aDB::FETCH_GROUP:
case aDB::FETCH_GROUP|aDB::BOTH:
$mFirst = array_shift ($this -> mOutput);
array_shift ($this -> mOutput);
return array ($mFirst => $this -> mOutput);
break;
case aDB::FETCH_GROUP|aDB::FETCH_ASSOC:
case aDB::FETCH_GROUP|aDB::FETCH_NUM:
$mFirst = array_shift ($this -> mOutput);
return array ($mFirst => $this -> mOutput);
break;
default:
return $this -> mOutput;
break;
}
}
}
/**
* public function next
* goes to the next result set
*
*/
public function next () {
$this -> iPos ++;
if ($this -> aDB_MODE === aDB::FETCH_EXTRACT) {
$this -> mOutput = $this -> aDB -> __fetch ($this -> rQry, aDB::FETCH_ASSOC);
} else {
$this -> mOutput = $this -> aDB -> __fetch ($this -> rQry, $this -> aDB_MODE);
}
}
/**
* public function valid
* Checks the validity of the current position
*
* @return boolean
*/
public function valid () {
if (($this -> iOffset + $this -> iPos) >= $this -> iMax) {
return false;
}
if (!is_null ($this -> iCount) && $this -> iCount > 0 && $this -> iPos >= $this -> iCount) {
return false;
}
if (false === $this -> mOutput) {
$this -> next ();
}
return true;
}
/**
* public function rewind
* moves the offset to the first position
*
*/
public function rewind () {
$this -> seek ($this -> iOffset);
}
/**
* public function seek
* seeks a given offset
*
* @param integer $iOffset
*/
public function seek ($iOffset) {
if (false === $this -> aDB -> __seek ($iOffset)) {
$this -> aDB -> interceptException ('aDBExceptionDbConnectorErrors', str_replace ('{OFFSET}', $iOffset, aDBExceptionDbConnectorErrors::INVALID_SEEK_POSITION), aDBExceptionDbConnectorErrors::CODE_INVALID_SEEK_POSITION, get_class($this), __FUNCTION__);
return false;
}
$this -> iOffset = $iOffset;
$this -> iPos = -1;
return true;
}
/**
* public function key
* Returns the current internal position
*
* @return integer
*/
public function key () {
return $this -> iPos;
}
/**
* public function count
* count the total number of result sets
*
* @return integer
*/
public function count () {
return $this -> aDB -> count ($this -> rQry);
}
/**
* public function getOffset
* Returns the current request position
*
* @return integer
*/
public function getOffset () {
return $this -> iPos + $this -> iOffset;
}
}
/**
* abstract class aDB
* @author johan <johan.barbier@gmail.com>
* @version 20070521
*/
abstract class aDB {
/**
* fetch modes
*
*/
const BOTH = 0;
const FETCH_ASSOC = 1;
const FETCH_NUM = 2;
const FETCH_GROUP = 4;
const FETCH_EXTRACT = 8;
/**
* Allowed parameter bindings
*
*/
const PARAM_STR = 1000;
const PARAM_INT = 1001;
/**
* DB Configuration
*
* @var array
*/
protected $aConConf = array (
'HOST' => '', 'LOGIN' => '', 'PWD' => '', 'DB' => '');
/**
* aDB options
*
* @var array
*/
protected $aOptions = array (
'AUTOCONNECT' => true,
'EXCEPTION_ON_ERROR' => true
);
/**
* Fetch length
*
* @var integer
*/
protected $iFetchLength;
/**
* Starting offset
*
* @var integer
*/
protected $iOffset = 0;
/**
* DB connection link resource
*
* @var resource
*/
protected $rLink;
/**
* Query
*
* @var string
*/
protected $sQuery;
/**
* Errors log when no exception
*
* @var array
*/
protected static $aErrorLog;
/**
* public function __construct
* Constructor.
* Automatic connection if AUTOCONNECT option is true
*
* @param array $aConConf
* @param array $aOptions
*/
public function __construct ($aConConf = null, $aOptions = null) {
if (is_array ($aOptions)) {
foreach ($aOptions as $sK => $sV) {
if (isset ($this -> aOptions[$sK])) {
$this -> aOptions[$sK] = $sV;
}
}
}
if (true === $this -> aOptions['AUTOCONNECT'] && !is_null ($aConConf)) {
$this -> connect ($aConConf);
}
}
/**
* public function set_limit
* Sets a limit to the query : starting offset and fetch length
* Is this method is called without any parameter, it will cancel previous set_limit call (useful if you used a limitation, and then do not want any more limitation)
*
* @param integer $iOffset
* @param integer $iCount
*/
public function set_limit ($iOffset = 0, $iCount = null) {
if (!is_int($iOffset)) {
$this -> interceptException ('aDBExceptionTypesError', str_replace ('{PARAM}', '1st parameter', aDBExceptionTypesError::MUST_BE_AN_INT), aDBExceptionTypesError::CODE_MUST_BE_AN_INT, get_class($this), __FUNCTION__);
return false;
}
if (!is_int($iCount) && !is_null ($iCount)) {
$this -> interceptException ('aDBExceptionTypesError', str_replace ('{PARAM}', '2d parameter', aDBExceptionTypesError::MUST_BE_AN_INT), aDBExceptionTypesError::CODE_MUST_BE_AN_INT, get_class($this), __FUNCTION__);
return false;
}
$this -> iFetchLength = $iCount;
$this -> iOffset = $iOffset;
return true;
}
/**
* public function next_limit
* Update the limit : aDB::iFetchLength remains the same, but aDB::iOffset is incremennted with aDB::iFetchLength.
* So, the offset is positionned on the very next result set.
*/
public function next_limit () {
if (!is_int($this -> iOffset)) {
$this -> interceptException ('aDBExceptionTypesError', str_replace ('{PARAM}', '1st parameter', aDBExceptionTypesError::MUST_BE_AN_INT), aDBExceptionTypesError::CODE_MUST_BE_AN_INT, get_class($this), __FUNCTION__);
return false;
}
if (!is_int($this -> iFetchLength) && !is_null ($this -> iFetchLength)) {
$this -> interceptException ('aDBExceptionTypesError', str_replace ('{PARAM}', '2d parameter', aDBExceptionTypesError::MUST_BE_AN_INT), aDBExceptionTypesError::CODE_MUST_BE_AN_INT, get_class($this), __FUNCTION__);
return false;
}
$this -> iOffset += $this -> iFetchLength;
return true;
}
/**
* public function connect
* Connects to the DB server
* If DB NAME has been set in the configuration, automatically selects it.
*
*/
public function connect ($aConConf) {
if (is_array ($aConConf)) {
foreach ($aConConf as $sK => $sV) {
if (isset ($this -> aConConf[$sK])) {
$this -> aConConf[$sK] = $sV;
}
}
} else {
$this -> interceptException ('aDBExceptionTypesError', str_replace ('{PARAM}', '1st parameter', aDBExceptionTypesError::MUST_BE_AN_ARRAY), aDBExceptionTypesError::CODE_MUST_BE_AN_ARRAY, get_class($this), __FUNCTION__);
return false;
}
if (false === ($this -> rLink = $this -> _connect ())) {
$this -> interceptException ('aDBExceptionDbConnectorErrors', str_replace ('{MSG}', $this -> _errorMsg (), aDBExceptionDbConnectorErrors::CONNEXION_FAILED), aDBExceptionDbConnectorErrors::CODE_CONNEXION_FAILED, get_class($this), __FUNCTION__);
return false;
}
if (!empty ($this -> aConConf['DB'])) {
$this -> select_db ($this -> aConConf['DB']);
}
return true;
}
/**
* public function select_db
* Selects a db
*
* @param string $sDbName
*/
public function select_db ($sDbName = null) {
if (!isset ($this -> rLink)) {
$this -> interceptException ('aDBExceptionDbConnectorErrors', aDBExceptionDbConnectorErrors::CONNECTION_LINK_MISSING, aDBExceptionDbConnectorErrors::CODE_CONNECTION_LINK_MISSING, get_class($this), __FUNCTION__);
return false;
}
if (!is_null ($sDbName)) {
$this -> aConConf['DB'] = $sDbName;
}
if (false === $this -> _select_db ($this -> aConConf['DB'])) {
$this -> interceptException ('aDBExceptionDbConnectorErrors', str_replace ('{MSG}', $this -> _errorMsg (), aDBExceptionDbConnectorErrors::CONNEXION_FAILED), aDBExceptionDbConnectorErrors::CODE_CONNEXION_FAILED, get_class($this), __FUNCTION__);
return false;
}
return true;
}
/**
* public function query
* Queries the DB server
*
* @param string $sQuery : query string
* @param boolean $bOverWriteQry : is sets to true, overwrites aDB::sQuery property; if not, does nothing
* @return resource : query resource
*/
public function query ($sQuery, $bOverWriteQry = true) {
if (!isset ($this -> rLink)) {
$this -> interceptException ('aDBExceptionDbConnectorErrors', aDBExceptionDbConnectorErrors::CONNECTION_LINK_MISSING, aDBExceptionDbConnectorErrors::CODE_CONNECTION_LINK_MISSING, get_class($this), __FUNCTION__);
return false;
}
if (false === ($rRes = @$this -> _query ($sQuery))) {
$this -> interceptException ('aDBExceptionDbConnectorErrors', str_replace (array ('{QRY}', '{MSG}'), array ($sQuery, $this -> _errorMsg ()), aDBExceptionDbConnectorErrors::QUERY_FAILED), aDBExceptionDbConnectorErrors::CODE_QUERY_FAILED, get_class($this), __FUNCTION__);
return false;
}
if (true === $bOverWriteQry) {
$this -> rQry = $rRes;
}
return $rRes;
}
/**
* public function fetch
* using current query resource or given query resource, instanciates an iterator to move through result sets
*
* @param aDB Class Constant $aDB_MODE : fetch mode
* @param resource $rQry : query resource
* @param boolean $bIsSeekable : if sets to true, query is seekable
* @return sqlIterator : the Iterator
*/
public function fetch ($aDB_MODE = aDB::BOTH, $rQry = null, $bIsSeekable = true) {
if (is_null ($rQry) && is_null ($this -> rQry)) {
$this -> interceptException ('aDBExceptionDbConnectorErrors', aDBExceptionDbConnectorErrors::INVALID_QRY_RESOURCE, aDBExceptionDbConnectorErrors::CODE_INVALID_QRY_RESOURCE, get_class($this), __FUNCTION__);
return false;
}
if (!is_null ($rQry)) {
return new sqlIterator ($this, $rQry, $aDB_MODE, $this -> iOffset, $this -> iFetchLength, $bIsSeekable);
} else {
return new sqlIterator ($this, $this -> rQry, $aDB_MODE, $this -> iOffset, $this -> iFetchLength, $bIsSeekable);
}
}
/**
* public function fetchColumn
* using current query resource or given query resource, returns the value of a given column
*
* @param integer $iColumn : column number
* @param resource $rQry : query resource
* @param boolean $bIsSeekable : if sets to true, query is seekable
* @return mixed : column value
*/
public function fetchColumn ( $iColumn = 0, $rQry = null, $bIsSeekable = true) {
if (!is_int($iColumn)) {
$this -> interceptException ('aDBExceptionTypesError', str_replace ('{PARAM}', '2d parameter', aDBExceptionTypesError::MUST_BE_AN_INT), aDBExceptionTypesError::CODE_MUST_BE_AN_INT, get_class($this), __FUNCTION__);
return false;
}
if (is_null ( |
| |