begin process at 2012 02 15 20:40:14
  Trouver un code source :
 
dans
 
Accueil > 

Code

 > 

Base de données

 > [PHP5] ABSTRACTION BDD STYLE PDO AVEC ITÉRATEURS, TRANSACTIONS

[PHP5] ABSTRACTION BDD STYLE PDO AVEC ITÉRATEURS, TRANSACTIONS


 Information sur la source

Note :
10 / 10 - par 4 personnes
10,00 / 10

  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10
Catégorie :Base de données Classé sous :abstraction, bdd, itérateurs, pdo, database Niveau :Expert Date de création :23/05/2007 Date de mise à jour :16/02/2008 14:06:27 Vu / téléchargé :10 558 / 327

Auteur : malalam

Ecrire un message privé
Site perso
Commentaire sur cette source (39)
Ajouter un commentaire et/ou une note

 Description

Bon, une énième couche d'abstraction de bdd.
J'ai finalement implémenté une classe d'usinage qui permet d'utiliser 1 instance de chaque type et d'en changer au besoin la connexion (1 sule instance de mysql se connectant à 2 serveurs mysql distincts, par exemple, et 1 instance de mssql).
Cette couche fonctionne comme les autres (une classe abstraite générique, et des classes enfants spécialisées sur un serveur de bdd). Mais j'ai tenté de calquer ses fonctionnalités sur celles de PDO (celles que j'utilise le plus en tous cas, et les plus simples à implémlenter).
Elle gère donc la préparation de requêtes, les transactions, permet de lier des valeurs à des chaînes (bindValue), possède différents modes de récupération des données (notamment aDB::FETCH_GROUP qui regroupe les données de cette forme: si je vais chercher, dans l'ordre, user_id et user_nom, j'aurais un tableau de la forme array (ID => array (NOM), ou aDB::FETCH_EXTRACT qui renverra dans ce même cas des variables $user_id et $user_nom, pour chaque jeu de résultat retourné).
3 méthodes distinctes de récupération des résultats d'une requête :
aDB::fetch() renvoie un itérateur (donc, on fait un foreach dessus, ou un while()). Cette méthode permet l'utilisation de aDB::FETCH_EXTRACT mais pas de aDB::FETCH_GROUP.
aDB::fetchAll() : renvoie un tableau avec tous les jeux de résultats. cette méthode permetr l'utilisation de aDB::FETCH_GROUP mais pas de aDB::FETCH_EXTRACT.
Les deux fonctionnent aussi en mode aDB::BOTH,  aDB::FETCH_NUM ou aDB::FETCH_ASSOC.
aDB::fetchColumn() : renvoie directement la valeur d'une colonne donnée.

En plus des autres méthodes habituelles (aDB::lastInsertId (), aDB::count() et cie...), possède aussi en interne une méthode pour limiter la récupération des jeux de résultat (la clause LIMIT de mysql, ou TOP de mssql...mais sans les utiliser ;-) : aDB::set_limit().
Cette méthode prend un offset de départ (une position de départ) et une longueur (le nombre de lignes à récupérer à partie de la position de départ).
Il faut penser à rappeler cette méthode sans paramètre pour remettre les paramètres par défaut (à savoir une position de départ à 0, et une longueur nulle : donc tout récupérer à partir du départ).

Voilà...il y a un exemple dans le fichier, nécessitant la création d'une toute plusieurs petites bases avec une table, mais bon.
Exemples de multi instances, d'utilisation des différents types d'exceptions, etc...
Testé avec mssql et mssql.


Source

  • <?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() {
  • $sMsg = '<strong>'.$this -> sCallerClass.'::'.$this -> sCallerFunc.'()</strong><br />';
  • $sMsg .= $this->getMessage().' ['.$this->getCode().']<br />';
  • $sMsg .= '<em>File</em> : '.$this->getFile().' on line '.$this->getLine().'<br />';
  • $sMsg .= '<em>Trace</em> :<br />'.$this->getTraceAsString().'<br />';
  • return $sMsg;
  • }
  • }
  • /**
  • * 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 MUST_BE_A_STRING = '{PARAM} must be a string';
  • const MUST_BE_A_BOOL = '{PARAM} must be a boolean';
  • const MUST_BE_A_SCALAR = '{PARAM} must be a scalar value';
  • const CODE_MUST_BE_AN_ARRAY = 0;
  • const CODE_MUST_BE_AN_INT = 1;
  • const CODE_MUST_BE_A_STRING = 2;
  • const CODE_MUST_BE_A_BOOL = 3;
  • const CODE_MUST_BE_A_SCALAR = 4;
  • }
  • /**
  • * 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 UNSUPPORTED_METHOD = 'This method is not yet supported by this db type';
  • const NO_LINK_FOUND_AND_NO_CONFIGURATION_FOUND = 'You try to use an instance which has been destroyed previously';
  • const NO_PROC_STMT = 'No statement resource found';
  • 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;
  • const CODE_UNSUPPORTED_METHOD = 5;
  • const CODE_NO_LINK_FOUND_AND_NO_CONFIGURATION_FOUND = 6;
  • const CODE_NO_PROC_STMT = 7;
  • }
  • /**
  • * 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 = 'Freeing result failed with message [{MSG}]';
  • const COULD_NOT_FREE_STMT = 'Freeing statement failed with message [{MSG}]';
  • const PROC_FAILED = 'Procedure [{QRY}] 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;
  • const CODE_COULD_NOT_FREE_STMT = 8;
  • const CODE_PROC_FAILED = 9;
  • }
  • /**
  • * 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 $_aInstance;
  • private static $_aInstances;
  • /**
  • * 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...
  • * NEW : if $mIsSingleton is left to true, behaviour is as described above.
  • * If it is false, a new instance is automatically created and stored.
  • * If it is a set to something else (string for example), it is used to store the instance under this name, if it does not already exists, or returns it if it does (must be the same DB type, too).
  • *
  • * @param string $saDBType : name of the requested aDB class
  • * @param array $aConConf : array of aDB connection configuration
  • * @param mixed $mIsSingleton : true if automatic singleton, false if no singleton, scalar value meaning that the instance is in a namespace : singleton on that namespace
  • * @param unknown_type $aOptions : array of aDB options
  • * @return aDB object : the requested aDB instance
  • */
  • public static function getInstance ($saDBType, $aConConf = null, $mIsSingleton = true, $bOpenNewConnection = true, $aOptions = null) {
  • if(!is_bool($bOpenNewConnection)) {
  • throw new aDBExceptionTypesError (str_replace('{PARAM}', '$bOpenNewConnection', aDBExceptionTypesError::MUST_BE_A_BOOL), aDBExceptionTypesError::CODE_MUST_BE_A_BOOL, get_class($this), __FUNCTION__);
  • }
  • 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(true === $mIsSingleton) {
  • 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, $bOpenNewConnection);
  • }
  • 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];
  • }
  • } elseif(false === $mIsSingleton) {
  • $iInstance = self::$_aInstances[$saDBType][] = new $saDBType ($aConConf, $aOptions);
  • return $iInstance;
  • } else {
  • if(!is_scalar($mIsSingleton)) {
  • throw new aDBExceptionTypesError (str_replace('{PARAM}', '$mIsSingleton', aDBExceptionTypesError::MUST_BE_A_SCALAR), aDBExceptionTypesError::CODE_MUST_BE_A_SCALAR, get_class($this), __FUNCTION__);
  • }
  • if(!isset(self::$_aInstance[$saDBType][$mIsSingleton]) || !self::$_aInstance[$saDBType][$mIsSingleton] instanceof $saDBType) {
  • self::$_aInstance[$saDBType][$mIsSingleton] = new $saDBType ($aConConf, $aOptions, $mIsSingleton);
  • } else {
  • if (!is_null ($aConConf)) {
  • self::$_aInstance[$saDBType][$mIsSingleton] -> connect($aConConf, $bOpenNewConnection);
  • }
  • if (!is_null ($aOptions)) {
  • foreach ($aOptions as $sOption => $mValue) {
  • self::$_aInstance[$saDBType][$mIsSingleton] -> setOption ($sOption, $mValue);
  • }
  • }
  • }
  • return self::$_aInstance[$saDBType][$mIsSingleton];
  • }
  • }
  • }
  • /**
  • * class sqlIterator implements Iterator, SeekableIterator, Countable
  • * @author johan <johan.barbier@gmail.com>
  • * @version 20070524
  • */
  • class sqlIterator implements Iterator, SeekableIterator {
  • /**
  • * 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;
  • if ((aDB::FETCH_GROUP^$aDB_MODE) !== 0 && (aDB::FETCH_GROUP^$aDB_MODE) !== $aDB_MODE && (aDB::FETCH_GROUP^$aDB_MODE) < $aDB_MODE) {
  • $this -> aDB_MODE = aDB::FETCH_GROUP^$aDB_MODE;
  • } elseif ((aDB::FETCH_GROUP_UNIQUE^$aDB_MODE) !== 0 && (aDB::FETCH_GROUP_UNIQUE^$aDB_MODE) !== $aDB_MODE && (aDB::FETCH_GROUP_UNIQUE^$aDB_MODE) < $aDB_MODE) {
  • $this -> aDB_MODE = aDB::FETCH_GROUP_UNIQUE^$aDB_MODE;
  • } else {
  • $this -> aDB_MODE = $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:
  • case aDB::FETCH_GROUP_UNIQUE|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:
  • case aDB::FETCH_GROUP_UNIQUE|aDB::FETCH_ASSOC:
  • case aDB::FETCH_GROUP_UNIQUE|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);
  • }
  • if(false === $this -> mOutput) {
  • if(false !== $this->aDB->nextResult($this->rQry)) {
  • $this->iMax = $this->count();
  • $this->rewind();
  • }
  • }
  • }
  • /**
  • * 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;
  • const FETCH_GROUP_UNIQUE = 16;
  • /**
  • * Allowed parameter bindings
  • *
  • */
  • const PARAM_STR = 1000;
  • const PARAM_INT = 1001;
  • const PARAM_FLOAT = 1002;
  • const PARAM_BOOL = 1003;
  • /**
  • * 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;
  • /**
  • * DB query link resource
  • *
  • * @var resource
  • */
  • public $rQry;
  • /**
  • * Statement link resource
  • *
  • * @var resource
  • */
  • protected $rStmtLink;
  • /**
  • * Query
  • *
  • * @var string
  • */
  • protected $sQuery;
  • /**
  • * Errors log when no exception
  • *
  • * @var array
  • */
  • protected static $aErrorLog;
  • /**
  • * NameSpace if any
  • *
  • * @var string
  • */
  • protected $sNameSpace = null;
  • /**
  • * public function __construct
  • * Constructor.
  • * Automatic connection if AUTOCONNECT option is true
  • *
  • * @param array $aConConf
  • * @param array $aOptions
  • */
  • public function __construct ($aConConf = null, $aOptions = null, $sNameSpace = null) {
  • $this->sNameSpace = $sNameSpace;
  • if (is_array ($aOptions)) {
  • foreach ($aOptions as $sK => $sV) {
  • if (isset ($this -> aOptions[$sK])) {
  • $this -> aOptions[$sK] = $sV;
  • }
  • }
  • }
  • if(is_null ($aConConf) && is_null($this->rLink)) {
  • $this -> interceptException ('aDBExceptionInvalidClassCalls', aDBExceptionInvalidClassCalls::NO_LINK_FOUND_AND_NO_CONFIGURATION_FOUND, aDBExceptionInvalidClassCalls::CODE_NO_LINK_FOUND_AND_NO_CONFIGURATION_FOUND, get_class($this), __FUNCTION__);
  • }
  • if ((true === $this -> aOptions['AUTOCONNECT'] && !is_null ($aConConf)) || is_null($this->rLink)) {
  • $this -> connect ($aConConf);
  • }
  • }
  • /**
  • * public function __destruct
  • * Destructor.
  • * AClose connection
  • *
  • * @@return boolean true on success, false if not
  • */
  • public function __destruct() {
  • return @$this->close();
  • }
  • /**
  • * 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, $bOpenNewConnection = true) {
  • 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(is_null($this->rLink) || true === $bOpenNewConnection) {
  • if (false === ($this -> rLink = $this -> _connect ($bOpenNewConnection))) {
  • $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 (true === $bOverWriteQry) {
  • if(!is_null($this->rQry)) {
  • $this->freeResult();
  • }
  • }
  • 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 ($rQry) && is_null ($this -> rQry) && is_null($this->rProcStmt)) {
  • $this -> interceptException ('aDBExceptionDbConnectorErrors', aDBExceptionDbConnectorErrors::INVALID_QRY_RESOURCE, aDBExceptionDbConnectorErrors::CODE_INVALID_QRY_RESOURCE, get_class($this), __FUNCTION__);
  • return false;
  • }
  • if (!is_null ($rQry)) {
  • $it = new sqlIterator ($this, $rQry, aDB::FETCH_NUM, $this -> iOffset, null, $bIsSeekable);
  • } else {
  • $it = new sqlIterator ($this, $this -> rQry, aDB::FETCH_NUM, $this -> iOffset, null, $bIsSeekable);
  • }
  • $it -> next ();
  • $aRes = $it -> current ();
  • if (empty ($aRes) || !isset ($aRes[$iColumn])) {
  • return false;
  • }
  • return $aRes[$iColumn];
  • }
  • /**
  • * public function fetchAll
  • * using current query resource or given query resource, returns an array with all the result sets.
  • * The array's structure depends on the fetch mode.
  • *
  • * @param aDB Class Constant $aDB_MODE
  • * @param resource $rQry
  • * @param boolean $bIsSeekable : if sets to true, query is seekable
  • * @return array
  • */
  • public function fetchAll ($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;
  • }
  • $aRes = array ();
  • if (!is_null ($rQry)) {
  • $it = new sqlIterator ($this, $rQry, $aDB_MODE, $this -> iOffset, $this -> iFetchLength, $bIsSeekable);
  • } else {
  • $it = new sqlIterator ($this, $this -> rQry, $aDB_MODE, $this -> iOffset, $this -> iFetchLength, $bIsSeekable);
  • }
  • $it -> next ();
  • while ($it -> valid ()) {
  • switch ($aDB_MODE) {
  • case aDB::FETCH_GROUP_UNIQUE:
  • case aDB::FETCH_GROUP_UNIQUE|aDB::BOTH:
  • case aDB::FETCH_GROUP_UNIQUE|aDB::FETCH_ASSOC:
  • case aDB::FETCH_GROUP_UNIQUE|aDB::FETCH_NUM:
  • $aTmp = $it -> current ();
  • $Ik = key($aTmp);
  • $aRes[$Ik] = $aTmp[$Ik];
  • break;
  • default :
  • $aRes[] = $it -> current ();
  • break;
  • }
  • $it -> next ();
  • }
  • return $aRes;
  • }
  • /**
  • * public function count
  • * count the total number of result sets of the given query
  • *
  • * @param resource $rQry
  • * @return integer
  • */
  • public function count ($rQry = null) {
  • 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 (int)@$this -> _count ($rQry);
  • } else {
  • return (int)@$this -> _count ($this -> rQry);
  • }
  • }
  • /**
  • * public function tableExist
  • * Verifie l'existence d'une table
  • *
  • * @param resource $rQry
  • * @return boolean
  • */
  • public function tableExists ($sTableName) {
  • if (!is_string($sTableName)) {
  • $this -> interceptException ('aDBExceptionTypesError', str_replace('{PARAM}', '1st parameter', aDBExceptionTypesError::MUST_BE_A_STRING), aDBExceptionTypesError::CODE_MUST_BE_A_STRING, get_class($this), __FUNCTION__);
  • return false;
  • }
  • return @$this -> _tableExists ($sTableName);
  • }
  • /**
  • * public function prepare
  • * Prepare the given query
  • *
  • * @param string $sQuery
  • */
  • public function prepare ($sQuery) {
  • $this -> sQuery = $this -> _escape ($sQuery);
  • return true;
  • }
  • /**
  • * public function bindValue
  • * Binds a value and a type to a needle
  • *
  • * @param string $sNeedle : string to be replaced
  • * @param mixed $mValue : value
  • * @param aDB Class Constant $cType : defines the type of the value
  • */
  • public function bindValue ($sNeedle, $mValue, $cType = null) {
  • if (is_null ($this -> sQuery)) {
  • $this -> interceptException ('aDBExceptionInvalidClassCalls', aDBExceptionInvalidClassCalls::NO_QUERY_TO_PREPARE, aDBExceptionInvalidClassCalls::CODE_NO_QUERY_TO_PREPARE, get_class($this), __FUNCTION__);
  • return false;
  • }
  • if (false === strpos($this -> sQuery, $sNeedle)) {
  • $this -> interceptException ('aDBExceptionInvalidClassCalls', str_replace (array ('{NEEDLE}', '{QUERY}'), array ($sNeedle, $this -> sQuery), aDBExceptionInvalidClassCalls::NEEDLE_NOT_FOUND), aDBExceptionInvalidClassCalls::CODE_NEEDLE_NOT_FOUND, get_class($this), __FUNCTION__);
  • return false;
  • }
  • if (is_null ($cType)) {
  • $sType = gettype ($mValue);
  • switch ($sType) {
  • case 'integer':
  • $cType = aDB::PARAM_INT;
  • break;
  • case 'double':
  • $cType = aDB::PARAM_FLOAT;
  • break;
  • case 'boolean':
  • $cType = aDB::PARAM_BOOL;
  • break;
  • case 'string':
  • $cType = aDB::PARAM_STR;
  • break;
  • default:
  • $this -> interceptException ('aDBExceptionInvalidClassCalls', aDBExceptionInvalidClassCalls::PARAM_TYPE_NOT_FOUND, aDBExceptionInvalidClassCalls::CODE_PARAM_TYPE_NOT_FOUND, get_class($this), __FUNCTION__);
  • return false;
  • break;
  • }
  • }
  • switch ($cType) {
  • case aDB::PARAM_STR:
  • $mValue = $this -> _escape ($mValue);
  • $mValue = "'".$mValue."'";
  • $this -> sQuery = preg_replace('`(\b|[^_])('.$sNeedle.')(\b|[^_])`m', '$1'.$mValue.'$3', $this -> sQuery);
  • break;
  • case aDB::PARAM_INT:
  • case aDB::PARAM_FLOAT:
  • case aDB::PARAM_BOOL:
  • $this -> sQuery = preg_replace('`(\b|[^_])('.$sNeedle.')(\b|[^_])`m', '${1}'.$mValue.'${3}', $this -> sQuery);
  • break;
  • default:
  • $this -> interceptException ('aDBExceptionInvalidClassCalls', aDBExceptionInvalidClassCalls::PARAM_TYPE_NOT_FOUND, aDBExceptionInvalidClassCalls::CODE_PARAM_TYPE_NOT_FOUND, get_class($this), __FUNCTION__);
  • return false;
  • break;
  • }
  • return true;
  • }
  • /**
  • * public function execute
  • * Execute a prepared query
  • *
  • */
  • public function execute ($bOverWriteQry = true) {
  • if (is_null ($this -> sQuery)) {
  • $this -> interceptException ('aDBExceptionInvalidClassCalls', aDBExceptionInvalidClassCalls::NO_QUERY_TO_PREPARE, aDBExceptionInvalidClassCalls::CODE_NO_QUERY_TO_PREPARE, get_class($this), __FUNCTION__);
  • return false;
  • }
  • $this -> query ($this -> sQuery, $bOverWriteQry);
  • return true;
  • }
  • /**
  • * public function call
  • * Call a stored procedure
  • *
  • */
  • public function call($sProcName) {
  • if (!is_string ($sProcName)) {
  • $this -> interceptException ('aDBExceptionTypesError', aDBExceptionTypesError::MUST_BE_A_STRING , aDBExceptionTypesError::CODE_MUST_BE_A_STRING, get_class($this), __FUNCTION__);
  • return false;
  • }
  • return $this->rStmtLink = $this->_call ($sProcName);
  • }
  • /**
  • * public function bindParam
  • * Binds parameters for a stored procedure
  • *
  • */
  • public function bindParam($sParam, $mValue, $cType = null) {
  • if((is_null($this->rStmtLink) || !is_resource($this->rStmtLink))) {
  • $this -> interceptException ('aDBExceptionInvalidClassCalls', aDBExceptionInvalidClassCalls::NO_PROC_STMT, aDBExceptionInvalidClassCalls::CODE_NO_PROC_STMT, get_class($this), __FUNCTION__);
  • return false;
  • }
  • if(is_null($cType)) {
  • $cType = gettype($mValue);
  • }
  • return $this -> _bindParam ($sParam, $mValue, $cType);
  • }
  • /**
  • * public function procExec
  • * Executes a stored procedure
  • *
  • */
  • public function procExec($bOverWriteQry = true) {
  • if((is_null($this->rStmtLink) || !is_resource($this->rStmtLink))) {
  • $this -> interceptException ('aDBExceptionInvalidClassCalls', aDBExceptionInvalidClassCalls::NO_PROC_STMT, aDBExceptionInvalidClassCalls::CODE_NO_PROC_STMT, get_class($this), __FUNCTION__);
  • return false;
  • }
  • if(true === $bOverWriteQry) {
  • if(!is_null($this->rQry)) {
  • $this->freeResult();
  • }
  • }
  • if(false === ($rRes = $this -> _procExec())) {
  • $this -> interceptException ('aDBExceptionDbConnectorErrors', str_replace (array ('{QRY}', '{MSG}'), array ($sQuery, $this -> _errorMsg ()), aDBExceptionDbConnectorErrors::PROC_FAILED), aDBExceptionDbConnectorErrors::CODE_PROC_FAILED, get_class($this), __FUNCTION__);
  • return false;
  • }
  • if(true === $bOverWriteQry) {
  • $this -> rQry = $rRes;
  • }
  • return $rRes;
  • }
  • /**
  • * public function nextResult
  • * Moves to trhe next result set
  • *
  • */
  • public function nextResult($rQry = null) {
  • if (is_null ($this -> rQry) && is_null ($rQry)) {
  • $this -> interceptException ('aDBExceptionDbConnectorErrors', aDBExceptionDbConnectorErrors::INVALID_QRY_RESOURCE, aDBExceptionDbConnectorErrors::CODE_INVALID_QRY_RESOURCE, get_class($this), __FUNCTION__);
  • return false;
  • }
  • if(!is_null($rQry)) {
  • return $this->_nextResult($rQry);
  • } else {
  • return $this->_nextResult($this->rQry);
  • }
  • }
  • /**
  • * public function escape
  • * Escapes a string
  • *
  • * @param string $sString
  • */
  • public function escape ($sString) {
  • return $this -> _escape ($sString);
  • }
  • /**
  • * public function startTransaction
  • * Starts a transaction
  • *
  • */
  • public function startTransaction ($sTransacName = null) {
  • $this -> _startTransaction ($sTransacName);
  • return true;
  • }
  • /**
  • * public function commitTransaction
  • * Commits a transaction
  • *
  • */
  • public function commitTransaction ($sTransacName = null) {
  • $this -> _commitTransaction ($sTransacName);
  • return true;
  • }
  • /**
  • * public function rollbackTransaction
  • * Rolls a transaction back
  • *
  • */
  • public function rollbackTransaction ($sTransacName = null) {
  • $this -> _rollbackTransaction ($sTransacName);
  • return true;
  • }
  • /**
  • * public function countTransaction
  • * Returns the number of transactions
  • *
  • * @return integer
  • */
  • public function countTransaction () {
  • return @$this -> _countTransaction ();
  • }
  • /**
  • * public function savePoint
  • * Creates a savepoint
  • *
  • */
  • public function savePoint ($sSavePointName) {
  • $this -> _savePoint ($sSavePointName);
  • return true;
  • }
  • /**
  • * public function rollbackToSavePoint
  • * Rollbacks to a savepoint
  • *
  • */
  • public function rollbackToSavePoint ($sSavePointName) {
  • $this -> _rollbackToSavePoint ($sSavePointName);
  • return true;
  • }
  • /**
  • * public function releaseSavePoint
  • * Releases a savepoint
  • *
  • */
  • public function releaseSavePoint ($sSavePointName) {
  • $this -> _releaseSavePoint ($sSavePointName);
  • return true;
  • }
  • /**
  • * public function lastInsertId
  • * Returns the last inserted ID
  • *
  • * @return integer
  • */
  • public function lastInsertId () {
  • return @$this -> _lastInsertId ();
  • }
  • /**
  • * public function close
  • * Close connexion
  • *
  • * @return integer
  • */
  • public function close($bDestroyInstance = false) {
  • $bReturn = @$this -> _close ();
  • $this->rLink = null;
  • if(true === $bDestroyInstance) {
  • if(isset(aDBFactory::$_aInstance[get_class($this)][$this->sNameSpace])) {
  • unset(aDBFactory::$_aInstance[get_class($this)][$this->sNameSpace]);
  • }
  • }
  • return $bReturn;
  • }
  • /**
  • * public function getIdentityColName
  • * Get table's identity column name
  • *
  • * @param string $sTable : table's name
  • * @return string
  • */
  • public function getIdentityColName($sTable) {
  • return @$this->_getIdentityColName($sTable);
  • }
  • /**
  • * public function getColumnProperties
  • * Get the table's columns properties, or the properties of a given column in this table
  • *
  • * @param string $sTable : table's name
  • * @param string $sColumn : column's name
  • * @return array : associative array with columns properties
  • */
  • public function getColumnProperties($sTable, $sColumn = null) {
  • return @$this->_getColumnProperties($sTable, $sColumn);
  • }
  • /**
  • * public function freeResult
  • * Free requested query result resource
  • *
  • * @return boolean
  • */
  • public function freeResult ($rQry = null) {
  • if (is_null ($rQry)) {
  • if (false === ($bRes = @$this->_freeResult())) {
  • $this -> interceptException ('aDBExceptionDbConnectorErrors', str_replace ('{MSG}', $this -> _errorMsg (), aDBExceptionDbConnectorErrors::COULD_NOT_FREE_RESULT), aDBExceptionDbConnectorErrors::CODE_COULD_NOT_FREE_RESULT, get_class($this), __FUNCTION__);
  • }
  • $this->rQry = null;
  • } else {
  • if (false === ($bRes = @$this->_freeResult($rQry))) {
  • $this -> interceptException ('aDBExceptionDbConnectorErrors', str_replace ('{MSG}', $this -> _errorMsg (), aDBExceptionDbConnectorErrors::COULD_NOT_FREE_RESULT), aDBExceptionDbConnectorErrors::CODE_COULD_NOT_FREE_RESULT, get_class($this), __FUNCTION__);
  • }
  • }
  • return $bRes;
  • }
  • /**
  • * public function freeStatement
  • * Free requested statement resource
  • *
  • * @return boolean
  • */
  • public function freeStatement ($rStmt = null) {
  • if (is_null ($rStmt)) {
  • if (false === ($bRes = @$this -> _freeStatement())) {
  • $this -> interceptException ('aDBExceptionDbConnectorErrors', str_replace ('{MSG}', $this -> _errorMsg (), aDBExceptionDbConnectorErrors::COULD_NOT_FREE_STMT), aDBExceptionDbConnectorErrors::CODE_COULD_NOT_FREE_STMT, get_class($this), __FUNCTION__);
  • }
  • $this->rStmtLink = null;
  • } else {
  • if (false === ($bRes = @$this -> _freeStatement($rStmt))) {
  • $this -> interceptException ('aDBExceptionDbConnectorErrors', str_replace ('{MSG}', $this -> _errorMsg (), aDBExceptionDbConnectorErrors::COULD_NOT_FREE_STMT), aDBExceptionDbConnectorErrors::CODE_COULD_NOT_FREE_STMT, get_class($this), __FUNCTION__);
  • }
  • }
  • return $bRes;
  • }
  • /**
  • * public function __fetch
  • * public fetch method used by the Iterator
  • *
  • * @param resurce $rQry
  • * @param aDB Class Constant $aDB_MODE
  • * @return array
  • */
  • public function __fetch ($rQry, $aDB_MODE) {
  • return @$this -> _fetch ($rQry, $aDB_MODE);
  • }
  • /**
  • * public function __seek
  • * Seek method used by the Iterator
  • *
  • * @param integer $iOffset
  • * @return resource
  • */
  • public function __seek ($iOffset) {
  • return @$this -> _seek ($iOffset);
  • }
  • /**
  • * public function errorMsg
  • * Returns db api error message
  • *
  • * @return string
  • */
  • public function errorMsg () {
  • return $this -> _errorMsg ();
  • }
  • /**
  • * public function interceptException
  • * If EXCEPTION_ON_ERROR mode is active, throw an aDBException.
  • * If not, logs any error in the aDB::aErrorLogs array
  • *
  • * @param string $sErrorMsg
  • * @param string $sClass
  • * @param string $sFunction
  • */
  • public function interceptException ($sExceptionClass, $sErrorMsg, $iCode, $sClass = 'unknown', $sFunction = 'unknown') {
  • if (true === $this -> aOptions['EXCEPTION_ON_ERROR']) {
  • throw new $sExceptionClass ($sErrorMsg, $iCode, $sClass, $sFunction);
  • } else {
  • aDB::$aErrorLog[] = $sClass.'::'.$sFunction.'() : '.$sErrorMsg.' ['.$iCode.']';
  • }
  • }
  • /**
  • * public function server_info
  • * Gets server info if any
  • *
  • * @param resource $rQry
  • * @return mixed
  • */
  • public function server_info ($rLink = null) {
  • if (!is_null ($rLink)) {
  • if (false === ($mInfos = @$this -> _server_info ($rLink))) {
  • $this -> interceptException ('aDBExceptionDbConnectorErrors', aDBExceptionDbConnectorErrors::REQUEST_ERROR, aDBExceptionDbConnectorErrors::CODE_REQUEST_ERROR, get_class($this), __FUNCTION__);
  • return false;
  • }
  • return $mInfos;
  • } else {
  • if (false === ($mInfos = @$this -> _server_info ($this ->rLink))) {
  • $this -> interceptException ('aDBExceptionDbConnectorErrors', aDBExceptionDbConnectorErrors::REQUEST_ERROR, aDBExceptionDbConnectorErrors::CODE_REQUEST_ERROR, get_class($this), __FUNCTION__);
  • return false;
  • }
  • return $mInfos;
  • }
  • }
  • /**
  • * public function setOption
  • * Sets an existing option
  • *
  • * @param string $sOption
  • * @param mixed $mValue
  • */
  • public function setOption ($sOption, $mValue) {
  • if (false === array_key_exists ($sOption, $this -> aOptions)) {
  • $this -> interceptException ('aDBExceptionInvalidClassCalls', str_replace ('{OPTION}', $sOption, aDBExceptionInvalidClassCalls::INVALID_OPTION), aDBExceptionInvalidClassCalls::CODE_INVALID_OPTION, get_class($this), __FUNCTION__);
  • return false;
  • }
  • $this -> aOptions[$sOption] = $mValue;
  • return true;
  • }
  • /**
  • * public function __get
  • * returns an authorized property value
  • *
  • * @param string $sProp
  • * @return mixed
  • */
  • public function __get ($sProp) {
  • switch ($sProp) {
  • case 'QUERY':
  • return $this -> sQuery;
  • break;
  • case 'ERRORS':
  • return $this -> aErrorLog;
  • break;
  • case 'CONF':
  • return $this -> aConConf;
  • break;
  • case 'NAMESPACE':
  • return $this -> sNameSpace;
  • break;
  • case 'LAST_ERROR':
  • if (is_array($this -> aOptions['EXCEPTION_ON_ERROR'])) {
  • return end($this -> aErrorLog);
  • }
  • return false;
  • break;
  • default:
  • $this -> interceptException ('aDBExceptionInvalidClassCalls', str_replace ('{PROP}', $sProp, aDBExceptionInvalidClassCalls::PROP_NOT_GETABLE), aDBExceptionInvalidClassCalls::CODE_PROP_NOT_GETABLE, get_class($this), __FUNCTION__);
  • return false;
  • break;
  • }
  • }
  • abstract protected function _connect ($bOpenNewConnection = true);
  • abstract protected function _errorMsg ();
  • abstract protected function _select_db ($sDbName);
  • abstract protected function _seek ($iOffset);
  • abstract protected function _query ($sQuery);
  • abstract protected function _fetch ($rQry, $aDB_MODE);
  • abstract protected function _count ($rQry);
  • abstract protected function _lastInsertId ();
  • abstract protected function _startTransaction ($sTransacName = null);
  • abstract protected function _commitTransaction ($sTransacName = null);
  • abstract protected function _rollbackTransaction ($sTransacName = null);
  • abstract protected function _countTransaction ();
  • abstract protected function _escape ($sString);
  • abstract protected function _server_info ($rLink);
  • abstract protected function _freeResult ($rQry = null);
  • abstract protected function _freeStatement ($rStmt = null);
  • abstract protected function _savePoint ($sSavePointName);
  • abstract protected function _rollbackToSavePoint ($sSavePointName);
  • abstract protected function _releaseSavePoint ($sSavePointName);
  • abstract protected function _close ();
  • abstract protected function _getIdentityColName($sTable);
  • abstract protected function _tableExists($sTable);
  • abstract protected function _getColumnProperties($sTable, $sColumn = null);
  • abstract protected function _call($sProcName);
  • abstract protected function _bindParam($sParam, $mValue, $cType);
  • abstract protected function _procExec();
  • abstract protected function _nextResult($rQry);
  • }
  • class mssql extends aDB {
  • protected function _connect ($bOpenNewConnection = true) {
  • return @mssql_connect ($this -> aConConf['HOST'], $this -> aConConf['LOGIN'], $this -> aConConf['PWD'], $bOpenNewConnection);
  • }
  • protected function _errorMsg () {
  • return @mssql_get_last_message();
  • }
  • protected function _select_db ($sDbName) {
  • return @mssql_select_db ($sDbName, $this -> rLink);
  • }
  • protected function _seek ($iOffset) {
  • return @mssql_data_seek ($this -> rQry, $iOffset);
  • }
  • protected function _query ($sQuery) {
  • return @mssql_query ($sQuery, $this -> rLink);
  • }
  • protected function _fetch ($rQry, $aDB_MODE) {
  • switch ($aDB_MODE) {
  • case aDB::FETCH_ASSOC:
  • $sMode = 'MSSQL_ASSOC';
  • break;
  • case aDB::FETCH_NUM:
  • $sMode = 'MSSQL_NUM';
  • break;
  • default:
  • $sMode = 'MSSQL_BOTH';
  • break;
  • }
  • if (defined ('MSSQL_ASSOC')) {
  • return @mssql_fetch_array ($rQry, constant ($sMode));
  • } else {
  • $aRes = @mssql_fetch_array ($rQry);
  • if(false !== $aRes) {
  • if ($sMode === 'MSSQL_NUM') {
  • $aRes = self::phpBadGirl ($aRes, 0);
  • }
  • if ($sMode === 'MSSQL_ASSOC') {
  • $aRes = self::phpBadGirl ($aRes, 1);
  • }
  • }
  • return $aRes;
  • }
  • }
  • protected static function phpBadGirl ($aTmp, $iMode) {
  • $aNew = array ();
  • if(is_array($aTmp)) {
  • switch ($iMode) {
  • case 0:
  • foreach ($aTmp as $mK => $mV) {
  • if (is_int ($mK)) {
  • $aNew[$mK] = $mV;
  • }
  • }
  • break;
  • case 1:
  • foreach ($aTmp as $mK => $mV) {
  • if (is_string ($mK)) {
  • $aNew[$mK] = $mV;
  • }
  • }
  • break;
  • }
  • }
  • if (empty ($aNew)) {
  • return $aTmp;
  • }
  • return $aNew;
  • }
  • protected function _count ($rQry) {
  • return @mssql_num_rows ($rQry);
  • }
  • protected function _lastInsertId () {
  • $sQuery = 'SELECT @@IDENTITY';
  • $rQry = $this -> query ($sQuery, false);
  • return $this -> fetchColumn (0, $rQry, false);
  • }
  • protected function _startTransaction ($sTransacName = null) {
  • $sQuery = 'BEGIN TRANSACTION';
  • if(!is_null($sTransacName)) {
  • $sQuery .= ' '.$this->escape($sTransacName);
  • }
  • $rQry = $this -> query ($sQuery, false);
  • return $rQry;
  • }
  • protected function _commitTransaction ($sTransacName = null) {
  • $sQuery = 'COMMIT TRANSACTION';
  • if(!is_null($sTransacName)) {
  • $sQuery .= ' '.$this->escape($sTransacName);
  • }
  • $rQry = $this -> query ($sQuery, false);
  • return $rQry;
  • }
  • protected function _rollbackTransaction ($sTransacName = null) {
  • $sQuery = 'ROLLBACK TRANSACTION';
  • if(!is_null($sTransacName)) {
  • $sQuery .= ' '.$this->escape($sTransacName);
  • }
  • $rQry = $this -> query ($sQuery, false);
  • return $rQry;
  • }
  • protected function _countTransaction () {
  • $sQuery = 'SELECT @@TRANCOUNT';
  • $rQry = $this -> query ($sQuery, false);
  • return (int)$this -> fetchColumn (0, $rQry, false);
  • }
  • protected function _escape ($sString) {
  • return @str_replace ("'", "''", $sString);
  • }
  • protected function _server_info ($rLink) {
  • throw new aDBExceptionInvalidClassCalls(aDBExceptionInvalidClassCalls::UNSUPPORTED_METHOD, aDBExceptionInvalidClassCalls::CODE_UNSUPPORTED_METHOD, get_class($this), __FUNCTION__);
  • }
  • protected function _freeResult ($rQry = null) {
  • if(!is_null($rQry) && is_resource($rQry) && get_resource_type($rQry) === 'mssql result') {
  • return @mssql_free_result ($rQry);
  • } elseif (is_resource($this->rQry) && get_resource_type($this->rQry) === 'mssql result') {
  • return @mssql_free_result ($this->rQry);
  • } else {
  • return true;
  • }
  • }
  • protected function _freeStatement ($rStmt = null) {
  • if(!is_null($rStmt)) {
  • return @mssql_free_statement ($rStmt);
  • } else {
  • return @mssql_free_statement ($this->rStmtLink);
  • }
  • }
  • protected function _savePoint ($sSavePointName) {
  • $sQuery = 'SAVE TRANSACTION '.$this->_escape($sSavePointName);
  • $rQry = $this -> query ($sQuery, false);
  • return $rQry;
  • }
  • protected function _rollbackToSavePoint ($sSavePointName) {
  • $sQuery = 'ROLLBACK TRANSACTION '.$this->_escape($sSavePointName);
  • $rQry = $this -> query ($sQuery, false);
  • return $rQry;
  • }
  • protected function _releaseSavePoint ($sSavePointName) {
  • throw new aDBExceptionInvalidClassCalls(aDBExceptionInvalidClassCalls::UNSUPPORTED_METHOD, aDBExceptionInvalidClassCalls::CODE_UNSUPPORTED_METHOD, get_class($this), __FUNCTION__);
  • }
  • protected function _getIdentityColName($sTable) {
  • $sQuery = 'SELECT TOP 1 IDENTITYCOL FROM '.$this->_escape($sTable);
  • $rQry = $this -> query ($sQuery, false);
  • return @mssql_field_name($rQry);
  • }
  • protected function _tableExists($sTable){
  • $sQuery="SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'$sTable')";
  • $rQry = $this -> query ($sQuery, false);
  • if ($this->count($rQry)===0){
  • return false;
  • }
  • return true;
  • }
  • protected function _getColumnProperties($sTable, $sColumn = null) {
  • $sQuery = "SELECT syscolumns.name as colname, systypes.name,
  • syscolumns.length, syscolumns.isnullable, syscolumns.xprec, syscolumns.collation,
  • syscolumns.iscomputed, syscolumns.colorder, syscolumns.autoval
  • FROM dbo.systypes systypes
  • INNER JOIN dbo.syscolumns syscolumns ON syscolumns.xtype = systypes.xtype
  • WHERE syscolumns.id = OBJECT_ID(N'{$sTable}')";
  • if(!is_null($sColumn)) {
  • $sQuery.= " AND syscolumns.name = '{$sColumn}'";
  • }
  • $this -> query ($sQuery);
  • return $this->fetchAll(aDB::FETCH_ASSOC);
  • }
  • protected function _close () {
  • return @mssql_close ($this->rLink);
  • }
  • protected function _call($sProcName) {
  • return @mssql_init($sProcName);
  • }
  • protected function _bindParam($sParam, $mValue, $cType) {
  • switch($cType) {
  • case aDB::PARAM_INT:
  • case 'integer':
  • $sType = SQLINT4;
  • break;
  • case aDB::PARAM_STR:
  • case 'string':
  • $sType = SQLVARCHAR;
  • break;
  • case aDB::PARAM_FLOAT:
  • case 'float':
  • $sType = SQLFLT8;
  • break;
  • case aDB::PARAM_BOOL:
  • case 'boolean':
  • $sType = SQLINT1;
  • break;
  • default:
  • $this -> interceptException ('aDBExceptionInvalidClassCalls', aDBExceptionInvalidClassCalls::PARAM_TYPE_NOT_FOUND, aDBExceptionInvalidClassCalls::CODE_PARAM_TYPE_NOT_FOUND, get_class($this), __FUNCTION__);
  • break;
  • }
  • return @mssql_bind($this->rStmtLink, $sParam, $mValue, $sType);
  • }
  • protected function _procExec() {
  • return @mssql_execute($this->rStmtLink);
  • }
  • protected function _nextResult($rQry) {
  • if(function_exists('mssql_next_result')) {
  • return @mssql_next_result($rQry);
  • } else {
  • return false;
  • }
  • }
  • }
  • class mysql extends aDB {
  • protected function _connect ($bOpenNewConnection = true) {
  • return @mysql_connect ($this -> aConConf['HOST'], $this -> aConConf['LOGIN'], $this -> aConConf['PWD']);
  • }
  • protected function _errorMsg () {
  • return @mysql_error();
  • }
  • protected function _select_db ($sDbName) {
  • return @mysql_select_db ($sDbName, $this -> rLink);
  • }
  • protected function _seek ($iOffset) {
  • return @mysql_data_seek ($this -> rQry, $iOffset);
  • }
  • protected function _query ($sQuery) {
  • return @mysql_query ($sQuery, $this -> rLink);
  • }
  • protected function _fetch ($rQry, $aDB_MODE) {
  • switch ($aDB_MODE) {
  • case aDB::FETCH_ASSOC:
  • $sMode = 'MYSQL_ASSOC';
  • break;
  • case aDB::FETCH_NUM:
  • $sMode = 'MYSQL_NUM';
  • break;
  • default:
  • $sMode = 'MYSQL_BOTH';
  • break;
  • }
  • return @mysql_fetch_array ($rQry, constant ($sMode));
  • }
  • protected function _count ($rQry) {
  • return @mysql_num_rows ($rQry);
  • }
  • protected function _lastInsertId () {
  • return @mysql_insert_id ($this -> rQry);
  • }
  • protected function _startTransaction ($sTransacName = null) {
  • $sQuery = 'START TRANSACTION';
  • $rQry = $this -> query ($sQuery, false);
  • return $rQry;
  • }
  • protected function _commitTransaction ($sTransacName = null) {
  • $sQuery = 'COMMIT';
  • $rQry = $this -> query ($sQuery, false);
  • return $rQry;
  • }
  • protected function _rollbackTransaction ($sTransacName = null) {
  • $sQuery = 'ROLLBACK';
  • $rQry = $this -> query ($sQuery, false);
  • return $rQry;
  • }
  • protected function _countTransaction () {
  • throw new aDBExceptionInvalidClassCalls(aDBExceptionInvalidClassCalls::UNSUPPORTED_METHOD, aDBExceptionInvalidClassCalls::CODE_UNSUPPORTED_METHOD, get_class($this), __FUNCTION__);
  • }
  • protected function _escape ($sString) {
  • return @mysql_real_escape_string ($sString, $this -> rLink);
  • }
  • protected function _server_info ($rLink) {
  • return @mysql_get_server_info ($rLink);
  • }
  • protected function _freeResult ($rQry = null) {
  • if(!is_null($rQry)) {
  • return @mysql_free_result ($rQry);
  • } else {
  • return @mysql_free_result ($this->rQry);
  • }
  • }
  • protected function _freeStatement ($rStmt = null) {
  • //todo
  • return false;
  • }
  • protected function _savePoint ($sSavePointName) {
  • $sQuery = 'SAVEPOINT '.$sSavePointName;
  • $rQry = $this -> query ($sQuery, false);
  • return $rQry;
  • }
  • protected function _rollbackToSavePoint ($sSavePointName) {
  • $sQuery = 'ROLLBACK TO SAVEPOINT '.$sSavePointName;
  • $rQry = $this -> query ($sQuery, false);
  • return $rQry;
  • }
  • protected function _releaseSavePoint ($sSavePointName) {
  • $sQuery = 'RELEASE SAVEPOINT '.$sSavePointName;
  • $rQry = $this -> query ($sQuery, false);
  • return $rQry;
  • }
  • protected function _getIdentityColName($sTable) {
  • throw new aDBExceptionInvalidClassCalls(aDBExceptionInvalidClassCalls::UNSUPPORTED_METHOD, aDBExceptionInvalidClassCalls::CODE_UNSUPPORTED_METHOD, get_class($this), __FUNCTION__);
  • }
  • protected function _tableExists($sTable){
  • $sQuery="SHOW TABLES LIKE '$sTable'";
  • $rQry = $this -> query ($sQuery, false);
  • if ($this->count($rQry)===0){
  • return false;
  • }
  • return true;
  • }
  • protected function _getColumnProperties($sTable, $sColumn = null) {
  • // todo
  • return false;
  • }
  • protected function _close () {
  • return @mysql_close ($this->rLink);
  • }
  • protected function _call($sProcName) {
  • //todo
  • return false;
  • }
  • protected function _bindParam($sParam, $mValue, $cType) {
  • //todo
  • return false;
  • }
  • protected function _procExec() {
  • //todo
  • return false;
  • }
  • protected function _nextResult($rQry) {
  • //todo
  • return false;
  • }
  • }
  • ?>
<?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() {
		$sMsg = '<strong>'.$this -> sCallerClass.'::'.$this -> sCallerFunc.'()</strong><br />';
		$sMsg .= $this->getMessage().' ['.$this->getCode().']<br />';
		$sMsg .= '<em>File</em> : '.$this->getFile().' on line '.$this->getLine().'<br />';
		$sMsg .= '<em>Trace</em> :<br />'.$this->getTraceAsString().'<br />';
		return $sMsg;
	}

}
/**
 * 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 MUST_BE_A_STRING = '{PARAM} must be a string';
	const MUST_BE_A_BOOL = '{PARAM} must be a boolean';
	const MUST_BE_A_SCALAR = '{PARAM} must be a scalar value';

	const CODE_MUST_BE_AN_ARRAY = 0;
	const CODE_MUST_BE_AN_INT = 1;
	const CODE_MUST_BE_A_STRING = 2;
	const CODE_MUST_BE_A_BOOL = 3;
	const CODE_MUST_BE_A_SCALAR = 4;
}

/**
 * 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 UNSUPPORTED_METHOD = 'This method is not yet supported by this db type';
	const NO_LINK_FOUND_AND_NO_CONFIGURATION_FOUND = 'You try to use an instance which has been destroyed previously';
	const NO_PROC_STMT = 'No statement resource found';
	
	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;
	const CODE_UNSUPPORTED_METHOD = 5;
	const CODE_NO_LINK_FOUND_AND_NO_CONFIGURATION_FOUND = 6;
	const CODE_NO_PROC_STMT = 7;
}

/**
 * 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 = 'Freeing result failed with message [{MSG}]';
	const COULD_NOT_FREE_STMT = 'Freeing statement failed with message [{MSG}]';
	const PROC_FAILED = 'Procedure [{QRY}] 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;
	const CODE_COULD_NOT_FREE_STMT = 8;
	const CODE_PROC_FAILED = 9;
}

/**
 * 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 $_aInstance;
	
	private static $_aInstances;

	/**
	 * 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...
	 * NEW : if $mIsSingleton is left to true, behaviour is as described above.
	 * If it is false, a new instance is automatically created and stored.
	 * If it is a set to something else (string for example), it is used to store the instance under this name, if it does not already exists, or returns it if it does (must be the same DB type, too).
	 *
	 * @param string $saDBType : name of the requested aDB class
	 * @param array $aConConf : array of aDB connection configuration
	 * @param mixed $mIsSingleton : true if automatic singleton, false if no singleton, scalar value meaning that the instance is in a namespace : singleton on that namespace
	 * @param unknown_type $aOptions : array of aDB options
	 * @return aDB object : the requested aDB instance
	 */
	public static function getInstance ($saDBType, $aConConf = null, $mIsSingleton = true, $bOpenNewConnection = true, $aOptions = null) {
			if(!is_bool($bOpenNewConnection)) {
				throw new aDBExceptionTypesError (str_replace('{PARAM}', '$bOpenNewConnection', aDBExceptionTypesError::MUST_BE_A_BOOL), aDBExceptionTypesError::CODE_MUST_BE_A_BOOL, get_class($this), __FUNCTION__);
			}
		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(true === $mIsSingleton) {
			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, $bOpenNewConnection);
					}
					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];
			}
		} elseif(false === $mIsSingleton) {
			$iInstance = self::$_aInstances[$saDBType][] = new $saDBType ($aConConf, $aOptions);
			return $iInstance;
		} else {
			if(!is_scalar($mIsSingleton)) {
				throw new aDBExceptionTypesError (str_replace('{PARAM}', '$mIsSingleton', aDBExceptionTypesError::MUST_BE_A_SCALAR), aDBExceptionTypesError::CODE_MUST_BE_A_SCALAR, get_class($this), __FUNCTION__);
			}
			if(!isset(self::$_aInstance[$saDBType][$mIsSingleton]) || !self::$_aInstance[$saDBType][$mIsSingleton] instanceof $saDBType) {
				self::$_aInstance[$saDBType][$mIsSingleton] = new $saDBType ($aConConf, $aOptions, $mIsSingleton);
			} else {
				if (!is_null ($aConConf)) {
					self::$_aInstance[$saDBType][$mIsSingleton] -> connect($aConConf, $bOpenNewConnection);
				}
				if (!is_null ($aOptions)) {
					foreach ($aOptions as $sOption => $mValue) {
						self::$_aInstance[$saDBType][$mIsSingleton] -> setOption ($sOption, $mValue);
					}
				}
			}
			return self::$_aInstance[$saDBType][$mIsSingleton];
		}
	}
}

/**
 * class sqlIterator implements Iterator, SeekableIterator, Countable
 * @author johan <johan.barbier@gmail.com>
 * @version 20070524
 */
class sqlIterator implements Iterator, SeekableIterator {

	/**
	 * 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;
		if ((aDB::FETCH_GROUP^$aDB_MODE) !== 0 && (aDB::FETCH_GROUP^$aDB_MODE) !== $aDB_MODE && (aDB::FETCH_GROUP^$aDB_MODE) < $aDB_MODE) {
			$this -> aDB_MODE = aDB::FETCH_GROUP^$aDB_MODE;
		} elseif ((aDB::FETCH_GROUP_UNIQUE^$aDB_MODE) !== 0 && (aDB::FETCH_GROUP_UNIQUE^$aDB_MODE) !== $aDB_MODE && (aDB::FETCH_GROUP_UNIQUE^$aDB_MODE) < $aDB_MODE) {
			$this -> aDB_MODE = aDB::FETCH_GROUP_UNIQUE^$aDB_MODE;
		} else {
			$this -> aDB_MODE = $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:
				case aDB::FETCH_GROUP_UNIQUE|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:
				case aDB::FETCH_GROUP_UNIQUE|aDB::FETCH_ASSOC:
				case aDB::FETCH_GROUP_UNIQUE|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);
		}
		if(false === $this -> mOutput) {
			if(false !== $this->aDB->nextResult($this->rQry)) {
				$this->iMax = $this->count();
				$this->rewind();
			}
		} 

	}

	/**
	 * 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;
	const FETCH_GROUP_UNIQUE = 16;

	/**
	 * Allowed parameter bindings
	 *
	 */
	const PARAM_STR = 1000;
	const PARAM_INT = 1001;
	const PARAM_FLOAT = 1002;
	const PARAM_BOOL = 1003;

	/**
	 * 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;

	/**
	 * DB query link resource
	 *
	 * @var resource
	 */
	public $rQry;
	
	/**
	 * Statement link resource
	 *
	 * @var resource
	 */
	protected $rStmtLink;
	
	/**
	 * Query
	 *
	 * @var string
	 */
	protected $sQuery;

	/**
	 * Errors log when no exception
	 *
	 * @var array
	 */
	protected static $aErrorLog;

	/**
	 * NameSpace if any
	 *
	 * @var string
	 */
	protected $sNameSpace = null;
	/**
	 * public function __construct
	 * Constructor.
	 * Automatic connection if AUTOCONNECT option is true
	 *
	 * @param array $aConConf
	 * @param array $aOptions
	 */
	public function __construct ($aConConf = null, $aOptions = null, $sNameSpace = null) {
		$this->sNameSpace = $sNameSpace;
		if (is_array ($aOptions)) {
			foreach ($aOptions as $sK => $sV) {
				if (isset ($this -> aOptions[$sK])) {
					$this -> aOptions[$sK] = $sV;
				}
			}
		}
		if(is_null ($aConConf) && is_null($this->rLink)) {
			$this -> interceptException ('aDBExceptionInvalidClassCalls', aDBExceptionInvalidClassCalls::NO_LINK_FOUND_AND_NO_CONFIGURATION_FOUND, aDBExceptionInvalidClassCalls::CODE_NO_LINK_FOUND_AND_NO_CONFIGURATION_FOUND, get_class($this), __FUNCTION__);
		}
		if ((true === $this -> aOptions['AUTOCONNECT'] && !is_null ($aConConf)) || is_null($this->rLink)) {
			$this -> connect ($aConConf);
		}
	}

	/**
	 * public function __destruct
	 * Destructor.
	 * AClose connection
	 *
	 * @@return boolean true on success, false if not
	 */
	public function __destruct() {
		return @$this->close();
	}

	/**
	 * 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, $bOpenNewConnection = true) {
		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(is_null($this->rLink) || true === $bOpenNewConnection) {
			if (false === ($this -> rLink = $this -> _connect ($bOpenNewConnection))) {
				$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 (true === $bOverWriteQry) {
			if(!is_null($this->rQry)) {
				$this->freeResult();
			}
		}
		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 ($rQry) && is_null ($this -> rQry)  && is_null($this->rProcStmt)) {
			$this -> interceptException ('aDBExceptionDbConnectorErrors', aDBExceptionDbConnectorErrors::INVALID_QRY_RESOURCE, aDBExceptionDbConnectorErrors::CODE_INVALID_QRY_RESOURCE, get_class($this), __FUNCTION__);
			return false;
		}
		if (!is_null ($rQry)) {
			$it = new sqlIterator ($this, $rQry, aDB::FETCH_NUM, $this -> iOffset, null, $bIsSeekable);
		} else {
			$it = new sqlIterator ($this, $this -> rQry, aDB::FETCH_NUM, $this -> iOffset, null, $bIsSeekable);
		}
		$it -> next ();
		$aRes = $it -> current ();
		if (empty ($aRes) || !isset ($aRes[$iColumn])) {
			return false;
		}
		return $aRes[$iColumn];
	}

	/**
	 * public function fetchAll
	 * using current query resource or given query resource, returns an array with all the result sets.
	 * The array's structure depends on the fetch mode.
	 *
	 * @param aDB Class Constant $aDB_MODE
	 * @param resource $rQry
	 * @param boolean $bIsSeekable : if sets to true, query is seekable
	 * @return array
	 */
	public function fetchAll ($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;
		}
		$aRes = array ();
		if (!is_null ($rQry)) {
			$it = new sqlIterator ($this, $rQry, $aDB_MODE, $this -> iOffset, $this -> iFetchLength, $bIsSeekable);
		} else {
			$it = new sqlIterator ($this, $this -> rQry, $aDB_MODE, $this -> iOffset, $this -> iFetchLength, $bIsSeekable);
		}
		$it -> next ();
		while ($it -> valid ()) {
			switch ($aDB_MODE) {
				case aDB::FETCH_GROUP_UNIQUE:
				case aDB::FETCH_GROUP_UNIQUE|aDB::BOTH:
				case aDB::FETCH_GROUP_UNIQUE|aDB::FETCH_ASSOC:
				case aDB::FETCH_GROUP_UNIQUE|aDB::FETCH_NUM:
					$aTmp = $it -> current ();
					$Ik = key($aTmp);
					$aRes[$Ik] = $aTmp[$Ik];
					break;
				default :
					$aRes[] = $it -> current ();
					break;
			}
			$it -> next ();
		}
		return $aRes;
	}

	/**
	 * public function count
	 * count the total number of result sets of the given query
	 *
	 * @param resource $rQry
	 * @return integer
	 */
	public function count ($rQry = null) {
		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 (int)@$this -> _count ($rQry);
		} else {
			return (int)@$this -> _count ($this -> rQry);
		}
	}
	
	/**
	 * public function tableExist
	 * Verifie l'existence d'une table
	 *
	 * @param resource $rQry
	 * @return boolean
	 */
	public function tableExists ($sTableName) {
		if (!is_string($sTableName)) {
			$this -> interceptException ('aDBExceptionTypesError', str_replace('{PARAM}', '1st parameter', aDBExceptionTypesError::MUST_BE_A_STRING), aDBExceptionTypesError::CODE_MUST_BE_A_STRING, get_class($this), __FUNCTION__);
			return false;
		}
		return @$this -> _tableExists ($sTableName);
	}

	/**
	 * public function prepare
	 * Prepare the given query
	 *
	 * @param string $sQuery
	 */
	public function prepare ($sQuery) {
		$this -> sQuery = $this -> _escape ($sQuery);
		return true;
	}

	/**
	 * public function bindValue
	 * Binds a value and a type to a needle
	 *
	 * @param string $sNeedle : string to be replaced
	 * @param mixed $mValue : value
	 * @param aDB Class Constant $cType : defines the type of the value
	 */
	public function bindValue ($sNeedle, $mValue, $cType = null) {
		if (is_null ($this -> sQuery)) {
			$this -> interceptException ('aDBExceptionInvalidClassCalls', aDBExceptionInvalidClassCalls::NO_QUERY_TO_PREPARE, aDBExceptionInvalidClassCalls::CODE_NO_QUERY_TO_PREPARE, get_class($this), __FUNCTION__);
			return false;
		}
		if (false === strpos($this -> sQuery, $sNeedle)) {
			$this -> interceptException ('aDBExceptionInvalidClassCalls', str_replace (array ('{NEEDLE}', '{QUERY}'), array ($sNeedle, $this -> sQuery), aDBExceptionInvalidClassCalls::NEEDLE_NOT_FOUND), aDBExceptionInvalidClassCalls::CODE_NEEDLE_NOT_FOUND, get_class($this), __FUNCTION__);
			return false;
		}
		if (is_null ($cType)) {
			$sType = gettype ($mValue);
			switch ($sType) {
				case 'integer':
					$cType = aDB::PARAM_INT;
					break;
				case 'double':
					$cType = aDB::PARAM_FLOAT;
					break;
				case 'boolean':
					$cType = aDB::PARAM_BOOL;
					break;
				case 'string':
					$cType = aDB::PARAM_STR;
					break;
				default:
					$this -> interceptException ('aDBExceptionInvalidClassCalls', aDBExceptionInvalidClassCalls::PARAM_TYPE_NOT_FOUND, aDBExceptionInvalidClassCalls::CODE_PARAM_TYPE_NOT_FOUND, get_class($this), __FUNCTION__);
					return false;
					break;

			}
		}
		switch ($cType) {
			case aDB::PARAM_STR:
				$mValue = $this -> _escape ($mValue);
				$mValue = "'".$mValue."'";
				$this -> sQuery = preg_replace('`(\b|[^_])('.$sNeedle.')(\b|[^_])`m', '$1'.$mValue.'$3', $this -> sQuery);
				break;
			case aDB::PARAM_INT:
			case aDB::PARAM_FLOAT:
			case aDB::PARAM_BOOL:
				$this -> sQuery = preg_replace('`(\b|[^_])('.$sNeedle.')(\b|[^_])`m', '${1}'.$mValue.'${3}', $this -> sQuery);
				break;
			default:
				$this -> interceptException ('aDBExceptionInvalidClassCalls', aDBExceptionInvalidClassCalls::PARAM_TYPE_NOT_FOUND, aDBExceptionInvalidClassCalls::CODE_PARAM_TYPE_NOT_FOUND, get_class($this), __FUNCTION__);
				return false;
				break;
		}
		return true;
	}

	/**
	 * public function execute
	 * Execute a prepared query
	 *
	 */
	public function execute ($bOverWriteQry = true) {
		if (is_null ($this -> sQuery)) {
			$this -> interceptException ('aDBExceptionInvalidClassCalls', aDBExceptionInvalidClassCalls::NO_QUERY_TO_PREPARE, aDBExceptionInvalidClassCalls::CODE_NO_QUERY_TO_PREPARE, get_class($this), __FUNCTION__);
			return false;
		}
		$this -> query ($this -> sQuery, $bOverWriteQry);
		return true;
	}

	/**
	 * public function call
	 * Call a stored procedure
	 *
	 */
	public function call($sProcName) {
		if (!is_string ($sProcName)) {
			$this -> interceptException ('aDBExceptionTypesError', aDBExceptionTypesError::MUST_BE_A_STRING , aDBExceptionTypesError::CODE_MUST_BE_A_STRING, get_class($this), __FUNCTION__);
			return false;
		}
		return $this->rStmtLink = $this->_call ($sProcName);
	}
	
	/**
	 * public function bindParam
	 * Binds parameters for a stored procedure
	 *
	 */
	public function bindParam($sParam, $mValue, $cType = null) {
		if((is_null($this->rStmtLink) || !is_resource($this->rStmtLink))) {
			$this -> interceptException ('aDBExceptionInvalidClassCalls', aDBExceptionInvalidClassCalls::NO_PROC_STMT, aDBExceptionInvalidClassCalls::CODE_NO_PROC_STMT, get_class($this), __FUNCTION__);
			return false;
		}
		if(is_null($cType)) {
			$cType = gettype($mValue);
		}
		return $this -> _bindParam ($sParam, $mValue, $cType);
	}
	
	/**
	 * public function procExec
	 * Executes a stored procedure
	 *
	 */
	public function procExec($bOverWriteQry = true) {
		if((is_null($this->rStmtLink) || !is_resource($this->rStmtLink))) {
			$this -> interceptException ('aDBExceptionInvalidClassCalls', aDBExceptionInvalidClassCalls::NO_PROC_STMT, aDBExceptionInvalidClassCalls::CODE_NO_PROC_STMT, get_class($this), __FUNCTION__);
			return false;
		}
		if(true === $bOverWriteQry) {
			if(!is_null($this->rQry)) {
				$this->freeResult();
			}
		}
		if(false === ($rRes = $this -> _procExec())) {
			$this -> interceptException ('aDBExceptionDbConnectorErrors', str_replace (array ('{QRY}', '{MSG}'), array ($sQuery, $this -> _errorMsg ()), aDBExceptionDbConnectorErrors::PROC_FAILED), aDBExceptionDbConnectorErrors::CODE_PROC_FAILED, get_class($this), __FUNCTION__);
			return false;
		}
		if(true === $bOverWriteQry) {
			$this -> rQry = $rRes;
		}
		return $rRes;
	}
	
	/**
	 * public function nextResult
	 * Moves to trhe next result set
	 *
	 */
	public function nextResult($rQry = null) {
		if (is_null ($this -> rQry) && is_null ($rQry)) {
			$this -> interceptException ('aDBExceptionDbConnectorErrors', aDBExceptionDbConnectorErrors::INVALID_QRY_RESOURCE, aDBExceptionDbConnectorErrors::CODE_INVALID_QRY_RESOURCE, get_class($this), __FUNCTION__);
			return false;
		}
		if(!is_null($rQry)) {
			return $this->_nextResult($rQry);
		} else {
			return $this->_nextResult($this->rQry);
		}
	}
	
	/**
	 * public function escape
	 * Escapes a string
	 *
	 * @param string $sString
	 */
	public function escape ($sString)  {
		return $this -> _escape ($sString);
	}

	/**
	 * public function startTransaction
	 * Starts a transaction
	 *
	 */
	public function startTransaction ($sTransacName = null)  {
		$this -> _startTransaction ($sTransacName);
		return true;
	}

	/**
	 * public function commitTransaction
	 * Commits a transaction
	 *
	 */
	public function commitTransaction ($sTransacName = null)  {
		$this -> _commitTransaction ($sTransacName);
		return true;
	}

	/**
	 * public function rollbackTransaction
	 * Rolls a transaction back
	 *
	 */
	public function rollbackTransaction ($sTransacName = null)  {
		$this -> _rollbackTransaction ($sTransacName);
		return true;
	}

	/**
	 * public function countTransaction
	 * Returns the number of transactions
	 *
	 * @return integer
	 */
	public function countTransaction () {
		return @$this -> _countTransaction ();
	}

	/**
	 * public function savePoint
	 * Creates a savepoint
	 *
	 */
	public function savePoint ($sSavePointName)  {
		$this -> _savePoint ($sSavePointName);
		return true;
	}

	/**
	 * public function rollbackToSavePoint
	 * Rollbacks to a savepoint
	 *
	 */
	public function rollbackToSavePoint ($sSavePointName)  {
		$this -> _rollbackToSavePoint ($sSavePointName);
		return true;
	}

	/**
	 * public function releaseSavePoint
	 * Releases a savepoint
	 *
	 */
	public function releaseSavePoint ($sSavePointName)  {
		$this -> _releaseSavePoint ($sSavePointName);
		return true;
	}

	/**
	 * public function lastInsertId
	 * Returns the last inserted ID
	 *
	 * @return integer
	 */
	public function lastInsertId () {
		return @$this -> _lastInsertId ();
	}

	/**
	 * public function close
	 * Close connexion
	 *
	 * @return integer
	 */
	public function close($bDestroyInstance = false) {
		$bReturn = @$this -> _close ();
		$this->rLink = null;
		if(true === $bDestroyInstance) {
			if(isset(aDBFactory::$_aInstance[get_class($this)][$this->sNameSpace])) {
				unset(aDBFactory::$_aInstance[get_class($this)][$this->sNameSpace]);
			} 
		}
		return $bReturn;
	}

	/**
	 * public function getIdentityColName
	 * Get table's identity column name
	 *
	 * @param string $sTable : table's name
	 * @return string
	 */
	public function getIdentityColName($sTable) {
		return @$this->_getIdentityColName($sTable);
	}

	/**
	 * public function getColumnProperties
	 * Get the table's columns properties, or the properties of a given column in this table
	 *
	 * @param string $sTable : table's name
	 * @param string $sColumn : column's name 
	 * @return array : associative array with columns properties
	 */
	public function getColumnProperties($sTable, $sColumn = null) {
		return @$this->_getColumnProperties($sTable, $sColumn);
	}
	
	/**
	 * public function freeResult
	 * Free requested query result resource
	 *
	 * @return boolean
	 */
	public function freeResult ($rQry = null) {
		if (is_null ($rQry)) {
			if (false === ($bRes = @$this->_freeResult())) {
				$this -> interceptException ('aDBExceptionDbConnectorErrors', str_replace ('{MSG}', $this -> _errorMsg (), aDBExceptionDbConnectorErrors::COULD_NOT_FREE_RESULT), aDBExceptionDbConnectorErrors::CODE_COULD_NOT_FREE_RESULT, get_class($this), __FUNCTION__);
			}
			$this->rQry = null;
		} else {
			if (false === ($bRes = @$this->_freeResult($rQry))) {
				$this -> interceptException ('aDBExceptionDbConnectorErrors', str_replace ('{MSG}', $this -> _errorMsg (), aDBExceptionDbConnectorErrors::COULD_NOT_FREE_RESULT), aDBExceptionDbConnectorErrors::CODE_COULD_NOT_FREE_RESULT, get_class($this), __FUNCTION__);
			}
		}
		return $bRes;
	}
	
	/**
	 * public function freeStatement
	 * Free requested statement resource
	 *
	 * @return boolean
	 */
	public function freeStatement ($rStmt = null) {

		if (is_null ($rStmt)) {
			if (false === ($bRes = @$this -> _freeStatement())) {
				$this -> interceptException ('aDBExceptionDbConnectorErrors', str_replace ('{MSG}', $this -> _errorMsg (), aDBExceptionDbConnectorErrors::COULD_NOT_FREE_STMT), aDBExceptionDbConnectorErrors::CODE_COULD_NOT_FREE_STMT, get_class($this), __FUNCTION__);
			}
			$this->rStmtLink = null;
		} else {
			if (false === ($bRes = @$this -> _freeStatement($rStmt))) {
				$this -> interceptException ('aDBExceptionDbConnectorErrors', str_replace ('{MSG}', $this -> _errorMsg (), aDBExceptionDbConnectorErrors::COULD_NOT_FREE_STMT), aDBExceptionDbConnectorErrors::CODE_COULD_NOT_FREE_STMT, get_class($this), __FUNCTION__);
			}
		}
		return $bRes;
	}
	
	/**
	 * public function __fetch
	 * public fetch method used by the Iterator
	 *
	 * @param resurce $rQry
	 * @param aDB Class Constant $aDB_MODE
	 * @return array
	 */
	public function __fetch ($rQry, $aDB_MODE) {
		return @$this -> _fetch ($rQry, $aDB_MODE);
	}

	/**
	 * public function __seek
	 * Seek method used by the Iterator
	 *
	 * @param integer $iOffset
	 * @return resource
	 */
	public function __seek ($iOffset) {
		return @$this -> _seek ($iOffset);
	}

	/**
	 * public function errorMsg
	 * Returns db api error message
	 *
	 * @return string
	 */
	public function errorMsg () {
		return $this -> _errorMsg ();
	}

	/**
	 * public function interceptException
	 * If EXCEPTION_ON_ERROR mode is active, throw an aDBException.
	 * If not, logs any error in the aDB::aErrorLogs array
	 *
	 * @param string $sErrorMsg
	 * @param string $sClass
	 * @param string $sFunction
	 */
	public function interceptException ($sExceptionClass, $sErrorMsg, $iCode, $sClass = 'unknown', $sFunction = 'unknown') {
		if (true === $this -> aOptions['EXCEPTION_ON_ERROR']) {
			throw new $sExceptionClass ($sErrorMsg, $iCode, $sClass, $sFunction);
		} else {
			aDB::$aErrorLog[] = $sClass.'::'.$sFunction.'() : '.$sErrorMsg.' ['.$iCode.']';
		}
	}

	/**
	 * public function server_info
	 * Gets server info if any
	 *
	 * @param resource $rQry
	 * @return mixed
	 */
	public function server_info ($rLink = null) {
		if (!is_null ($rLink)) {
			if (false === ($mInfos = @$this -> _server_info ($rLink))) {
				$this -> interceptException ('aDBExceptionDbConnectorErrors', aDBExceptionDbConnectorErrors::REQUEST_ERROR, aDBExceptionDbConnectorErrors::CODE_REQUEST_ERROR, get_class($this), __FUNCTION__);
				return false;
			}
			return $mInfos;
		} else {
			if (false === ($mInfos = @$this -> _server_info ($this ->rLink))) {
				$this -> interceptException ('aDBExceptionDbConnectorErrors', aDBExceptionDbConnectorErrors::REQUEST_ERROR, aDBExceptionDbConnectorErrors::CODE_REQUEST_ERROR, get_class($this), __FUNCTION__);
				return false;
			}
			return $mInfos;
		}
	}

	/**
	 * public function setOption
	 * Sets an existing option
	 *
	 * @param string $sOption
	 * @param mixed $mValue
	 */
	public function setOption ($sOption, $mValue) {
		if (false === array_key_exists ($sOption, $this -> aOptions)) {
			$this -> interceptException ('aDBExceptionInvalidClassCalls', str_replace ('{OPTION}', $sOption, aDBExceptionInvalidClassCalls::INVALID_OPTION), aDBExceptionInvalidClassCalls::CODE_INVALID_OPTION, get_class($this), __FUNCTION__);
			return false;
		}
		$this -> aOptions[$sOption] = $mValue;
		return true;
	}

	/**
	 * public function __get
	 * returns an authorized property value
	 *
	 * @param string $sProp
	 * @return mixed
	 */
	public function __get ($sProp) {
		switch ($sProp) {
			case 'QUERY':
				return $this -> sQuery;
				break;
			case 'ERRORS':
				return $this -> aErrorLog;
				break;
			case 'CONF':
				return $this -> aConConf;
				break;
			case 'NAMESPACE':
				return $this -> sNameSpace;
				break;
			case 'LAST_ERROR':
				if (is_array($this -> aOptions['EXCEPTION_ON_ERROR'])) {
					return end($this -> aErrorLog);
				}
				return false;
				break;
			default:
				$this -> interceptException ('aDBExceptionInvalidClassCalls', str_replace ('{PROP}', $sProp, aDBExceptionInvalidClassCalls::PROP_NOT_GETABLE), aDBExceptionInvalidClassCalls::CODE_PROP_NOT_GETABLE, get_class($this), __FUNCTION__);
				return false;
				break;
		}
	}

	abstract protected function _connect ($bOpenNewConnection = true);
	abstract protected function _errorMsg ();
	abstract protected function _select_db ($sDbName);
	abstract protected function _seek ($iOffset);
	abstract protected function _query ($sQuery);
	abstract protected function _fetch ($rQry, $aDB_MODE);
	abstract protected function _count ($rQry);
	abstract protected function _lastInsertId ();
	abstract protected function _startTransaction ($sTransacName = null);
	abstract protected function _commitTransaction ($sTransacName = null);
	abstract protected function _rollbackTransaction ($sTransacName = null);
	abstract protected function _countTransaction ();
	abstract protected function _escape ($sString);
	abstract protected function _server_info ($rLink);
	abstract protected function _freeResult ($rQry = null);
	abstract protected function _freeStatement ($rStmt = null);
	abstract protected function _savePoint ($sSavePointName);
	abstract protected function _rollbackToSavePoint ($sSavePointName);
	abstract protected function _releaseSavePoint ($sSavePointName);
	abstract protected function _close ();
	abstract protected function _getIdentityColName($sTable);
	abstract protected function _tableExists($sTable);
	abstract protected function _getColumnProperties($sTable, $sColumn = null);
	abstract protected function _call($sProcName);
	abstract protected function _bindParam($sParam, $mValue, $cType);
	abstract protected function _procExec();
	abstract protected function _nextResult($rQry);
}

class mssql extends aDB {

	protected function _connect ($bOpenNewConnection = true) {
		return @mssql_connect ($this -> aConConf['HOST'], $this -> aConConf['LOGIN'], $this -> aConConf['PWD'], $bOpenNewConnection);
	}

	protected function _errorMsg () {
		return @mssql_get_last_message();
	}

	protected function _select_db ($sDbName) {
		return @mssql_select_db ($sDbName, $this -> rLink);
	}

	protected function _seek ($iOffset) {
		return @mssql_data_seek ($this -> rQry, $iOffset);
	}

	protected function _query ($sQuery) {
		return @mssql_query ($sQuery, $this -> rLink);
	}

	protected function _fetch ($rQry, $aDB_MODE) {
		switch ($aDB_MODE) {
			case aDB::FETCH_ASSOC:
				$sMode = 'MSSQL_ASSOC';
				break;
			case aDB::FETCH_NUM:
				$sMode = 'MSSQL_NUM';
				break;
			default:
				$sMode = 'MSSQL_BOTH';
				break;
		}
		if (defined ('MSSQL_ASSOC')) {
			return @mssql_fetch_array ($rQry, constant ($sMode));
		} else {
			$aRes = @mssql_fetch_array ($rQry);
			if(false !== $aRes) {
				if ($sMode === 'MSSQL_NUM') {
					$aRes = self::phpBadGirl ($aRes, 0);
				}
				if ($sMode === 'MSSQL_ASSOC') {
					$aRes = self::phpBadGirl ($aRes, 1);
				}
			}
			return $aRes;
		}
	}

	protected static function phpBadGirl ($aTmp, $iMode) {
		$aNew = array ();
		if(is_array($aTmp)) {
			switch ($iMode) {
				case 0:
					foreach ($aTmp as $mK => $mV) {
						if (is_int ($mK)) {
							$aNew[$mK] = $mV;
						}
					}
					break;
				case 1:
					foreach ($aTmp as $mK => $mV) {
						if (is_string ($mK)) {
							$aNew[$mK] = $mV;
						}
					}
					break;
			}
		}
		if (empty ($aNew)) {
			return $aTmp;
		}
		return $aNew;
	}

	protected function _count ($rQry) {
		return @mssql_num_rows ($rQry);
	}

	protected function _lastInsertId () {
		$sQuery = 'SELECT @@IDENTITY';
		$rQry = $this -> query ($sQuery, false);
		return $this -> fetchColumn (0, $rQry, false);
	}

	protected function _startTransaction ($sTransacName = null)  {
		$sQuery = 'BEGIN TRANSACTION';
		if(!is_null($sTransacName)) {
			$sQuery .= ' '.$this->escape($sTransacName);
		}
		$rQry = $this -> query ($sQuery, false);
		return $rQry;
	}

	protected function _commitTransaction ($sTransacName = null)  {
		$sQuery = 'COMMIT TRANSACTION';
		if(!is_null($sTransacName)) {
			$sQuery .= ' '.$this->escape($sTransacName);
		}
		$rQry = $this -> query ($sQuery, false);
		return $rQry;
	}

	protected function _rollbackTransaction ($sTransacName = null)  {
		$sQuery = 'ROLLBACK TRANSACTION';
		if(!is_null($sTransacName)) {
			$sQuery .= ' '.$this->escape($sTransacName);
		}
		$rQry = $this -> query ($sQuery, false);
		return $rQry;
	}

	protected function _countTransaction () {
		$sQuery = 'SELECT @@TRANCOUNT';
		$rQry = $this -> query ($sQuery, false);
		return (int)$this -> fetchColumn (0, $rQry, false);
	}

	protected function _escape ($sString) {
		return @str_replace ("'", "''", $sString);
	}

	protected function _server_info ($rLink) {
		throw new aDBExceptionInvalidClassCalls(aDBExceptionInvalidClassCalls::UNSUPPORTED_METHOD, aDBExceptionInvalidClassCalls::CODE_UNSUPPORTED_METHOD, get_class($this), __FUNCTION__);
	}

	protected function _freeResult ($rQry = null) {
		if(!is_null($rQry) && is_resource($rQry) && get_resource_type($rQry) === 'mssql result') {
			return @mssql_free_result ($rQry);
		} elseif (is_resource($this->rQry) && get_resource_type($this->rQry) === 'mssql result') {
			return @mssql_free_result ($this->rQry);
		} else {
			return true;
		}
	}
	

	protected function _freeStatement ($rStmt = null) {
		if(!is_null($rStmt)) {
			return @mssql_free_statement ($rStmt);
		} else {
			return @mssql_free_statement ($this->rStmtLink);
		}
	}

	protected function _savePoint ($sSavePointName)  {
		$sQuery = 'SAVE TRANSACTION '.$this->_escape($sSavePointName);
		$rQry = $this -> query ($sQuery, false);
		return $rQry;
	}

	protected function _rollbackToSavePoint ($sSavePointName)  {
		$sQuery = 'ROLLBACK TRANSACTION '.$this->_escape($sSavePointName);
		$rQry = $this -> query ($sQuery, false);
		return $rQry;
	}

	protected function _releaseSavePoint ($sSavePointName)  {
		throw new aDBExceptionInvalidClassCalls(aDBExceptionInvalidClassCalls::UNSUPPORTED_METHOD, aDBExceptionInvalidClassCalls::CODE_UNSUPPORTED_METHOD, get_class($this), __FUNCTION__);
	}

	protected function _getIdentityColName($sTable) {
		$sQuery = 'SELECT TOP 1 IDENTITYCOL FROM '.$this->_escape($sTable);
		$rQry = $this -> query ($sQuery, false);
		return @mssql_field_name($rQry);
	}
	
	protected function _tableExists($sTable){
		$sQuery="SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'$sTable')";
		$rQry = $this -> query ($sQuery, false);
		if ($this->count($rQry)===0){
			return false;
		}
		return true;
	}
	
	protected function _getColumnProperties($sTable, $sColumn = null) {
		$sQuery = "SELECT syscolumns.name as colname, systypes.name, 
			syscolumns.length, syscolumns.isnullable, syscolumns.xprec, syscolumns.collation,
			syscolumns.iscomputed, syscolumns.colorder, syscolumns.autoval
			FROM dbo.systypes systypes
			INNER JOIN dbo.syscolumns syscolumns ON syscolumns.xtype = systypes.xtype
			WHERE syscolumns.id = OBJECT_ID(N'{$sTable}')";
		if(!is_null($sColumn)) {
			$sQuery.= " AND syscolumns.name = '{$sColumn}'";
		}
		$this -> query ($sQuery);
		return $this->fetchAll(aDB::FETCH_ASSOC);
	}

	protected function _close () {
		return @mssql_close ($this->rLink);
	}
	
	protected function _call($sProcName) {
		return @mssql_init($sProcName);
	}
	
	protected function _bindParam($sParam, $mValue, $cType) {
		switch($cType) {
			case aDB::PARAM_INT: 
			case 'integer':
				$sType = SQLINT4;
				break;
			case aDB::PARAM_STR: 
			case 'string':
				$sType = SQLVARCHAR;
				break;
			case aDB::PARAM_FLOAT: 
			case 'float':
				$sType = SQLFLT8;
				break;
			case aDB::PARAM_BOOL: 
			case 'boolean':
				$sType = SQLINT1;
				break;
			default: 
				$this -> interceptException ('aDBExceptionInvalidClassCalls', aDBExceptionInvalidClassCalls::PARAM_TYPE_NOT_FOUND, aDBExceptionInvalidClassCalls::CODE_PARAM_TYPE_NOT_FOUND, get_class($this), __FUNCTION__);
				break;
		}
		return @mssql_bind($this->rStmtLink, $sParam, $mValue, $sType);
	}
	
	protected function _procExec() {
		return @mssql_execute($this->rStmtLink);
	}
	
	protected function _nextResult($rQry) {
		if(function_exists('mssql_next_result')) {
			return @mssql_next_result($rQry);
		} else {
			return false;
		}
	}
	
}

class mysql extends aDB {

	protected function _connect ($bOpenNewConnection = true) {
		return @mysql_connect ($this -> aConConf['HOST'], $this -> aConConf['LOGIN'], $this -> aConConf['PWD']);
	}

	protected function _errorMsg () {
		return @mysql_error();
	}

	protected function _select_db ($sDbName) {
		return @mysql_select_db ($sDbName, $this -> rLink);
	}

	protected function _seek ($iOffset) {
		return @mysql_data_seek ($this -> rQry, $iOffset);
	}

	protected function _query ($sQuery) {
		return @mysql_query ($sQuery, $this -> rLink);
	}

	protected function _fetch ($rQry, $aDB_MODE) {
		switch ($aDB_MODE) {
			case aDB::FETCH_ASSOC:
				$sMode = 'MYSQL_ASSOC';
				break;
			case aDB::FETCH_NUM:
				$sMode = 'MYSQL_NUM';
				break;
			default:
				$sMode = 'MYSQL_BOTH';
				break;
		}
		return @mysql_fetch_array ($rQry, constant ($sMode));
	}

	protected function _count ($rQry) {
		return @mysql_num_rows ($rQry);
	}

	protected function _lastInsertId () {
		return @mysql_insert_id ($this -> rQry);
	}

	protected function _startTransaction ($sTransacName = null)  {
		$sQuery = 'START TRANSACTION';
		$rQry = $this -> query ($sQuery, false);
		return $rQry;
	}

	protected function _commitTransaction ($sTransacName = null)  {
		$sQuery = 'COMMIT';
		$rQry = $this -> query ($sQuery, false);
		return $rQry;
	}

	protected function _rollbackTransaction ($sTransacName = null)  {
		$sQuery = 'ROLLBACK';
		$rQry = $this -> query ($sQuery, false);
		return $rQry;
	}

	protected function _countTransaction ()  {
		throw new aDBExceptionInvalidClassCalls(aDBExceptionInvalidClassCalls::UNSUPPORTED_METHOD, aDBExceptionInvalidClassCalls::CODE_UNSUPPORTED_METHOD, get_class($this), __FUNCTION__);
	}

	protected function _escape ($sString) {
		return @mysql_real_escape_string ($sString, $this -> rLink);
	}

	protected function _server_info ($rLink) {
		return @mysql_get_server_info ($rLink);
	}

	protected function _freeResult ($rQry = null) {
		if(!is_null($rQry)) {
			return @mysql_free_result ($rQry);
		} else {
			return @mysql_free_result ($this->rQry);
		}
	}
	
	protected function _freeStatement ($rStmt = null) {
		//todo
		return false;
	}

	protected function _savePoint ($sSavePointName)  {
		$sQuery = 'SAVEPOINT '.$sSavePointName;
		$rQry = $this -> query ($sQuery, false);
		return $rQry;
	}

	protected function _rollbackToSavePoint ($sSavePointName)  {
		$sQuery = 'ROLLBACK TO SAVEPOINT '.$sSavePointName;
		$rQry = $this -> query ($sQuery, false);
		return $rQry;
	}

	protected function _releaseSavePoint ($sSavePointName)  {
		$sQuery = 'RELEASE SAVEPOINT '.$sSavePointName;
		$rQry = $this -> query ($sQuery, false);
		return $rQry;
	}

	protected function _getIdentityColName($sTable) {
		throw new aDBExceptionInvalidClassCalls(aDBExceptionInvalidClassCalls::UNSUPPORTED_METHOD, aDBExceptionInvalidClassCalls::CODE_UNSUPPORTED_METHOD, get_class($this), __FUNCTION__);
	}
	
	protected function _tableExists($sTable){
		$sQuery="SHOW TABLES LIKE '$sTable'";
		$rQry = $this -> query ($sQuery, false);
		if ($this->count($rQry)===0){
			return false;
		}
		return true;
	}
	
	protected function _getColumnProperties($sTable, $sColumn = null) {
		// todo
		return false;
	}

	protected function _close () {
		return @mysql_close ($this->rLink);
	}
	
	protected function _call($sProcName) {
		//todo
		return false;
	}
	
	protected function _bindParam($sParam, $mValue, $cType) {
		//todo
		return false;
	}
	
	protected function _procExec() {
		//todo
		return false;
	}
	
	protected function _nextResult($rQry) {
		//todo
		return false;
	}
}
?>

 Conclusion

Quelques modifications suite aux remarques de Coucou et de Fhx.
Sachant que je n'ai pas encore poussé la notion d'abstraction comme voulu par FhX, mais j'y viens... :-)

20071228 : Je viens d'uploader une nouvelle version. Il y a une régression : cette version est désormais compatible entièrement PHP5.0.4, et plus 5.1. Pour des besoins pro, je n'ai amélioré que cette version.
Etant donné qu'elle a pas mal évoluée, je décide de remplacer la précedente.
Parmi les nouveautés :
- un meilleur support des transactions
- ajout d'un paramètre de récupération des données : aDB::FETCH_GROUP_UNIQUE, je vous laisse le découvrir ;-)
- Ajout de plusieurs méthodes :
aDB::tableExists() vérifie l'existance d'une table
aDB::getIdentityColName() renvoie le nom de la colonne "Identity" d'une table
aDB::getColumnProperties() renvoie les propriétés d'une ou toutes les colonnes d'une table (type, valeur par défaut, nom, ordre, collation, etc...) => dispo uniquement pour mssql (j'utilise principalement ce serveur DB, mais j'implémenterai mysql un de ces 4...sinon, vous pouvez vous y coller :-) ).
- grosse modif de l'usinage : un nouveau paramètre fait son apparition, ne modifiant pas le comportement si vous utilisez déjà l'ancienne classe. $mIsSingleton : par défaut, ce paramètre est fixé à true. Son comportement est alors le même qu'avant : vous avez un pur singleton. Si vous le fixez à false, il n'y a plus de singleton du tout, on renvoie systématiquement une nouvelle instance, MAIS toutes les instances créées sont stockées dans un tableau (je dois encore implémenter la méthode permettant de récupérer une instance donnée dans ce tableau); enfin, si vous lui donnez une autre valeur (toute valeur acceptée en tant que clef d'un tableau), vous créez une sorte d'espace de nom : si une instance du même type avec le même espace de nom existe, elle est retournée; sinon, elle est créée. Le but ? utiliser des instances différentes par serveur de DB par exemple et par base, mais réutiliser la même pour un type et un serveur DB donné, simplement.
- aDB::bindValue() a été modifiée de manière à être plus stable. Lier des colonnes telles que user_nom et user_nom_fact est désormais possible (avant, le str_replace() empêchait cette utilisation...ceux qui ont essayé savent de quoi je parle, les autres s'en foutent et ne buteront jamais sur le problème grâce à cette mise à jour).
- et pas mal d'autres détails à découvrir!

 Fichier Zip

Les Membres Club peuvent télécharger directement un fichier contenu dans le zip sans télécharger le zip en entier !

Télécharger le zip


 Historique

23 mai 2007 18:45:56 :
Modif mineurr
24 mai 2007 13:02:05 :
grosses modifications : ajout d'ue classe d'usinage avec posibilité de se connecter à plusieurs serveurs de bdd, répartition des Exceptions.
24 mai 2007 14:50:07 :
Petits ajouts d'exemples
24 mai 2007 15:13:41 :
Nouvelle petite modif...
24 mai 2007 15:14:01 :
Nouvelle petite modif...
24 mai 2007 15:28:33 :
Ajouts de commentaires
24 mai 2007 17:22:45 :
Modifications et rajout d'une documentation (en français :-) ).
24 mai 2007 18:01:24 :
Modifs sur l'itérateur
24 mai 2007 18:11:55 :
Grr...
24 mai 2007 18:13:07 :
grr 2
25 mai 2007 09:37:27 :
Correction des bugs Itérateur
25 mai 2007 09:52:08 :
Ajout du caractère optionnel du 3ème argument pour aDB::bindValue()
25 mai 2007 18:09:23 :
Petites modifications encore.
28 mai 2007 16:57:41 :
Ajout de deux nouvelles méthodes : aDB::freeResult (), qui libère la mémoire d'un résultat...et aDB::next_limit () qui met à jour la limite : une sorte de pagination intégrée.
28 mai 2007 17:45:01 :
Ajout de méthodes concernant les points d'enregistrement dans une transaction
28 décembre 2007 19:37:28 :
Grosse, groooooosse mise à jour.
16 février 2008 14:06:28 :
Ajout du support des procédures stockées Ajout du support des jeux de résultats multiples Ajout de quelques méthodes utilitaires Amélioration de la méthode bindValue() Ces ajouts ont été faits essentiellement sur la version mssql, étant donné que c'est celle que j'utilise le plus souvent.

 Sources du même auteur

Source avec Zip ASTUCES/HACK PHP
SQUELETTE DE GESTION DES DROITS
[PHP 5.1] CLASS STRING : NOUVEL EXEMPLE SUR LA SPL
Source avec Zip Source avec une capture [PHP 5.1] PHOTOPHOP (PHPDRAW 2)
Source avec Zip Source avec une capture [PHP5.1] O-LOC : CLASSE ET BACKOFFICE D'INTERNATIONALISATION

 Sources de la même categorie

Source avec Zip RECHERCHE DES DOUBLONS DANS UNE TABLE MYSQL EN SÉLECTIONNANT... par aladec2007
[CRON] INSERT ON DUPLICATE KEY UPDATE par pierreSabatier
Source avec Zip CLASS MYSQL 5/PHP5 AVEC GESTION DES EXCEPTION ET DES REQUÊTE... par devil_may_cry
Source avec Zip Source avec une capture MOTEUR DE RECHERCHE SUR BASE MYSQL FULLTEXT ET BOOLEAN AVEC ... par cod57
SYSTÈME DE PAGINATION AVEC BDD ET IMAGES (FICHIERS) par begueradj

 Sources en rapport avec celle ci

Source avec Zip Source avec une capture PAGINATION + FICHIER CSS par profdi
Source avec Zip Source avec une capture CONVERTIR BASE FIREBIRD EN MYSQL par castelfrederic29
Source avec Zip COUCHE D'ABSTRACTION DE LA BASE DE DONNÉES ET GESTION ORIENT... par vanadium1
Source avec Zip CLASSE BASE DE DONNEES , EX AVEC MYSQL & MSSQL [PHP5 & PHP4] par malalam
Source avec Zip Source avec une capture NEWSLETTER AVEC BASE DE DONNÉE par piranhaworld

Commentaires et avis

Commentaire de FhX le 23/05/2007 22:56:49

Ca me rappèle bien des choses :)
J'avais fais un truc "similaire" (quoi que...), disons pour l'abstraction en tout cas mdr.

Pour ce que j'en pense :
- Je trouve l'itérateur un peu bizzare.
En effet, je m'attendais à voir dans ::Next() quelque chose qui fasse un fetch() dans ton objet de bdd. Hors, c'est le ::current() qui le fait chez toi.
C'est une grosse contradiction, car si j'appèle ::current() plusieurs fois, j'ai passé plusieurs tours de fetching alors que ton iKey n'a pas bougé d'un poil. De plus, sur un "foreach ( $ton_iterateur as $key=>$val ) ... ", je ne suis pas sur qu'il marche. Je dis bien que je n'en suis pas sur, je n'ai pas essayé.

Je trouve ca bizzare c'est tout :)
Ah non, ce que je trouve bizarre en faite, c'est ca :
"results are fetched through an iterator wich returns variables corresponding to columns name (just like an extract)"

Ca me fait un peu peur ^^

- Concernant l'abstraction.
La aussi, j'emets quelques rétissance. D'abord pour la duplication de méthodes internes. C'est ce que j'avais fait il y a un temps, mais je me suis appercu du mauvais coté.
Ce que tu fais de cette abstraction, ce n'est que de la vérification. En réalité, ce que tu devrais faire, c'est de l'agregation d'objet. Explication :

Classe abstraite SQL
+ $var - lien de connection sql.
+ $var - nom d'hote, user, password et nom de la base de donnée
+ method() - Constructeur public
+ method() - connection à la base de donnée
+ method() - fermeture de la base de donnée

Interface iSQL
+ method() - Exécute la requète
+ method() - Prépare une requète
+ method() - libère la mémoire d'une requète


Classe concrète MySQL étendant SQL implémentant iSQL
+ method() - return mysql_query($sql)
+ method() - return new Query_Prepare($sql)
+ method() - mysql_free_result($query)

Classe concrète MSSQL étendant SQL implémentant iSQL
+ method() - return mssql_query($sql)
+ method() - return new Query_Prepare($sql)
+ method() - mssql_free_result($query)


Class concrète "d'abstraction" SQL implémentant iSQL
+ $var - objet contenant la classe SQL voulu
+ method() - construct( SQL $object )
+ method() - query($sql) ==> $this->object->query($sql); // vérification avant et après
+ method() - prepare($sql) ==> $this->object->prepare($sql); // Vérification avant et après
...etc...

A l'instanciation :
$db = new abstraction( new MySQL );
$db2 = new abstraction( new MSSQL );

La classe "d'abstraction" se chargera de faire la même chose que ton abstraction à toi :)
Et si je n'ai pas envie de cette "abstraction", je la vire :)
Je vois les choses sous cet angle, car tu ne fais pas une vrai abstraction en réalité.


- Tu ne peux traiter qu'une query à la fois.

- Je rêve, mais j'aimerai pouvoir faire un jour :
$objet = new mysql;  
echo $objet->prepare('SELECT.....', $var1, $var2)->query()->fetch_object()->getMachinFromTruc(...)->getMachinChose();

Chose impossible sur ta classe :)


Cependant, c'est pas mal, ainsi je noterai 9.


Non 10, tout est propre :)

Commentaire de coucou747 le 24/05/2007 04:56:59 administrateur CS

j'aime bien le bindValue, le prepare et autre, ca rappelle le dbi du perl :) euh, par contre, pour bindValue, pourquoi ne pas utiliser gettype pour le dernier parametre ?

c'est joli, propre, OO, proposer des exeptions differentes pour les differentes fonctions permet d'avoir plusieurs catch qui font des choses differentes, ici si je ne me trompe pas (il est 5h du matin, alors pardonnez mes erreurs), tu renvois toujours les memes exeptions...

sinon, proposer un cache ca peut etre interessant, un bench, ou autre...

Donc concretement, ca permet d'utiliser des resultats de requettes comme un tableau, dans ce cas, on pourrait renvoyer un array et non un objet... (ouais ca fait moins OO, et ? ca n'est pas une erreur...)

Ca permet de ne pas avoir d'injections de codes disons malveillants... ok, c'est un avantage.
Ca permet de manipuler n'importe quel type de bdd sans pour autant s'ennuyer avec des "pointeurs sur fonctions", oui, c'est un fait...

Sinon, pourquoi ne pas faire une class genre Factory et faire la connexion comme on la ferait en java ou en perl ?
a=new SqlConnection('mysql://user:pass@Bdd');

histoire d'avoir vraiment juste une variable a changer et pas une fonction lorseque l'on change de base.... (ouais, balader un String dans un fichier de conf, c'est plus propre que de balader un nom d'objet...)

Commentaire de malalam le 24/05/2007 08:10:55 administrateur CS

Hello,

déjà, merci à tous les deux :-)
Maintenant, je vais tâcher d'expliquer quelques points car n'ayant pas fait de doc encore, certaines fonctionnalités restent obscures je pense :-)).
FhX => je n'ai tjrs pas pu réparer mon pc perso...mais je n'oublie pas ce que je t'ai promis (si t'en as tjrs besoin depuis le temps), dès que je le retrouve (au taf, je ne peux décemment pas lol).
L'itérateur : il fonctionne très bien. Je comprends ta réticence, mais j'ai testé les deux façons de faire : l'actuelle, et faire le fetch dans le next, le stocker dans une propriété, et renvoyer cette propriété dans le current. Ce qui m'a géné, dans ce cas, c'est que si je décide de traverser mon itérateur avec un while au lieu d'un foreach, je dois alors faire un next AVANT de faire un current...et pas à la fin de chaque boucle while comme habituellement. C'était trop étrange à mon goût...
Extract : attention...ça n'a pas de rapport direct avec l'itérateur. C'est juste un paramètre, le aDB::FETCH_EXTRACT qui permet de récupérer les valeurs de cette manière. Mais on peut aussi fonctionner "normalement" via aDB::BOTH, aDB::FETCH_ASSOC etc...et retrouver un tableau associatif, numérique, les deux...Par contre, c'est dangereuyx en effet, et à utiliser en connaissance de cause. Comme un extract (), ou le PDOStatement::bindColumn() avec PDO::FETCH_BOUND.
Pour ta notion d'abstraction, c'est effectivement une jolie idée...je vais me pencher dessus.

Coucou => Tu ne te trompes pas pour les exceptions, il faudrait que je répartisse un peu tout ça, en effet, et que j'appelle différents types d'exceptions en fonction des erreurs trouvées.
Le cache, j'y pense. Le bench, je ne suis pas pour, je préfère utiliser ça indépendemment de toute classe : comme ça, j'ai une classe bench, que j'utilise où je le veux. Du coup, en ajouter une à ces classes les alourdirai inutilement.
Selon la méthode utilisée, la classe renvoie un tableau (un vrai lol, c'est aDB::fetchAll()) ou un itérateur (un objet donc, c'est aDB::fetch), ou même une simple valeur (aDB::fetchColumn).
L'usinage, je n'en voyais pas non plus l'intérêt dans ma classe : je ne veux pas de singleton (j'utilise souvent plusieurs objets db distincts). Ceci dit, on ne change pas de méthode pour se connecter à des bases différentes là ? Ce sera tjrs la même méthode et les mêmes paramètres à passer.
Mais je vais quand même revenir sur cette histoire d'usine...je pense. On verra.

Commentaire de coucou747 le 24/05/2007 08:42:21 administrateur CS

je veux me connecter a odbc, mysql, acces avec le meme objet... c'est pas possible avec ton code, c'est la ou factory entre en scene... (imagine, tu fais un gestionnaire de forum, et tu veux qu'il puisse gerer tout type de BDD juste en changeant un fichier de conf, le fichier de conf ne contenant que des define et autre...) voila ce qui peut te motiver a faire une factory...

(Moi je place ca dans mes signets, voir si ca evolue, si ca evolue bien, je veux dire avec les differentes exeptions, et autres ameliorations, je dois dire que j'aurais du mal a faire mieux... du moins en partant de 0 ca serait trop long, alors pourquoi ne pas utiliser ca si un jours j'ai une grosse usine a mettre en place...)

Commentaire de kankrelune le 24/05/2007 15:07:35

Wouaw... que dire... beau boulot... .. .

Je n'ai pas eu le temps de regarder le code en détail je ne le commenterais donc pas mais de ce que j'ai vu c'est plus que propre... .. .

Pour commenter les commentaires (lol) précédant je pense que l'idée d'une factory est plutot bonne... on a pas souvent besoin de se connecter à plusieurs serveur mais ça peut arriver et dans ce cas une factory simplifie quand même les choses... dans la même idée j'opterais plus pour un multiton qu'un singleton... ça permet des connections à différents serveur mais également à différentes bases de données... .. .

"(Moi je place ca dans mes signets, voir si ca evolue, si ca evolue bien, je veux dire avec les differentes exeptions, et autres ameliorations, je dois dire que j'aurais du mal a faire mieux... du moins en partant de 0 ca serait trop long, alors pourquoi ne pas utiliser ca si un jours j'ai une grosse usine a mettre en place...)"

+1

10/10... .. . ;o)

@ tchaOo°

Commentaire de malalam le 24/05/2007 15:16:43 administrateur CS

Merci :-)
J'ai fait pas mal d'ajouts cette fois : l'usinage avec multiton (comme quoi, Kankrelune...t'as raison, c'était une très bonne idée), les différentes exceptions...et des exemples pour utiliser les différentes exceptions justement.
reste tjrs à bosser sur l'abstraction :-)

Commentaire de FhX le 24/05/2007 17:26:12

"FhX => je n'ai tjrs pas pu réparer mon pc perso...mais je n'oublie pas ce que je t'ai promis (si t'en as tjrs besoin depuis le temps), dès que je le retrouve (au taf, je ne peux décemment pas lol)."
Oh, c'est quoi déja ?? ;)

"Ce qui m'a géné, dans ce cas, c'est que si je décide de traverser mon itérateur avec un while au lieu d'un foreach, je dois alors faire un next AVANT de faire un current...et pas à la fin de chaque boucle while comme habituellement. C'était trop étrange à mon goût..."
Ah vi, sauf qu'habituellement tu fais :

$i=0;
while ( $i < 100 } {
        echo $i;
        $i++;
}

donc, tu as posé le schéma type :

$iterateur = new iterateur(.....);
while ( $iterateur->valid() ) {
         $contenu = $iterateur->current();
         echo $contenu[...].$contenu[...];
         $iterateur->next();
}

On voit bien ici que le ::current() doit retourner le contenu de l'itérateur "en fonction du compteur interne de l'itérateur". Ce compteur est incrémenté par ::next(). Hors, dans ta classe d'itération, c'est ::current() qui se charge de la récupération des données MAIS SANS prendre en compte le compteur interne.
Si j'utilise ta classe et que je fais :
$iterateur = new iterateur(.....);
while ( $iterateur->valid() ) {
         $contenu = $iterateur->current();
         echo $contenu[...].$contenu[...];
         $contenu2 = $iterateur->current();
         echo $contenu2[...].$contenu2[...];
         $iterateur->next();
}
On voit bien que $contenu et $contenu2 contiennent des données différentes AU LIEU de contenir la même chose. Mais le ::next() en haut de la boucle te gène, où est donc le problème ? Regarde, c'est ici :

$i=0; // <============================= ICI
while ( $i < 100 } {
        echo $i;
        $i++;
}

Et voui, j'ai initialisé le contenu de ma variable avant la boucle.
Hors chez toi :
$iterateur = new iterateur(.....);
// <======== la clé devrait valoir -1 (0 étant le premier enregistrement), et la propriété stockant la ressource courante est de type null.
while ( $iterateur->valid() ) {
         $contenu = $iterateur->current();
         echo $contenu[...].$contenu[...];
         $iterateur->next();
}

Pour que cela fonctionne correctement, tu dois faire ceci :
$iterateur = new iterateur(.....);
$iterateur->next();
while ( $iterateur->valid() ) {
         $contenu = $iterateur->current();
         echo $contenu[...].$contenu[...];
         $iterateur->next();
}

Cela implique à ta classe d'itération quelques changements que voici : (c'est un schéma type)

class IterateMe extends Iterator {
private $key;
private $query;
private $output;

public function __construct($query) {
  $this->key = -1;
  $this->query = $query;
  $this->output = null;
}
public function valid() {
  return ( isset($this->output) || false !== $this->output);
}
public function next() {
  $this->key++;
  $this->output = mysql_fetch_****($query);
}
public function current() {
  return $this->output;
}
//etc...

}

Cette classe marche ensuite comme ceci :
// foreach
foreach ($it as $key=>$val) {
   // $val sortant de ::current();
   // next étant exécuté à chaque itération de boucle
}

// while
$it->next();
while ( $it->valid() ) {
       echo $it->current();
       echo $it->current();
       $it->next();
}



Vala vala :)


Pour l'abstraction, je vais faire un ptit truc ce week end. Je te filerai ca exclusivement :p

Commentaire de FhX le 24/05/2007 17:26:48

Mince, j'ai dis ni bonjour, ni aurevoir, ni rien.....



Vala c'est fait :D

Commentaire de malalam le 24/05/2007 18:03:01 administrateur CS

Ok ok, en effet, c'est plus joli de toute manière.
ce que j'ai fait, c'est que j'appelle automatiquement sqlIterator::next() dans le constructeur. Comme ça, pas la peine de l'appeler avant la boucle while () (ce qui n'est décidément pas naturel pour moi).

Commentaire de FhX le 24/05/2007 18:15:35

"ce que j'ai fait, c'est que j'appelle automatiquement sqlIterator::next() dans le constructeur."

Fait attention, car si tu utilises foreach(), regarde ce que cela fait :
--->instanciation itérateur
---> Appel de foreach() sur l'objet itérateur
  | --> Rewind()
  | --> next()
  | --> valid()
---> current() + key().

Si tu mets next() dans ton constructeur, tu vas récupérer le premier enregistrement. Hors, sur un foreach(), rewind() est appelé en début de boucle, donc tu vas "perdre" le bénéfice du next() dans ton constructeur.

Tu as le choix dans ton rewind(), soit tu t'en sers, soit tu t'en sers pas. Dans le cas ou tu t'en sert (donc ==> mysql_seek(0)), tu vas effectivement perdre un appel de méthode. Si tu ne t'en sers pas, tu vas "sauter" un enregistrement.

Vala, un ptit rappel :p

Commentaire de kankrelune le 24/05/2007 18:35:14

Perso je suis comme FhX... je vois pas en quoi l'appel de next() avant un while() dérange... hormis une ligne de code en plus c'est comme initialiser une variable avant un while()... .. .

@ tchaOo°

Commentaire de coucou747 le 24/05/2007 19:22:00 administrateur CS

"J'ai fait pas mal d'ajouts cette fois : l'usinage avec multiton (comme quoi, Kankrelune...t'as raison, c'était une très bonne idée)" => usinage et factory c'est pas pareil ?

$myDb = aDBFactory::getInstance ('mysql', $aCon); j'aurais vraiment prefere
$myDb = aDBFactory::getInstance ('mysql://login:pass@host:port/basededonnee');


public function bindValue ($sNeedle, $mValue, $cType) {
t'es pas decide a mettre ce troisieme parametre optionel ?

Commentaire de malalam le 25/05/2007 08:23:10 administrateur CS

Kankrelune => Ok ok lol je me rends. De tte manière FhX a raison, je n'avais pas pensé au foreach, qui plante effectivement, en l'état.
je verrai ça dans la journée (je vais pas tarder à aller au taf là)

Coucou => Si si, j'ai dit Kankrelune parce que c'était le dernier message. Mais j'ai réflêchi à ce que tu m'as dit concernant plusieurs connexions et effectivement l'usinage était la meilleure solution pour ça. On doit même pouvoir pousser le concept, et je suis sûr que t'as plein d'idées pour ça ;-)
Pour la manière de se connecter, je laisserai peut-être le choix entre ce que tu proposes, et la manière "traditionnelle", pourquoi pas, oui. je n'ai pas eu le temps de me pencher dessus, c'est tout :-) Mais j'avais bien noté ce que tu disais.
Idem pour le bindValue : j'ai bien noté et c'est une très bonne idée (même si ça va causer des erreurs, mais ça...charge à l'utilisateure de faire attention à son typage!). Je vais l'implémenter. Mais j'ai passé pas mal de temps à bosser sur les exceptions, à corriger quelques bugs, et sur cet itérateur. Je pense avoir le temps aujourd'hui.

Toute suggestion est la bienvenue, vu que ce package, je vais l'utiliser, moi, alors autant qu'il soit sympa à utiliser et pratique ;-)

Commentaire de malalam le 25/05/2007 08:23:39 administrateur CS

Zou, allons au taf...:-(

Commentaire de coucou747 le 25/05/2007 08:55:51 administrateur CS

//java
Class.forName("org.gjt.mm.mysql.Driver").newInstance();
connection=DriverManager.getConnection("jdbc:mysql://localhost/database","root","");
en perl :
$dbh = DBI->connect("DBI:mysql:database","root","");
en openoffice.org basic, j'ai pas le code sous les yeux (en fait je ne l'ai plus depuis un triste formatage) mais ca ressemblait au code java :)
a partir de ca, j'en conclue qu'il n'existe pas de facon traditionelle de se connecter a une BDD...
en perl, ca donne un truc comme ca :
$sth = $dbh->prepare("SELECT LAST_INSERT_ID()");
$sth->execute();
($id) =$sth->fetchrow();
$sth->finish();
et si t'as un parametre, tu mets un '?' dans la requette et une variable a execute alors perl est ok un vieux langage, mais tu vois, cette classe dbi, c'est geant...
sinon t'as
$rows_affected = $dbh->do("...") || die "Database Error: $DBI::errstr";
principale probleme du perl, j'ai rien vu au sujet des exeptions...

Commentaire de codefalse le 25/05/2007 10:28:18 administrateur CS

Yop !
Alors deja, si on pouvait mettre 11 sur 10, je l'aurai fait !
Vraiment impressionné !
Bon, j'ai du relire 3 fois tes 1400 lignes histoire de bien tout comprendre (j'ai un peu de retard sur la poo ...) mais c'est bien rentré !

Par contre, quelques questions que je me doit de poser pour mieux comprendre :)
Ta classe d'exception (aDBException), se limite au sgbd. Est-ce une volontée de ta part pour cette partie ou est-ce que ca restera comme ca pour tout ton projet ?
Dites moi si je me trompe, mais ne serait-il pas plus interessant de faire une classe de gestion d'exception globale, et ensuite heriter les erreures comme tu le fait, mais spécialisé pour chaque usages ? (x classes héritant de l'exception globale pour la db, x classes héritant de l'exception globale pour la classe xxx, etc). Bon si c'est une volontée de ta part juste pour te limiter au db, ok :)

Par contre, dans ta classe abstraite aDB, tu utilise interceptException pour envoyer des exceptions.
L'utilité de cette fonction est juste pour la condition (EXCEPTION_ON_ERROR mode is active) alors on genere une nouvelle erreur ? J'ai bien compris ?
Dans cette fonction interceptException, pourquoi utilise tu l'usinage alors que ton appel envers la classe aDBException reste toujours pareil ?
car tu fait
#  $e = new ReflectionClass($sExceptionClass);
# throw $e -> newInstance ($sErrorMsg, $iCode, $sClass, $sFunction);

ca pourrait être pareil que faire
# throw new $sExceptionClass ($sErrorMsg, $iCode, $sClass, $sFunction);
Je me trompe ? (c'est probable !! :p)

Sinon une petite note, dans le commentaire descriptif du constructeur de la classe class aDBException extends Exception, tu a oublié le parametre $iCode
(j'ai pas passé tous tes commentaires en revue, mais je trouvé celui la :p)

Voila voila !

Commentaire de guill76 le 26/05/2007 00:02:05

Nickel très grande classe.
great!!!
Just a little mistake=> "UNLIMITED LOOP" when using a wrong dbname in the factory method.
Sinon l'emploi des combinaisons pour le fetchall est pas mal: ça me fait penser au systeme du chmod sous unix.
  

Commentaire de malalam le 26/05/2007 08:12:07 administrateur CS

Coucou => par "traditionnelle", j'entends la façon que l'on voit le plus fréquemment dans les classes DB en PHP, à savoir un tableau de config ou des variables séparées host, login, pwd, db.
Mais évidemment, je biens d'accord sur le fait qu'il n'y a pas de règle générale pour ça.
PDO est calqué sur DBI, donc je vois très bien ce que tu veux dire. Oui c'est une excellente implémentation! Mais tout n'est pas très facile à simuler, notamment les marqueurs (avec les bindValue, bindParam, bindColumn et compagnie).Je n'ai pas non plus calqué le principe de la classe DBhandler ($dbh), et la classe Statementhandler ($sth), mais j'y songe aussi...
Enfin, ok, je réflêchirai à une manière de passer la config dans une chaîne, façon DBI ou PDO :-)

Codefalse => merci, déjà :-)
Généralement, en effet, on étend un tas d'exceptions spécialisées dans les classes du package que l'on crée. Bref, oui, c'est bien une volonté de ma part. Cela permet de gérer correctement les exceptions derrière avec différents catch. Ceci dit, rien n'empêche d'avoir aussi des exceptions plus génériques hein. Mais il faudrait alors les inclure dans tous les packages créés...autant la jouer spécialisé. Je vais les scinder un peu plus encore, c'est loin d'être suffisemment précis, en l'état, d'ailleurs.
En effet,le but est de ne lever les exceptions que si l'option EXCEPTION_ON_ERROR est active. Et pour la méthode aDB::interceptException(), tu as parfaitement raison. J'étais parti sur une tout autre façon de gérér cette méthode au départ, et l'utilisation de la réflexion est un reste malencontreux de ces essais :-) Je l'ai enlevée depuis.
Je corrigerai le commentaire, merci :-)

Guill76 => merci :-)
Par contre, je ne te suis pas sur l'erreur ? Tu tombes vraiment dans une boucle infinie quand tu entres un mauvais type de db dans l'usine?  Je ne vois pas comment ?

Commentaire de kankrelune le 26/05/2007 11:35:31

"Enfin, ok, je réflêchirai à une manière de passer la config dans une chaîne, façon DBI ou PDO :-)"

Vite fait comme ça, en reprenant l'exemple de coucou (sgdb://login:pass@host:port/db), je dirais un truc du genre...

preg_match('~^([[:lower:]]+)://(\w+):(\w+)@(\w+)(?::([0-9]+))?/([\w-.]+)$~', $connectStr, $connectInfos);

@ tchaOo°

Commentaire de guill76 le 26/05/2007 13:58:49

Ouais, mais sur le nom de la base de données pas sur le type :
j'ai essayé avec mysql et une exception est lancée à priori dans un foreach:
pour t'aider j'ai ce message:
Connexion failed with message [Unknown database 'wrongdb']mysql :: select_db() : [0] Connexion failed with message [Unknown database 'wrongdb']

mysql :: select_db() : [0] Connexion failed with message [Unknown database 'wrongdb']
mysql :: select_db() : [0] Connexion failed with message [Unknown database 'wrongdb']
mysql :: select_db() : [0] Connexion failed with message [Unknown database 'wrongdb']
mysql :: select_db() : [0] Connexion failed with message [Unknown database 'wrongdb']

Commentaire de guill76 le 26/05/2007 14:13:21

Sinon une autre remarque importante pour ta classe :
Elle n'est malheureursement pas compatible avec les db non transactionnelles.
Toutes tes methodes transactionnelles sont abstraites :
J'aurais bien vu une implémentation de 2 types de Db (les transactionnelles et les autres) sinon ça oblige à implémenter les methodes de transaction même pour les bases non transactionnelles.

Commentaire de FhX le 26/05/2007 19:18:35

"J'aurais bien vu une implémentation de 2 types de Db (les transactionnelles et les autres) sinon ça oblige à implémenter les methodes de transaction même pour les bases non transactionnelles."

abstract class x {
abstract public function truc();
}
class y extends x {
abstract public function truc();
}

Rien ne t'empèche de ne rien mettre dedand :)

Commentaire de malalam le 28/05/2007 09:11:31 administrateur CS

Guill76 => désolé, mais je ne reproduis pas le bug. Et je ne vois pas comment il est possible vu qu'aucune exception n'est lancée dans une boucle.
Sauf si tu as utilisé ma dernière fonction dans les exemples, où, là, c'est possible puisque c'est ujne fonction qui fait de la récursivité en cas d'erreur. Auquel cas ce n'est pas un bug, mais une mauvaise utilisation de l'exemple ;-)
Peux-tu me montrer ton code ?
Pour les transactions, même remarque que FhX. Ca n'a pas réellement d'incidence.

Kankrelune : why not! Je vais la tester, merci :-)

Commentaire de guill76 le 28/05/2007 19:36:33

Voici le code qui boucle inifiment
$test = aDBFactory::getInstance ('mysql', array ('HOST' =>'localhost', 'LOGIN' => 'login', 'PWD' => 'pwd', 'DB' => 'wrongdb'));

Sinon pour les transactions, je sais qu'on peut contourner comme toi et FHX le dites, mais ce qui me gêne c'est que pour une classe de 30 methodes de transactions ça fait redéfinir 30 mèthodes à vide (bon là on est grosso modo à 5 methodes de transactions donc ça peut aller :)).

PS Malalam au passage : Si t'as l'occasion de rejeter un oeil à la source calendrier html que j'ai postée:
un avis d'expert en itérateurs me serait bien utile : j'ai fait une bonne Mise à jour.

Commentaire de marc1306 le 23/12/2007 19:15:32 10/10

Parfait quel beau travail !! peu de sources sont aussi récentes à jour et propre . thanks

Commentaire de malalam le 28/12/2007 19:38:45 administrateur CS

Merci Marc :-)
Je viens de faire une très grosse mise à jour : aucune incompatibilité avec les versions précédentes (vous pôuvez conserver sans les modifier les codes utilisant cette classe), mais un tas de nouveautés et d'améliorations par contre :-)

Commentaire de FhX le 04/01/2008 00:41:45

Sinon pour les transactions, je sais qu'on peut contourner comme toi et FHX le dites, mais ce qui me gêne c'est que pour une classe de 30 methodes de transactions ça fait redéfinir 30 mèthodes à vide (bon là on est grosso modo à 5 methodes de transactions donc ça peut aller :)).


Dans ce cas :

abstract class db {
}

class Transactiondb extends db {
}

class NonTransactiondb extends db {
}

Et puis roule ma poule :)

Commentaire de guill76 le 04/01/2008 19:42:47

Oui, C'était bien là que je voulais en venir!!

Commentaire de malalam le 04/01/2008 20:01:52 administrateur CS

On pourrait effectivement faire ça. Mais en même temps, des bdd non transactionnelles...heu...tu penses auxquelles?

Commentaire de guill76 le 05/01/2008 13:30:19

ben SQLITE, mysql <v4, etc..

Commentaire de guill76 le 05/01/2008 13:54:10

Oh une autre remarque importante sauf erreur de ma part, pour la connexion:

l'autre jour, je me suis heurté à un problème en créant plusieurs instances de db  (ce n'était pas avec cette classe mais j'ai l'impression qu'une multi instanciation peut causer le même problème ).

la fonction mysql_connect doit à tout prix utiliser le flag true ou false pour permettre la création d'une nouvelle ressource de connexion à MYSQL.
Donc il faut que ce soit paramétrable :la possibilité pour ta classe d'utiliser le multiton ne suffit pas dans ce cas..:)

Commentaire de malalam le 05/01/2008 16:09:16 administrateur CS

Avant c'était un singleton, c'est normal ;-) C'était intentionnel. Et j'ai en effet modifié l'usine depuis pour permettre 3 comportements distincts.


SQLite gère les transactions, et mysql 3...ça date sérieusement quand même. On pourrait oui mais très sncèrement je ne le ferai pas sur ma classe, ce serait quelques classes de plus pour pas grand chose.

Commentaire de guill76 le 07/01/2008 20:02:29

Ok mais je crois que si tu instancies 2 db de même type mais differentes ta classe se comporte comme un singleton même avec l'option multiton.
un exemple pour mieux me faire comprendre:

$configMysqlDb1=array('HOST'=>'localhost','LOGIN'=>'root','PWD'=>'xxx',
              'DB'=>'information_schema');

$configMysqlDb2=array('HOST'=>'localhost','LOGIN'=>'root','PWD'=>'xxx',
              'DB'=>'db_blindtest');

//exemple en multiton:
$instDb1=aDBFactory::getInstance('mysql',$configMysqlDb1,false);
print_r($instDb1);
//voir la propriété rlink
$instDb2=aDBFactory::getInstance('mysql',$configMysqlDb2,false);
print_r($instDb2);
//voir la propriété rlink

=>tu constateras que $instDb1->rLink = $instDb2->rLink donc ta classe doit se connecter avec le parametre true dans mysql_connect pour vraiment être un multiton

Commentaire de malalam le 07/01/2008 20:20:38 administrateur CS

Non, tu as 3 possibilités :
- singleton de base en fonction du type de db.
- pas de singleton du tout, quoi qu'il se passe : on crée toujours une nouvelle instance (il manque à implémenter une méthode permettant de récupérer une instance créée plus tôt vu que je les stocke).
- singleton avec "espace de nom" : on passe un "espace de nom", et un type de db évidemment. Si l'usine trouve dans ses instances une instance avec le même espace de nom pour ce type de DB, elle le renvoie, sinon elle l'instancie, le stocke, et retourne l'instance.
Le paramètre est $mIsSingleton, il est à true par défaut (singleton "typé" db), si on le passe à false, pas de singleton du tout, si on passe une chaîne ou toute valeur scalaire, c'est le 3ème cas (la valeur passée étant "l'espace de nom").
Dans l'exemple que tu me donnes, je suis très surpris car chez moi cela fonctionne très bien. Et le code étant le suivant :
#  self::$_instance[$saDBType] = new $saDBType ($aConConf, $aOptions);
#  return self::$_instance[$saDBType];
j'avoue ne pas comprendre comment tu peux te retrouver avec le même objet au final...c'est très bizarre. Tu es sur quelle version de PHP, il y a peut-être une subtilité que je n'aurais pas remarquée sur une des versions de PHP (j'ai découvert il y a 2 jours ceci :
http://www.php.net/manual/en/language.oop5.late-static-bindings.php
donc bon...il y a toujours moyen qu'on zappe une nouveauté ;-) ) (au passage, lisez, c'est une GROSSE nouveauté, TRES intéressante, parce que ce **tain de problème, je m'y suis heurté un paquet de fois...!).

Bref, j'aimerais creuser ton truc là, mais j'ai besoin de toi lol, vu que je ne le reproduis pas.


Commentaire de guill76 le 07/01/2008 20:48:08

D'accord mais oublie la notion singleton/multiton point de vue POO.
pense plutot à 2 ressources de db mysql telle qu'elles sont implémentés pas PHP.

Pour moi tout se joue dans mysql_connect()
resource mysql_connect  ([ string $server  [, string $username  [, string $password  [, bool $new_link  [, int $client_flags  ]]]]] )

le param $new_link n'est pas défini dans ta classe donc le mysql_connect dès son premier appel, te récupéres la ressource existante si elle existe.
Chose qui ne va pas de paire avec la notion singleton multiton POO.
J'ai testé ce comportement indépendament de ta classe : Mais je peux t'assurer que ta classe le reprend :

Regarde bien le ressource_id generé par mysql_connect : c'est le même.

Commentaire de malalam le 07/01/2008 20:55:53 administrateur CS

Ok je comprends mieux : tu es sur le même serveur DB avec la même connexion, donc par défaut, il reprend la même ressource, en effet, c'est logique. Je ne l'avais jamais remarqué parce que je ne l'ai jamais testé de cette manière : ou plutôt, utiliser la même ressource ne me pose pas de problème si je suis sur la même connexion.  Et généralement, j'utilise ce multiton sur différents serveurs, ou/et avec différentes connexions.
Ok, la solution est de passer dans le tableau de configuration le booléen $new_link, en le mettant à false par défaut si il n'est pas renseigné. Comme les fonctions idoines.
Je ferai ça, très bonne remarque :-)

Commentaire de caviar le 18/01/2008 17:25:43

saluté ... euh comment dire ... tu parlais d'un fichier d'exemple pour utiliser ta classe ... disons que j'ai un peu du mal à comprendre son fonctionnement du haut de mon faible niveau mais j'aimerai bien comprendre comment l'utiliser ... sur des trucs de base hein ...
connexion à la base, select, update, gestion de transaction etc... mais là je nage un peu sans exemple quand même ...
@+

Commentaire de malalam le 18/01/2008 20:15:24 administrateur CS

Hello Caviar,

ok...ce week-end, je te fais un autre fichier plus clair :-) D'autant plus que la classe a bcp évolué et pas le fichier d'exemples.

Commentaire de malalam le 29/01/2008 19:52:15 administrateur CS

@Caviar => je n'ai pas oublié hein...mais je suis entrain de modifier cette classe depuis quelques temps, j'aoute des fonctionnalités : support des procédures, support des requêtes renvoyant plusieurs jeux de résultats; etc. Du coup, j'attends de finaliser ça avant de faire un tuto complet et simple :-)

 Ajouter un commentaire


Discussions en rapport avec ce code source dans le forum

PDO serveur BDD distant [ par TheGorgo ] Bonjour, Je cherche à me connecter à un serveur de base de données distante en utilisant Doctrine. Lorsque je veux générer mon schèma de données, j'a update ne change pas ma table [ par dallelism ] salut;voici mon code qu'est faire un update de ma table.le pb est toute est juste la connection à la bdd est faite,la requette affiche qu'elle est cor problème pour migrer une BDD mysql [ par BencoAndCo ] Bonjour, j'ai fait la migration d'une base de données MySQL pour cela j'ai créé une requete permettant de reconstruire les tables de la bdd sur une Changements "automatiques" d'une BDD [ par Papymuzo ] Bonjour, Je suis confronté à un problème dont je ne trouve pas de solution. J'ai une table (nommons-la table1) avec trois champs (ID, DATE et STATUT Formulaire multi-critères [ par kryow ] onjour à tous, Je sollicite votre aide pour un projet sur lequel je travaille depuis un certain temps...sans succès ! [size=300][b][u]Le "Cahier des Récupérer chiffre dans case Bdd [ par guilstunt ] Bonjour a tous !! Je débute dans le php et je bloque sur un point malgré mes recherches. J'arrive a créer une bdd, de me connecter, de rentré des in Créer un tableau a partir de ma BDD [ par guilstunt ] Bonjour, Je souhaiterai créer un tableau avec 3 colonnes a partir de ma bdd Voici mon code <html xmlns="http://www.w3.org/1999/xhtm notification modification bdd [ par wallysub ] Bonjour, j'ai un site internet php/BDD mysql, et je souhaiterai que l'utilisateur puisse être alerté lorsque la base de donnée est modifiée. J'ai donc probleme avec pdo_mysql.dll [ par dossa ] salut, je travaille avec xampp qui combine apache 2.2.11, php5.2.8, mysql 5.1.30 le problème est lorsque l'exécute une requète avec pdo apache se plan could not find driver PDO avec wamp [ par dossa ] j'ai une problème avec la connexion avec mysql et pdo c'est la première fois que j'utilise wamp5 j'ai la classe suivante dans pdo1.php: class PDO2 e


Nos sponsors


Sondage...

Comparez les prix

CalendriCode

Février 2012
LMMJVSD
  12345
6789101112
13141516171819
20212223242526
272829    

Consulter la suite du CalendriCode

Photothèque

 
Développement réalisé par Nicolas SOREL (Nix) avec l'aide de : Cyril DURAND et Emmanuel (EBArtSoft), Merci à Vincent pour ses précieux conseils.
CodeS-SourceS.com© Toute reproduction même partielle est interdite sauf accord écrit du Webmaster
CodeS-SourceS.com© est une marque déposée tous droits réservés

Google Coop CodeS-SourceS Google Coop CodeS-SourceS
Temps d'éxécution de la page : 1,732 sec (4)

Nous contacter | Annoncer sur CodeS-SourceS | Mentions légales