<?php

class dbSqlManager {
    private $type = 'mysql';
    private $availableType = array('mysql', 'sqlite', 'pgsql');
    private $db = '';
    private $user = '';
    private $pwd = false;
    private $host = 'localhost';
    private $dns = false;
    private $opt = array(
        PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8",
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
    );
    
    private $cnx = false;
    
    public function setDbConf($db_type, $db_name, $db_user=false, $db_pwd=false, $db_host='localhost', $db_opt=false) {
        $availableType = array('mysql', 'sqlite', 'pgsql');
        $this->type = (in_array($db_type, $availableType)) ? $db_type : false;
        $this->db = ($db_name!="") ? $db_name : false;
        $this->user = ($db_user!="") ? $db_user : false;
        $this->pwd = ($db_pwd!="") ? $db_pwd : false;
        $this->host = ($db_host!="") ? $db_host : false;
        if(is_array($db_opt)) $this->opt = $db_opt;
        
        $this->dns = false;
        if($this->type && $this->db) {
            switch($this->type) {
                case 'mysql':
                    if(!$this->user) return "miss user";
                    $this->dns = 'mysql:host='.$this->host.';dbname='.$this->db;
                break;
                case 'sqlite':
                    $this->dns = 'sqlite:'.$this->db;
                break;
                case 'pgsql':
                    if(!$this->user || $this->pwd) return "miss user & pwd";
                    $this->dns = 'pgsql:host='.$this->host.';port=5432;dbname='.$this->db.';user='.$this->user.';password='.$this->pwd;
                break;
                default:
                    return "miss type";
                break;
            }
            
            return true;
        }
        
        return "miss type & db";
    }
    
    public function connect() {
        if(!$this->dns) return "no conf";
        
        try {
            switch($this->type) {
                case 'mysql':
                    if($this->pwd && $this->opt) $this->cnx = new PDO($this->dns,$this->user,$this->pwd,$this->opt);
                    else if($this->pwd) $this->cnx = new PDO($this->dns,$this->user,$this->pwd);
                break;
                case 'sqlite':
                    if(!file_exists(realpath($this->db))) throw new PDOException("the sqlite file does not exists !");
                    $this->cnx = new PDO($this->dns, '', '');
                break;
                case 'pgsql':
                    $this->cnx = new PDO($this->dns);
                break; 
                default: return "miss type"; break;
            };
        } catch ( PDOException $e ) {
            return "Echec de connection à la base de donnée : ".$e->getMessage();
        }
        
        return true;
    }
    
    public function disconnect() {
        $this->cnx = null;
        $this->cnx = false;
    }
    
    public function getTablesList() {
        $sql = "SHOW TABLES FROM ".$this->db;
        
        $r = $this->select($sql);
        if($r['erreur']!==false) return $r;
        
        $result = array();
        foreach($r['datas'] as $t) {
            $result[] = $t[array_key_first($t)];
        }
        
        return array('datas' => $result, 'erreur' => false, 'sql' => $sql);
    }
    
    public function select($sql,$fetchFisrt=false) {
        if(!$this->cnx) return array('datas' => array(), 'erreur' => 'no db connection', 'sql' => $this->cnx);
        
        $result = array();
        $erreur = false;
        try {
            $sth = $this->cnx->prepare($sql);
            if(!$sth) throw new PDOException("PDO error => sth : ".$this->getReadableVar($sth));
            
            $result = $sth->execute();
        
            if(!$result) {
                $e = $sth->errorInfo();
                $erreur = $e[2];
            }
            else {
                if($fetchFisrt) $result = $sth->fetch(PDO::FETCH_ASSOC);
                else $result = $sth->fetchAll(PDO::FETCH_ASSOC);
            }
        } 
        catch ( PDOException $e ) {
            $erreur = $e->getMessage();
        }
        return array('datas' => $result, 'erreur' => $erreur, 'sql' => $sql);
    }
    
    public function makeInsertSql($table,$infos,$quote="`") {
        $sql = "INSERT INTO ".$table." (";
        $values = ") VALUES (";
        $datas = array();
        
        foreach($infos as $key => $val) {
            if(is_null($val)) continue;
            $sql.=$quote.$key.$quote.",";
            $values.=":".$key.",";
            if(is_string($val)) $datas[$key] = stripslashes($val);
            else $datas[$key] = $val;
        }
        
        $sql = substr($sql,0,-1).substr($values,0,-1).")";
        
        return array('sql' => $sql, 'datas' => $datas, 'print_sql' => $this->parms($sql,$datas));
    }
    
    public function insert($table,$infos,$quote="`") {
        if(!$this->cnx) return array('result' => false, 'id' => 0, 'erreur' => 'no db connection', 'sql' => '');
        
        $result = array();
        $erreur = false;
        $id = false;
        
        // MAKE SQL
        $sql_datas = $this->makeInsertSql($table,$infos,$quote);
        $sql = $sql_datas['sql'];
        $datas = $sql_datas['datas'];
        
        // EXEC SQL
        try {
            $sth = $this->cnx->prepare($sql);
            if(!$sth) throw new PDOException("PDO error => sth : ".$this->getReadableVar($sth));

            $this->cnx->beginTransaction(); 
            $result = $sth->execute($datas);
            
            if(!$result) {
                $e = $sth->errorInfo();
                $erreur = $e[2];
            }
            else {
                if($this->type == "pgsql") {
                    $id = $sth->fetch(PDO::FETCH_ASSOC);
                    $id = $id['id'];
                }
                else $id = $this->cnx->lastInsertId();
            }
            
            $this->cnx->commit();
        } 
        catch ( PDOException $e ) {
            $erreur = $e->getMessage();
        }
        
        return array('result' => $result, 'id' => $id, 'erreur' => $erreur, 'sql' => $this->parms($sql,$datas));
        
    }
    
    public function makeUpdateSql($table,$infos,$conditions,$quote="`") {
        $sql = "UPDATE ".$table." SET ";
        $datas = array();
        
        foreach($infos as $key=>$val) {
            $sql.=$quote.$key.$quote."=:".$key.", ";
            if(is_string($val)) $datas[$key] = stripslashes($val);
            else if(is_null($val)) $datas[$key] = NULL;
            else $datas[$key] = $val;
        }
        
        $sql = substr($sql,0,-2)." ".$conditions;
        
        return array('sql' => $sql, 'datas' => $datas, 'print_sql' => $this->parms($sql,$datas));
    }
    
    public function update($table,$infos,$conditions,$quote="`") {
        if(!$this->cnx) return array('result' => false, 'erreur' => 'no db connection', 'sql' => '');
        
        $result = false;
        $erreur = false;

        // MAKE SQL
        $sql_datas = $this->makeUpdateSql($table,$infos,$conditions,$quote);
        $sql = $sql_datas['sql'];
        $datas = $sql_datas['datas'];
        
        // EXEC SQL
        try {
            $sth = $this->cnx->prepare($sql, array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY));
            if(!$sth) throw new PDOException("PDO error => sth : ".$this->getReadableVar($sth));

            $this->cnx->beginTransaction(); 
            $result = $sth->execute($datas);
            
            if(!$result) {
                $e = $sth->errorInfo();
                if(!is_null($e[2])) $erreur = $e[2];
                else if (!is_null($e[0])) $erreur = $e[0];
                else $erreur = $e;
            }
            
            $this->cnx->commit(); 
        } 
        catch ( PDOException $e ) {
            $erreur = $e->getMessage();
        }
        
        return array('result' => $result, 'erreur' => $erreur, 'sql' => $this->parms($sql,$datas));
    }
    
    public function deleteById($id,$table,$id_name_val='id',$quote="`") {
        // MAKE CONDITION
        $conditions = "WHERE ".$quote.$id_name_val.$quote."=";
        if(is_string($id)) $conditions .= "'".stripslashes($id)."'";
        else $conditions .= $id;
        
        // EXEC SQL
        return $this->deleteWithCondition($table,$conditions);
    }
    
    public function deleteWithCondition($table,$conditions) {
        if(!$this->cnx) return array('result' => false, 'erreur' => 'no db connection', 'sql' => '');
        
        $result = false;
        $erreur = false;
        
        // MAKE SQL
        $sql = "DELETE FROM ".$table." ".$conditions;
        
        // EXEC SQL
        try {
            $sth = $this->cnx->prepare($sql);
            if(!$sth) throw new PDOException("PDO error => sth : ".$this->getReadableVar($sth));

            $this->cnx->beginTransaction(); 
            $result = $sth->execute();
            
            if(!$result) {
                $e = $sth->errorInfo();
                if(!is_null($e[2])) $erreur = $e[2];
                else if (!is_null($e[0])) $erreur = $e[0];
                else $erreur = $e;
            }
            
            $this->cnx->commit(); 
        } 
        catch ( PDOException $e ) {
            $erreur = $e->getMessage();
        }
        
        return array('result' => $result, 'erreur' => $erreur, 'sql' => $sql);
    }
    
    public function execSql($sql) {
        if(!$this->cnx) return array('result' => false, 'erreur' => 'no db connection', 'sql' => $sql);
        
        $result = false;
        $erreur = false;

        try {
            $this->cnx->beginTransaction(); 
            
            $result = $this->cnx->exec($sql);
            
            if($result!==false) $result = true;
            else {
                $e = $this->cnx->errorInfo();
                throw new PDOException("PDO error => ".$e[2]);
            }
            
            $this->cnx->commit(); 
        } 
        catch ( PDOException $e ) {
            $erreur = $e->getMessage();
        }

        return array('result' => $result, 'erreur' => $erreur, 'sql' => $sql);
    }
    
    public function executeSql($sql) {
        if(!$this->cnx) return array('result' => false, 'erreur' => 'no db connection', 'sql' => $sql);
        
        $result = false;
        $erreur = false;

        try {
            $sth = $this->cnx->prepare($sql);
            if(!$sth) throw new PDOException("PDO error => sth : ".$this->getReadableVar($sth));

            $this->cnx->beginTransaction(); 
            $result = $sth->execute();
            
            if(!$result) {
                $e = $sth->errorInfo();
                if(!is_null($e[2])) $erreur = $e[2];
                else if (!is_null($e[0])) $erreur = $e[0];
                else $erreur = $e;
            }
            
            $this->cnx->commit();
        } 
        catch ( PDOException $e ) {
            $erreur = $e->getMessage();
        }

        return array('result' => $result, 'erreur' => $erreur, 'sql' => $sql);
    }
    
    public function quote($value) {
        if(!$this->cnx) return false;
        return $this->cnx->quote($value);
    }
    
    private function parms($string,$data) {
        $indexed=$data==array_values($data);
        foreach($data as $k=>$v) {
        if(is_string($v)) $v="'$v'";
            if($indexed) $string=preg_replace('/\?/',$v,$string,1);
            else $string=str_replace(":$k",$v,$string);
        }
        return $string;
    }
    
    private function afficheArray($array,$echo=false,$indent=20) {
        if(!is_array($array)) return "NOT AN ARRAY !".(($echo) ? "<br/>" : "");
    
        $htmlTxt = "array(";
        if(count($array)==0) return "array()";
        foreach($array as $key => $val) {
            $htmlTxt.="<br/><span style='margin-left:".$indent."px'>";
            $htmlTxt.= (is_string($key)) ? "'".$key."'" : $key;
            $htmlTxt.= " = ";
            $htmlTxt.= (is_array($val)) ? $this->afficheArray($val, false, ($indent+20), false) : $this->getReadableVar($val);
            $htmlTxt.= ",</span>";
        }
        $htmlTxt = substr($htmlTxt,0,-8).'</span><br/>';
        $htmlTxt.="<span style='margin-left:".($indent-20)."px'>)</span>";

        if($echo) {
            echo($htmlTxt."<br/>");
            return;
        }

        return $htmlTxt;
    }
    
    private function getReadableVar($var) {
        if(is_array($var)) return $this->afficheArray($var,false,20);
        elseif(is_bool($var)) return ($var) ? 'true' : false;
        elseif(is_string($var) && $var=='') return "Ø (empty string)";
        elseif(is_string($var)) return "'".$var."'";
        elseif(is_null($var)) return 'NULL';
        elseif($var instanceof DateTime) return $var->format('d/m/Y - H:i:s');
        else return print_r($var,1);
    }
    
    public function dump($destFile) { 
        if($this->type!="mysql") return "Wrong type => ".$this->type;
        
        if(!$this->pwd) $command = MYSQLDUMP." -u ".$this->user." ".$this->db." > ".$destFile;
        else $command = MYSQLDUMP." -u ".$this->user." -p".$this->pwd." ".$this->db." > ".$destFile;
        
        die($command);
                
        $result = array();
        $returnCode = 0;

        $result = system($command,$returnCode);

        if($returnCode==0) return true;
        else return "Une erreur est survenue durant la création dump de la base de données ! (error code : ".$returnCode." - command : ".$command." - result : ".$this->getReadableVar($result).")";
    }
    
    public function getSqliteTablesList($ignore=true) {
        $list = array();
        
        $sql = "SELECT `name` FROM sqlite_master WHERE `type`='table'";
        
        $r = $this->select($sql);
        
        if(!$r['erreur']) {
            foreach($r['datas'] as $t) {
                if(($t['name']!="sqlite_sequence" && $ignore) || !$ignore) $list[] = $t['name'];
            }
        }
        else {
            $er = "<br/>sql: ".$sql."<br/>error: ".$this->getReadableVar($r['erreur']);
            return "Une erreur est survenue durant la récupération de la liste tables de le base de données !".$er;
        }
        
        return $list;
    }
}

function dumpSQL($db,$user,$pwd,$destFile) {
    $command = MYSQLDUMP." -u ".$user." -p".$pwd." ".$db." > ".$destFile;
	$result = array();
	$returnCode = 0;
	
	$result = system($command,$returnCode);
        
	if($returnCode==0) return true;
	else return "Une erreur est survenue durant la création dump de la base de données ! (error code : ".$returnCode." - command : ".$command." - result : ".$result.")";
}
?>