<?php
/**
* Clase para el manejo de  bases de datos mysql y sqlite
* 
* DBA.class.php
* Database Access (DBA) para MySQL, SQLite, y  SQLite3
*/
class DBA
{
    private $conn = false;
    private $servertype = 'mysql';
    private $server;
    private $username;
    private $password;
    private $database;
    private $port;
    private $options = array();

    private $query = '';
    private $actualQuery = '';
    private $binds = array();
    private $result;
    private $lastError = '';
    private $lastid = 0;

    function DBA(){
        $this->connect();
    }
    
    
    //Conexion de produccion
    private function connect($dsn = 'mysql:host=localhost;dbname=kapital_ks;port=3306',$u='root',$p='')
    {
        $a = $this->parseConnStr($dsn);
        $this->servertype = $a['servertype'];
        $this->server = $a['server'];
        $this->database = $a['database'];
        $this->options = $a['options'];
        $this->port = $a['port'];
        $this->username = $u;
        $this->password = $p;
    }

    private function parseConnStr($s)
    {
        $a = array();
        $dividerpos = strpos($s,':');
        $a['servertype'] = strtolower(substr($s,0,$dividerpos));

        if($a['servertype']!='uri')
        {
            $t = array();
            $e = explode(';',substr($s,$dividerpos+1));
            foreach($e as $ele)
            {
                list($key,$value) = explode('=',$ele);
                $t[strtolower(trim($key))] = trim($value);
            }

            
            $a['server'] = $t['host'];
            $a['port'] =$t['port'];

            $a['database'] = $t['dbname'];
            unset($t['host'],$t['port'],$t['dbname']);
            $a['options'] = $t;
        }
        else
        {
            $a['server'] = substr($s,$dividerpos);
        }
        
        return $a;
    }

    public function prepare($q)
    {
        $this->binds = array();
        return $this->query = $q;
    }

    private function countVariable()
    {
        $t = $this->query;
        $t = str_replace(array('\\\\','\\?'),'',$t);
        return substr_count($t,'?');
    }

    public function bind($type, $var)
    {
        $this->binds[] = array($type,$var);
    }

    private function testConn()
    {

        switch($this->servertype)
        {
            case 'mysql':
                

                $this->conn = new mysqli();
                $this->conn->connect($this->server, $this->username, $this->password, $this->database, $this->port, false);
                
                if($this->conn->connect_error)
                {
                    $this->setLastError();
                    return false;
                }
                
                mysqli_set_charset($this->conn, "utf8");
           
            break;
            case 'sqlite':
              $err = '';
              $err = $this->conn = sqlite_open($this->server,0666);

              if($err != ''){$this->setLastError($err);return false;}
            break;
            case 'sqlite3':
              $err = '';
              if(isset($this->options['key'])){
                  $this->conn = new SQLite3($this->server, SQLITE3_OPEN_READWRITE | SQLITE3_OPEN_CREATE, $this->options['key']);
              }else{
                  $this->conn = new SQLite3($this->server, SQLITE3_OPEN_READWRITE | SQLITE3_OPEN_CREATE);

              }
              if(!$this->conn){$this->setLastError();return false;}
            break;
        }
        
        return $this->conn;
    }

    private function disconnect()
    {
        if($this->conn)
        {
            switch($this->servertype)
            {
                case 'mysql':
                    $this->conn->close();
                break;
                case 'sqlite':
                    sqlite_close($this->conn);
                break;
                case 'sqlite3':
                    $this->conn->close();
                break;
            }
        }
    }
    
    public function executeEBS()
    {
         if(!$this->testConn())
        {
            $this->setLastError("La conexión a la BD no se pudo establecer.");
            return false;
        }
		
        if(empty($this->query))
        {
            $this->setLastError("El query está vacio.");
            return false;
        }
       
        if($this->countVariable()!=count($this->binds))
        {
            $this->setLastError("El numero de variables no coincide con los parametros especificados vars: ".$this->countVariable()." Binds: ".count($this->binds).".");
            return false;
        }
        
       

        $q = $this->query;
        $t = '';
        $i = 0;$l=0;$a=0;

        
        while(($i = strpos($q,'?',$i)) !== false)
        {
            
            if($i>0 && (substr($q, $i-1,1)!='\\' || ($i>1 && substr($q,$i-2,2)=='\\\\')))
            {
               
                switch($this->binds[$a][0])
                {
                    case 's': // string
                        $value = '\''.$this->escape_string($this->binds[$a][1]).'\'';
                    break;

                    case 'd': // float o int
                        $numero = @floatval($this->binds[$a][1]);
                        if(!is_numeric($numero)){$this->setLastError("El valor ".$this->binds[$a][1]." no es un entero o un float válido. "); return false;}
                            $value = $this->binds[$a][1];
                    break;

                    case 'i': // integer


                        $numero = @intval ($this->binds[$a][1]);

                        if(!is_int($numero)){ $this->setLastError("El valor ".$this->binds[$a][1]." no es un entero válido. "); return false;}
                        $value = (int)$this->binds[$a][1];


                    break;

                    case 'f': // float
                        $numero = @floatval($this->binds[$a][1]);

                        if(!is_float($numero)){$this->setLastError("El valor ".$this->binds[$a][1]." no es un float válido. "); return false;}
                            $value = (float)$this->binds[$a][1];
                    break;

                    case 'r': // raw, can be SQL function
                        $value = $this->binds[$a][1];
                    break;

                    default:
                        return false;
                    break;
                }
                $t .= substr($q,$l,$i-$l).$value;
                $a++;
                $l = $i+1;$i++;
            }
            else
            {
                $i++;
            }
        } // WHILE LOOP
        
        $t.=substr($q,$l); // the rest of the query
        
        //EBS        
        $this->result = $this->query($t);
        
        if(!$this->result){
           
            return $this->getLastError();
        }
        $info = null;
        while($array = mysql_fetch_array($this->result)){
            $info[] = $array;
        }

        $this->disconnect();
        
        return $info;    
    }  

    public function execute()
    {
        if(!$this->testConn())
        {
            $this->setLastError("La conexión no se pudo establecer.");
            return false;
        }
		
        if(empty($this->query))
        {
            $this->setLastError("El query está vacio.");
            return false;
        }
       
        if($this->countVariable()!=count($this->binds))
        {
            $this->setLastError("El numero de variables no coincide con los parametros especificados vars: ".$this->countVariable()." Binds: ".count($this->binds).".");
            return false;
        }
        
       

        $q = $this->query;
        $t = '';
        $i = 0;$l=0;$a=0;

        
        while(($i = strpos($q,'?',$i)) !== false)
        {
            
            if($i>0 && (substr($q, $i-1,1)!='\\' || ($i>1 && substr($q,$i-2,2)=='\\\\')))
            {
               
                switch($this->binds[$a][0])
                {
                    case 's': // string
                        $value = '\''.$this->escape_string($this->binds[$a][1]).'\'';
                    break;

                    case 'd': // float o int
                        $numero = @floatval($this->binds[$a][1]);
                        if(!is_numeric($numero)){$this->setLastError("El valor ".$this->binds[$a][1]." no es un entero o un float vÃ¡lido. "); return false;}
                            $value = $this->binds[$a][1];
                    break;

                    case 'i': // integer


                        $numero = @intval ($this->binds[$a][1]);

                        if(!is_int($numero)){ $this->setLastError("El valor ".$this->binds[$a][1]." no es un entero vÃ¡lido. "); return false;}
                        $value = (int)$this->binds[$a][1];


                    break;

                    case 'f': // float
                        $numero = @floatval($this->binds[$a][1]);

                        if(!is_float($numero)){$this->setLastError("El valor ".$this->binds[$a][1]." no es un float vÃ¡lido. "); return false;}
                            $value = (float)$this->binds[$a][1];
                    break;

                    case 'r': // raw, can be SQL function
                        $value = $this->binds[$a][1];
                    break;

                    default:
                        return false;
                    break;
                }
                $t .= substr($q,$l,$i-$l).$value;
                $a++;
                $l = $i+1;$i++;
            }
            else
            {
                $i++;
            }
        } // WHILE LOOP
        
        $t.=substr($q,$l); // the rest of the query
        

        $this->result = $this->query($t);
			
        if(stripos(  substr($t,0,8), "insert " )!==false)
        {
            $this->setlastInsertId();
        }

        $this->disconnect();
        
        return $this->result ? true : false;
    }

    private function query($q)
    {

        $this->actualQuery = $q;
        switch($this->servertype)
        {
            case 'mysql':
              $r = $this->conn->query($q);
               
            break;
            case 'sqlite':
              $r = sqlite_query($q);
            break;
            case 'sqlite3':
              $r = $this->conn->query($q);
            break;
        }
        if(!$r){ $this->setLastError();}
        return $r;
    }

    private function escape_string($s)
    {
        switch($this->servertype)
        {
            case 'mysql':
              return $this->conn->real_escape_string($s);
            break;
            case 'sqlite':
              return sqlite_escape_string($s);
            break;
            case 'sqlite3':
              return $this->conn->escapeString($s);
            break;
        }
    }

    private function setLastError($err = '')
    {

        if($err != '')
        {
            $this->lastError = $err;
            return;
        }

        switch($this->servertype)
        {
            case 'mysql':
                $err = $this->conn->error;
                
                $this->lastError = $err;
            break;
            case 'sqlite':
                $this->lastError = sqlite_error_string(sqlite_last_error($this->conn));
            break;
            case 'sqlite3':
                $this->lastError = $this->conn->lastErrorMsg();
            break;
        }
    }

    public function fetch($into = '')
    {
        if(!$this->result)
        {
            return false;
        }
       
        $table = array();

        switch($this->servertype)
        {

            case 'mysql':
               
                while($row = $this->result->fetch_assoc())
                {
                    $table[] = $row;

                }

            break;
            case 'sqlite':
                if($into === '' || !class_exists($into)){
                while($row = sqlite_fetch_assoc($this->result,SQLITE_ASSOC)){$table[] = $row;}
                }else{
                while($row = sqlite_fetch_object($this->result,$into)){$table[] = $row;}
                }
            break;
            case 'sqlite3':
                if($into === '' || !class_exists($into))
                {
                    while($row = $this->result->fetchArray(SQLITE3_ASSOC)){$table[] = $row;}
                }
                else
                {

                    while($row = $this->result->fetchArray(SQLITE3_ASSOC))
                    {
                        $o = new $into();
                        foreach($row as $k => $v)
                            $o->$k = $v;

                        $table[] = $o;
                    }
                }
            break;
        }
        return $table;
    }

    public function fetchValue($column, $rowIndex = 0)
    {
        $table = $this->fetch();

        if(!empty($table))
            return $table[$rowIndex][$column];
        else
            return null;
    }

    public function getCount()
    {
        $table = $this->fetch();

        return sizeof($table);
    }
    
    /**
     * Obtiene el texto del query original
     * @return type
     */
    public function getLastQuery()
    {
        return $this->query;
    }
    
    /**
     * Obtiene el texto del query final con las variables escapadas
     * @return type
     */
    public function getLastActualQuery()
    {
        return $this->actualQuery;
    }

    public function getLastError()
    {
        return $this->lastError;
    }

    public function getLastId()
    {
        return $this->lastid;
    }

    private function setlastInsertId()
    {
        try
        {
            switch($this->servertype)
            {

                case 'mysql':
                        $this->lastid = $this->conn->insert_id;
                break;
                case 'sqlite':
                    $this->lastid=sqlite_last_insert_rowid($this->conn);
                break;
                case 'sqlite3':
                    $this->lastid=$this->conn->lastInsertRowID();
                break;
            }
        }
        catch(Exception $exception)
        {
            $this->setLastError($exception->getMessage());
        }	
    }

    public function begin()
    {
        return $this->query('BEGIN');
    }

    public function commit()
    {
        return $this->query('COMMIT');
    }

    public function rollback()
    {
        return $this->query('rollback');
    }

}

if ( !function_exists('json_encode') && !function_exists('json_decode'))
{
    require_once 'JSON/JSON.php';

    function json_encode($str)
    {
        $json = new Services_JSON();
        return $json->encode($str);
    }

    function json_decode($str)
    {
        $json = new Services_JSON();
        return $json->decode($str);
    }
}