<?php
/**
 * Façade permettant de simplifier l'accès aux objets de connexion par des méthodes statiques
 *
 * Cet objet permet d'accéder directement aux méthodes begin, query, queryTab, queryRow, commit, rollback
 * en passant le nom de la connection
 *
 * @version 2.0
 * @author Sébastien ROMMENS
 * @package Sesamath
 * @subpackage Database
 * @since Thu Apr 13 10:28:49 CEST 2006
 */
// Classe de gestion des connexions aux pools (on doit rester compatible 5.2
// pour les vieilles RHEL qui traînent encore, donc pas de __DIR__)
require(dirname(__FILE__) .'/DB/DB_Manager.class.php');

class DB
{
    const ERRTYPE_EXCEPTION = 'exception';
    const ERRTYPE_SILENT    = 'silent';
    const ERRTYPE_WARNING   = 'warning';

    const DEBUG_TYPE_FILE = 'file';
    // fonctionnait avec https://github.com/ccampbell/chromephp, à remettre en route si besoin */
    //const DEBUG_TYPE_CONSOLE = 'console';
    const DEBUG_TYPE_SCREEN = 'screen';

    /**
     * Constructeur privé
     */
    private function __construct() {

    }

    /**
     * Permet d'exécuter une requête.
     *
     * Aucun résultat n'est renvoyé par cette fonction. Elle doit être utilisé pour effectuer
     * des insertions, des updates... Elle est de même utilisée par les
     * autres fonctions de la classe comme queryRow() et queryTab().
     *
     * @param string $connection_name nom de la connection définie dans le fichier de configuration
     * @param string $query reqête SQL où les valeurs sont désignées par :ma_variable, sans apostrophe/guillemet (quoting ajouté au besoin)
     * @param mixed $param variables bind de type array(":bind"=>"value")
     * @return $res
     *   Le résultat de la méthode query sur l'objet PDO
     */
    public static function query($connection_name, $query, $param = "") {
        $connection = null;
        try {
            $databaseManager = DB_Manager::getInstance();
            $connection = $databaseManager->getConnexion($connection_name);
            if (is_object($connection)) {
                $time_start = microtime(true);
                $res = $connection->query($query, $param);
                DB_Manager::debug($connection, microtime(true) - $time_start);
                return $res;
            }
        } catch (DatabaseException $e) {
            DB_Manager::handleError($connection, $e);
            return false;
        }
    }

    /**
     * Permet d'exécuter une requête devant renvoyer une seule ligne de résultat.
     *
     * @param string $connection_name nom de la connection définie dans le fichier de configuration
     * @param string $query reqête SQL où les valeurs sont désignées par :ma_variable, sans apostrophe/guillemet (quoting ajouté au besoin)
     * @param mixed $param variables bind de type array(":ma_variable"=>"sa_valeur")
     * @return array
     *   Un tableau associatif à 1 niveau (champs ou alias du select comme clés), FALSE si erreur
     */
    public static function queryRow($connection_name, $query, $param = "") {
        $connection = null;
        try {
            $databaseManager = DB_Manager::getInstance();
            $connection = $databaseManager->getConnexion($connection_name);
            $time_start = microtime(true);
            $rs = $connection->queryRow($query, $param);
            DB_Manager::debug($connection, microtime(true) - $time_start);
            return $rs;
        } catch (DatabaseException $e) {
            DB_Manager::handleError($connection, $e);
            return false;
        }
    }

    /**
     * Retourne tous les résultats sous forme d'un tableau (une liste de lignes, chacune étant la liste des champs)
     *
     * @param string  $connection_name  Nom de la connection définie dans le fichier de configuration
     * @param string  $query            Requête SQL
     * @param array   $param            (facultatif) Paramètres de la requete, format array(":nomParam" => "value")
     * @param boolean $indexkey         (facultatif) Si true, ajoute la valeur de la 1re colonne comme 1er indice,
     *                                  le 2e étant un index et le 3e les autres valeurs
     * @param boolean $indexkey_is_uniq (facultatif) Si true (et $indexkey==true) alors la clé sera considérée comme unique
     *                                  (le tableau renvoyé n'aura donc que 2 niveaux au lieu de 3 en virant cette 2e clé d'index)
     * @return array Le tableau des résultats
     *
     * Exemples :
     * - queryTab($conn, "SELECT a, b, c FROM t1") renverra
     * [0 => [a0, b0, c0]]
     * [1 => [a1, b1, c1]]
     * - queryTab($conn, "SELECT a, b, c FROM t1", true) renverra
     * [a0 => [0 => [b0,c0]]]
     * - queryTab($conn, "SELECT a, b, c FROM t1", true, true) renverra
     * [a0 => [b0,c0]]
     */
    public static function queryTab($connection_name, $query, $param = array(), $indexkey = false, $indexkey_is_uniq = false) {
        $connection = null;
        try {
            $databaseManager = DB_Manager::getInstance();
            $connection = $databaseManager->getConnexion($connection_name);
            $time_start = microtime(true);
            $rs = $connection->queryTab($query, $param, $indexkey, $indexkey_is_uniq);
            DB_Manager::debug($connection, microtime(true) - $time_start);
            return $rs;
        } catch (DatabaseException $e) {
            DB_Manager::handleError($connection, $e);
            return false;
        }
    }

    /**
     * Retourne la 1re colonne des résultats
     *
     * @param string $connection_name Nom de la connection définie dans le fichier de configuration
     * @param string $query           Requête SQL
     * @param array  $param           Paramètres de la requete, format array(":nomParam" => "value")
     * @return array La liste des valeurs de la 1re colonne du résultat de la requête
     */
    public static function queryCol($connection_name, $query, $param = "") {
        $connection = null;
        try {
            $databaseManager = DB_Manager::getInstance();
            $connection = $databaseManager->getConnexion($connection_name);
            $time_start = microtime(true);
            $rs = $connection->queryTab($query, $param);
            // on remplace chaque ligne du tableau par la valeur d'indice 0 qu'elle contient
            foreach ($rs as &$val) {
                $val = array_shift($val);
            }
            DB_Manager::debug($connection, microtime(true) - $time_start);
            return $rs;
        } catch (DatabaseException $e) {
            DB_Manager::handleError($connection, $e);
            return false;
        }
    }

    /**
     *
     * Permet d'exécuter une requête devant renvoyer une seule chaine de résultat.
     *
     * @param string $connection_name nom de la connection définie dans le fichier de configuration
     * @param string $query chaine SQL
     * @param mixed $param variables bind de type array(":bind"=>"value")
     * @return mixed
     */
    public static function queryOne($connection_name, $query, $param = "") {
        $connection = null;
        try {
            $databaseManager = DB_Manager::getInstance();
            $connection = $databaseManager->getConnexion($connection_name);
            $time_start = microtime(true);
            $rs = $connection->queryRow($query, $param);
            DB_Manager::debug($connection, microtime(true) - $time_start);
            return array_shift($rs);
        } catch (DatabaseException $e) {
            DB_Manager::handleError($connection, $e);
            return false;
        }
    }

    /**
     * Initialise le début d'une transaction et passe l'autocommit à false
     *
     * @param string $connection_name nom de la connection définie dans le fichier de configuration
     * @return boolean
     */
    public static function begin($connection_name) {
        $connection = null;
        try {
            $databaseManager = DB_Manager::getInstance();
            /* @var $connection DatabaseInterface */
            $connection = $databaseManager->getConnexion($connection_name);
            return $connection->beginTransaction();
        } catch (DatabaseException $e) {
            DB_Manager::handleError($connection, $e);
            return false;
        }
    }

    /**
     * Commit des requêtes exécutées (query() lance les $statement->exec(),
     * mais aussi les commit si DB_driver_PDO->autoCommit vaut true
     * (ce qui est le cas par défaut, mis à false dans begin)
     *
     * @param string $connection_name nom de la connection définie dans le fichier de configuration
     * @return boolean
     */
    public static function commit($connection_name) {
        $connection = null;
        try {
            $databaseManager = DB_Manager::getInstance();
            $connection = $databaseManager->getConnexion($connection_name);
            return $connection->commit();
        } catch (DatabaseException $e) {
            DB_Manager::handleError($connection, $e);
            return false;
        }
    }

    /**
     * Rollback des requêtes exécutées
     *
     * @param string $connection_name nom de la connection définie dans le fichier de configuration
     * @return boolean
     */
    public static function rollback($connection_name) {
        $connection = null;
        try {
            $databaseManager = DB_Manager::getInstance();
            $connection = $databaseManager->getConnexion($connection_name);
            return $connection->rollback();
        } catch (DatabaseException $e) {
            DB_Manager::handleError($connection, $e);
            return false;
        }
    }

    /**
     * Permet de récupérer l'id du dernier objet inséré dans la base, si la requête est de type INSERT
     *
     * @param string $connection_name nom de la connection définie dans le fichier de configuration
     * @return mixed
     */
    public static function getLastOid($connection_name) {
        $connection = null;
        try {
            $databaseManager = DB_Manager::getInstance();
            $connection = $databaseManager->getConnexion($connection_name);
            return $connection->getLastOid();
        } catch (DatabaseException $e) {
            DB_Manager::handleError($connection, $e);
            return false;
        }
    }

    /**
     * Permet de récupérer le nombre d'enregistrements affectés par la dernière requete
     * ATTENTION, une requete update qui ne fait rien car les nouvelles valeurs sont identiques
     * aux anciennes renverra toujours 0 !
     * @param string $connection_name nom de la connection définie dans le fichier de configuration
     * @return int le nb de lignes
     */
    public static function rowCount($connection_name) {
        $connection = null;
        try {
            $databaseManager = DB_Manager::getInstance();
            $connection = $databaseManager->getConnexion($connection_name);
            return $connection->rowCount();
        } catch (DatabaseException $e) {
            DB_Manager::handleError($connection, $e);
            return false;
        }
    }

    /**
     * Pour récupérer le code d'erreur de la dernière requête exécutée sur cette connexion
     * @param $connection_name
     * @return string Le code d'erreur en char(5) ou null si pas d'erreur
     */
    public static function errorCode($connection_name) {
            $databaseManager = DB_Manager::getInstance();
            $connection = $databaseManager->getConnexion($connection_name);

            return $connection->errorCode();

    }

    /**
     * Permet de fermer une connexion à la base de données (ne sert que dans des cas bien préçis, ne pas utiliser si pas necessaire)
     *
     * @param string $connection_name Nom de la connexion définie dans le fichier de configuration
     */
    public static function close($connection_name) {
        $connection = null;
        try {
            $databaseManager = DB_Manager::getInstance();
            return $databaseManager->closeConnexion($connection_name);
        } catch (DatabaseException $e) {
            DB_Manager::handleError($connection, $e);
            return false;
        }
    }

    /**
     * Retourne le type d'erreur de la connexion
     * @param string $connection_name Nom de la connexion définie dans le fichier de configuration
     * @return string exception|silent|warning
     */
    public static function getErrorType($connection_name) {
        $connection = null;
        try {
            $databaseManager = DB_Manager::getInstance();
            $connection = $databaseManager->getConnexion($connection_name);

            return $connection->error_type;
        } catch (DatabaseException $e) {
            DB_Manager::handleError($connection, $e);

            return false;
        }
    }

    /**
     * Affecte le type d'erreur de la connexion (utiliser les constantes ERRTYPE_* de cette classe)
     * @param string $connection_name Nom de la connexion définie dans le fichier de configuration
     * @param string $error_type      exception|silent|warning (si autre sera mis à warning)
     * @return boolean
     */
    public static function setErrorType($connection_name, $error_type) {
        $connection = null;
        $retour = false;
        try {
            $databaseManager = DB_Manager::getInstance();
            $connection = $databaseManager->getConnexion($connection_name);
            $possibles = array(self::ERRTYPE_EXCEPTION, self::ERRTYPE_WARNING, self::ERRTYPE_SILENT);
            if (in_array($error_type, $possibles)) {
                $connection->error_type = $error_type;
                $retour = true;
            } else {
                $connection->error_type = self::ERRTYPE_WARNING;
                trigger_error("type d'erreur $error_type non géré par " .__CLASS__);
            }
        } catch (DatabaseException $e) {
            DB_Manager::handleError($connection, $e);
        }

        return $retour;
    }

    /**
     * Retourne le debug_type de la connexion
     * @param $connection_name
     * @return false
     * @throws DatabaseException
     */
    public static function getDebugType($connection_name) {
        $connection = null;
        try {
            $databaseManager = DB_Manager::getInstance();
            $connection = $databaseManager->getConnexion($connection_name);
            return $connection->debug_type;
        } catch (DatabaseException $e) {
            DB_Manager::handleError($connection, $e);
            return false;
        }
    }

    /**
     * Affecte le debug_type (qui log toutes les requêtes quand il est actif)
     * @param string $connection_name
     * @param string $debug_type à choisir parmi "" (désactive le debug) ou "file" (la constante DEBUG_LOG doit alors exister) ou "screen"
     * @return bool
     * @throws DatabaseException
     */
    public static function setDebugType($connection_name, $debug_type) {
        $connection = null;
        $retour = false;
        try {
            $databaseManager = DB_Manager::getInstance();
            $connection = $databaseManager->getConnexion($connection_name);
            // le type self::DEBUG_TYPE_CONSOLE marche plus
            $possibles = array('', self::DEBUG_TYPE_FILE, self::DEBUG_TYPE_SCREEN);
            if (in_array($debug_type, $possibles)) {
                $connection->debug_type = $debug_type;
                $retour = true;
            } else {
                $connection->debug_type = '';
                trigger_error("debug_type $debug_type non géré par " .__CLASS__);
            }
        } catch (DatabaseException $e) {
            DB_Manager::handleError($connection, $e);
        }

        return $retour;
    }

    /**
     * Retourne true si une transaction est en cours sur la connexion $connection_name
     * @param type $connection_name Nom de la connexion définie dans le fichier de configuration
     * @return boolean|null Null si la récupération de la connexion plante
     */
    public static function isTransactionStarted($connection_name) {
        $connection = null;
        try {
            $databaseManager = DB_Manager::getInstance();
            $connection = $databaseManager->getConnexion($connection_name);

            return $connection->transaction;
        } catch (DatabaseException $e) {
            DB_Manager::handleError($connection, $e);

            return null;
        }
    }

    /**
     * Affecte le nb de traces à mettre dans le log des requetes (toutes connexions)
     * @param int $nb passer 0 pour supprimer les traces, null pour tout garder ou le nb voulu (négatif pour la fin de la pile)
     */
    public static function setNbTraces() {
        DB_Manager::setNbTraces();
    }
}
