<?php
namespace imcat;

//数据库操作类，加载了外部的数据库驱动类

class glbDBObj{

    public $db = NULL; // 当前数据库操作对象
    public $config = array();
    public $driver = '';
    public $sql = '';//sql语句，主要用于输出构造成的sql语句
    public $pre = '';//表前缀，主要用于在其他地方获取表前缀
    public $ext = '';//表后缀，主要用于在其他地方获取表后缀
    public $data = array();// 数据信息
    public $otps = array(); // 查询表达式参数
    static $uobj = array(); //实例化的前数据对象

    function __construct($config=array()){
        $this->config = empty($config) ? self::getCfg() : $config;
        $this->otps['field'] = '*';//默认查询字段
        $this->driver = $this->config['db_driver'];
        $this->pre = $this->config['db_prefix'];//数据表前缀
        $this->ext = $this->config['db_suffix'];//数据表后缀
    }

    //连接数据库
    function connect(){ 
        if(!is_object($this->db)){ //$this->db不是对象，则连接数据库
            $file = 'db'.ucfirst($this->driver);
            require_once DIR_IMCAT."/adpt/dbdrv/{$file}.php";
            $class = "\\imcat\\$file";
            $this->db = new $class();//连接数据库
            $this->runTimer();
            $this->db->connect($this->config);
            $this->runTimer('connect');
        }
    }

    //执行时间分析
    function runTimer($mode=0){
        global $_cbase;
        if(!$mode){
            $_cbase['run']['qstart'] = microtime(1); //记录Start
            return;
        }else{
            $run_one = microtime(1)-$_cbase['run']['qstart'];
            $_cbase['run']['qtime'] += $run_one;
            $_cbase['run']['query']++;
            $loger = $this->runChkbug($mode);
            if(empty($loger)) return;
        }
        $tpl = $_cbase['run']['tplname'];
        if($_cbase['debug']['db_sql']=='db'){
            $info = basDebug::bugInfo();
            $sql = basReq::in($this->sql); $used = 1000*$run_one; $run = $_cbase['run']; 
            $kid = basKeyid::kidTemp().$_cbase['run']['query'];
            $vals = "'$kid','$sql','$used','{$info['vp']}','$tpl','','{$run['stamp']}'"; 
            $this->db->run("INSERT INTO ".$this->table('logs_dbsql',2)."(kid,`sql`,used,page,tpl,tag,atime)VALUES($vals)"); 
        }elseif(!empty($_cbase['debug']['db_sql'])){
            $fmt = $_cbase['debug']['db_sql'];
            $file = date($fmt).".dbsql";
            basDebug::bugLogs('dbobj',$this->sql,$file,'file');
        }
    }

    function runChkbug($mode){
        global $_cbase;
        if(!strpos($_cbase['debug']['db_acts'],$mode)) return 0;
        $indefine = 0;
        foreach($_cbase['debug']['db_area'] as $key){
            if(defined($key)){
                $indefine = 1;
                break;
            }
        }
        return $indefine;
    }

    //回调方法，连贯操作的实现
    function __call($method, $args) {
        $method = strtolower($method);
        if(in_array($method,array('field','data','where','groups','having','order','limit','cache'))){
            $this->otps[$method] = $args[0]; //接收数据
            return $this; //返回对象，连贯查询
        }else{
            glbError::show($method.':No Found.');
        }
    }
    
    //设置表，$nofix为true的时候，不加上默认的表前缀
    function table($table, $nofix=false){
        $full = '`'.$this->pre.$table.$this->ext.'`';
        if($nofix===2){ 
            return $full;
        }elseif($nofix){
            $this->otps['table'] = '`'.$table.'`';
        }else{
            $this->otps['bare_tab'] = $table;
            $this->otps['table'] = $full;
        }
        return $this;
    }

    //执行原生sql语句，如果sql是查询语句，返回二维数组
    function query($sql){ 
        $this->sql = $sql;
        //判断当前的sql是否是查询语句
        if(strpos(trim(strtolower($sql)),'select')===0){
            $this->runTimer();
            $data = $this->db->arr($this->sql);
            $this->runTimer('qSelect');
            return $data;
        }else{ //不是查询条件，执行之后，直接返回
            $query = $this->run($this->sql,'qOther');
            return $query;
        }
    }

    //执行sql语句
    function run($sql, $act=''){ 
        global $_cbase;
        $this->sql = $sql;
        $this->runTimer();
        $re = $this->db->run($this->sql);
        $this->runTimer($act);
        return $re;
    }

    //统计行数
    function count(){
        $table = $this->otps['table'];//当前表
        $field = 'count(*)';//查询的字段
        $where = $this->_parseCond();//条件
        $this->sql="SELECT $field FROM $table $where";
        $this->runTimer();       
        $re = $this->db->val($this->sql);
        $this->runTimer('count');
        return $re;
    }

    //只查询一条信息，返回一维数组    
    function find(){
        $table = $this->otps['table'];//当前表
        $field = $this->otps['field'];//查询的字段
        $this->otps['limit']=1;//限制只查询一条数据
        $where = $this->_parseCond();//条件
        $this->otps['field']='*';//设置下一次查询时，字段的默认值
        $this->sql="SELECT $field FROM $table $where";
        $data = array();
        $this->runTimer();
        $data = $this->db->row($this->sql);
        $this->runTimer('find');
        return $data;
    }

    //查询多条信息，返回数组
    function select(){
        $table = $this->otps['table'];//当前表
        $field = $this->otps['field'];//查询的字段
        $where = $this->_parseCond();//条件
        $this->otps['field'] = '*';//设置下一次查询时，字段的默认值
        $this->sql = "SELECT $field FROM $table $where";
        $data = array();
        $this->runTimer();
        $data = $this->db->arr($this->sql);
        $this->runTimer('select');
        return $data;
    }

    //插入数据
    function insert($log='a') {
        $table = $this->otps['table'];//当前表
        $data = $this->_parseData('add',$log);//要插入的数据
        $this->sql = "INSERT INTO $table $data";
        $this->run($this->sql,'insert');
        if($this->db->affRows){
            $id = $this->db->lastID;
            return empty($id)?$this->db->affRows:$id;
        }
        return false;
    }

    //替换数据
    function replace($log='a') {
        $table = $this->otps['table'];//当前表
        $data = $this->_parseData('add',$log);//要插入的数据
        $this->sql="REPLACE INTO $table $data" ;
        $this->run($this->sql,'replace');
        if($this->db->affRows){
            return  $this->db->lastID;
        }
        return false;
    }

    //修改更新
    function update($log='e'){ 
        $table = $this->otps['table'];//当前表
        $data = $this->_parseData('save',$log);//要更新的数据
        $where = $this->_parseCond();//更新条件
        if(empty($where)) return false;    //修改条件为空时，则返回false，避免不小心将整个表数据修改了
        $this->sql = "UPDATE $table SET $data $where" ;
        $this->run($this->sql,'update');
        return $this->db->affRows;
    }

    //删除
    function delete(){
        $table = $this->otps['table'];//当前表
        $where = $this->_parseCond();//条件
        if(empty($where)) return false;    //删除条件为空时，则返回false，避免数据不小心被全部删除
        $this->sql = "DELETE FROM $table $where";
        $this->run($this->sql,'delete');
        return $this->db->affRows;
    }

    // 取得数据表的字段信息
    function fields($tab){
        $tab = is_array($tab) ? $tab[0] : $tab;
        $this->runTimer();
        $a = $this->db->fields("$this->pre$tab$this->ext");
        $this->runTimer('fields');
        return $a;
    }

    // 取得数据库的表信息
    function tables($info=0){
        $this->runTimer();
        $a = $info ? $this->db->tabinfo() : $this->db->tables();
        $this->runTimer('tables'); 
        $tab = array();
        foreach($a as $v){ 
            if($info){
                $v['Name'] = str_replace(array("($this->pre","$this->ext)"),'',"({$v['Name']})");
                if(($this->pre && strstr($v['Name'],'(')) || ($this->ext && strpos($v['Name'],')'))) continue;
                $tab[] = $v;
            }else{
                $table = str_replace(array("($this->pre","$this->ext)"),'',"($v)");
                if(($this->pre && strstr($table,'(')) || ($this->ext && strpos($table,')'))) continue;
                $tab[] = $table;
            }
        }
        return $tab;
    }

    // 取得创建表sql
    function create($tab){
        $this->runTimer();
        $a = $this->db->create("{$this->pre}$tab{$this->ext}");
        $this->runTimer('create');
        return $a;
    }
    //返回sql语句
    function getSql(){
        return $this->sql;
    }
    //返回quoteSql语句
    function quoteSql($sql){
        return $this->db->quoteSql($sql);
    }

    //解析数据,添加数据时$type=add,更新数据时$type=save
    public function _parseData($type, $log='') {
        if(empty($this->otps['data'])){
            die("Error, Null-Data!");
        }
        //如果数据是字符串，直接返回
        if(is_string($this->otps['data'])){
            return $this->otps['data'];
        }
        $arfix = array('advs_','base','bext','coms_','docs_','types','users');
        if($log && in_array(substr($this->otps['bare_tab'],0,5), $arfix)){
            $dlog = basSql::logData($log);
            $this->otps['data'] = array_merge($dlog, $this->otps['data']);
        }
        switch($type){
            case 'add':
                $data=array();
                $data['key'] = "";
                $data['value'] = "";
                foreach($this->otps['data'] as $key=>$value){
                    $data['key'] .= "`$key`,";
                    $data['value'] .= "'$value',";
                }
                $data['key'] = substr($data['key'], 0,-1);//去除后面的逗号
                $data['value'] = substr($data['value'], 0,-1);//去除后面的逗号
                return " (".$data['key'].") VALUES (".$data['value'].") ";
                break;
            case 'save':
                $data = "";
                foreach($this->otps['data'] as $key=>$value){
                    $data .= "`$key`='$value',";
                }
                $data = substr($data, 0,-1); //去除后面的逗号
                return $data;
                break;
            default:
                return false;
        }        
    }

    //解析sql查询条件
    public function _parseCond() {
        $cond = ""; 
        //解析where()方法
        if(!empty($this->otps['where'])){
            $cond = " WHERE ";
            if(is_string($this->otps['where'])){
                $cond .= $this->otps['where'];
            }else if(is_array($this->otps['where'])){
                    foreach($this->otps['where'] as $key=>$value){
                         $cond .= " `$key`='$value' AND ";
                    }
                    $cond = substr($cond, 0,-4);
            }else{
                $cond = "";
            }
            unset($this->otps['where']);//清空$this->otps['where']数据
        }
        if(!empty($this->otps['groups'])&&is_string($this->otps['groups'])){
            $cond .= " GROUP BY ".$this->otps['groups'];
            unset($this->otps['groups']);
        }
        if(!empty($this->otps['having'])&&is_string($this->otps['having'])){
            $cond .= " HAVING ".$this->otps['having'];
            unset($this->otps['having']);
        }
        if(!empty($this->otps['order'])&&is_string($this->otps['order'])){
            $cond .= " ORDER BY ".$this->otps['order'];
            unset($this->otps['order']);
        }
        if(!empty($this->otps['limit'])&&(is_string($this->otps['limit'])||is_numeric($this->otps['limit']))){
            $cond .= " LIMIT ".$this->otps['limit'];
            unset($this->otps['limit']);
        }
        if(empty($cond))
            return "";
        return $cond;
    }
    
    // 
    static function dbObj($config=array(), $catch=0){ 
        $dbcfg = self::getCfg(); 
        if(empty($config)){
            $key = 'db0_main';
        }elseif(is_string($config)){ // `user`
            $key = $config;
            $config = isset($dbcfg[$key]) ? $dbcfg[$key] : $dbcfg;
            $key = isset($dbcfg[$key]) ? "db1_$key" : 'db0_main';
        }else{ // array()
            $config = array_merge($dbcfg, $config);
            $key = 'dba_'.$config['db_host'].'_'.$config['db_name'];
        }
        $key .= $catch;
        if(empty(self::$uobj[$key])){
            $class = $catch ? '\\imcat\\glbDBCache' : '\\imcat\\glbDBObj';
            self::$uobj[$key] = new $class($config); 
            self::$uobj[$key]->connect($config); 
        }
        return self::$uobj[$key];
    }
    
    static function getCfg($key=''){
        static $dbcfg;
        if(empty($dbcfg)){
            require DIR_ROOT.'/cfgs/boot/cfg_db.php';
            $dbcfg = $_cfgs;
        }
        return ($key && isset($dbcfg[$key])) ? $dbcfg[$key] : $dbcfg;
    }

}
