<?php

namespace ciy;

class pdo {
    public $link;
    public $isconnected;
    public $error;
    public $lastsql;
    public $lastdata;
    private $pf; //traceid全链路跟踪
    private $conn;
    function __construct($conn, $pf = null) {
        $this->isconnected = false;
        $this->error = '';
        $this->pf = $pf;
        $this->conn = $conn;
    }
    function connect() {
        if ($this->isconnected)
            return true;
        if (isset($this->link))
            $this->link = null;
        try {
            $timeout = 10;
            if (isset($this->conn['timeout']))
                $timeout = toint($this->conn['timeout']);
            $charset = 'utf8mb4';
            if (isset($this->conn['charset']))
                $charset = $this->conn['charset'];
            $persistent = false;
            if (isset($this->conn['persistent']))
                $persistent = (bool)$this->conn['persistent'];
            $opts = array(
                \PDO::ATTR_TIMEOUT => $timeout,
                \PDO::ATTR_PERSISTENT => $persistent,
                \PDO::ATTR_ERRMODE => \PDO::ERRMODE_SILENT,
                \PDO::MYSQL_ATTR_INIT_COMMAND => "SET CHARACTER SET {$charset}"
            );
            $this->link = new \PDO('mysql:host=' . $this->conn['host'] . ';dbname=' . $this->conn['name'] . ';port=' . $this->conn['port'] . ';', $this->conn['user'], $this->conn['pass'], $opts);
            $this->link->query('SET SESSION sql_mode = \'STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION\'');
        } catch (\PDOException $e) {
            return $this->errsql(false, 'PDO连接失败:' . $e->GetMessage());
        }
        $this->isconnected = true;
        return true;
    }
    function disconnect() {
        if ($this->isconnected)
            $this->link = null;
        $this->isconnected = false;
        return true;
    }
    function ping() {
        if (!$this->link)
            return false;
        try {
            $ret = $this->link->query('SELECT 1');
            if ($ret === false)
                return false;
        } catch (\PDOException $e) {
            return false;
        }
        return true;
    }
    function get($type, $query, $data) {
        if (!$this->isconnected)
            return false;
        if ($this->pf)
            $query = '/* ' . $this->pf . ' */ ' . $query;
        $rs = $this->_prepare($query, $data);
        if ($rs === false)
            return false;
        if ($type == 1) //返回全部数据
        {
            $retdata = $rs->fetchAll(\PDO::FETCH_ASSOC); //mysql_unbuffered_query
            $rs->closeCursor();
            if ($retdata === false)
                return $this->errsql(array(), "表1[{$query}]数据不存在");
            return $retdata;
        } else if ($type == 2) { //返回单行数据
            $retdata = $rs->fetch(\PDO::FETCH_ASSOC);
            $rs->closeCursor();
            if ($retdata === false)
                return $this->errsql(null, "表2[{$query}]数据不存在");
            return $retdata;
        } else if ($type == 3) { //返回第一行第一列数据
            $retdata = $rs->fetchColumn();
            $rs->closeCursor();
            if ($retdata === false)
                return $this->errsql(null, "表3[{$query}]数据不存在");
            return $retdata;
        }
        return null;
    }
    function update($csql, $updata) {
        if (!$this->isconnected)
            return false;
        if (empty($csql->where))
            return $this->errsql(false, 'update no where');
        if (!is_array($updata))
            return $this->errsql(false, 'var update not array');
        if (count($updata) == 0)
            return $this->errsql(0, 'update no data');

        $set = '';
        if (!is_array($updata))
            return $this->errsql(false, 'var update not array update');
        $data = array();
        foreach ($updata as $key => $val) {
            if (!empty($set))
                $set .= ',';
            if ($csql->wrapup) {
                if (is_array($val))
                    $set .= "`{$key}`=" . $val[0];
                else {
                    $set .= "`{$key}`=?";
                    $data[] = $val;
                }
            } else {
                if (is_array($val))
                    $set .= $key . '=' . $val[0];
                else {
                    $set .= $key . '=?';
                    $data[] = $val;
                }
            }
        }
        if ($this->execute("update {$csql->table} set {$set}" . $csql->buildwhere(), array_merge($data, $csql->tsmt)) === false)
            return false;
        return 1;
    }
    function insert($csql, $updata) {
        if (!$this->isconnected)
            return false;
        $data = array();
        if (!is_array($updata))
            return $this->errsql(false, 'var update not array');
        if (count($updata) == 0)
            return $this->errsql(0, 'update no data');
        $field = '';
        $value = '';
        if (!is_array($updata))
            return $this->errsql(false, 'var update not array insert');
        foreach ($updata as $key => $val) {
            if (!empty($field))
                $field .= ',';
            if ($csql->wrapup)
                $field .= '`' . $key . '`';
            else
                $field .= $key;
            if (!empty($value))
                $value .= ',';
            if (is_array($val))
                $value .= $val[0];
            else {
                $value .= '?';
                $data[] = $val;
            }
        }
        return $this->execute("insert into {$csql->table} ({$field}) values ({$value})", $data);
    }
    function insert_id() {
        return $this->link->lastInsertId();
    }
    function execute($query, $data) {
        if (!$this->isconnected)
            return false;
        if ($this->pf)
            $query = '/* ' . $this->pf . ' */ ' . $query;
        $rs = $this->_prepare($query, $data);
        if ($rs === false)
            return false;
        $cnt = $rs->rowCount();
        $rs->closeCursor();
        return $cnt;
    }
    function begin() {
        if (!$this->isconnected)
            return false;
        $this->link->setAttribute(\PDO::ATTR_AUTOCOMMIT, 0);
        $execute = $this->link->beginTransaction();
        if ($execute === false)
            return $this->errsql(false, 'begin失败:' . $this->pdoerr($this->link));
        return $execute;
    }
    function commit() {
        if (!$this->isconnected)
            return false;
        $execute = $this->link->commit();
        $this->link->setAttribute(\PDO::ATTR_AUTOCOMMIT, 1);
        if ($execute === false)
            return $this->errsql(false, 'commit失败:' . $this->pdoerr($this->link));
        return $execute;
    }
    function rollback() {
        if (!$this->isconnected)
            return false;
        $execute = $this->link->rollback();
        $this->link->setAttribute(\PDO::ATTR_AUTOCOMMIT, 1);
        if ($execute === false)
            return $this->errsql(false, 'rollback失败:' . $this->pdoerr($this->link));
        return $execute;
    }
    function pdoerr($pk) {
        $err = $pk->errorInfo();
        return $pk->errorCode() . ':' . @$err[1] . ':' . @$err[2];
    }
    function errsql($ret, $msg) {
        $this->error = $msg;
        return $ret;
    }
    function _prepare($query, $data) {
        $this->lastsql = $query;
        $this->lastdata = $data;
        for ($i = 0; $i < 3; $i++) {
            $rs = $this->link->prepare($query);
            if ($rs === false)
                return $this->errsql(false, "SQL预处理出错[{$query}]:" . $this->pdoerr($this->link));
            if ($rs->execute($data) === false) {
                $arr = $rs->errorInfo();
                // 如果是连接断开错误，尝试重新连接
                if ($arr[0] == 'HY000' && $arr[1] == 2006) {
                    sleep(1);
                    $this->isconnected = false;
                    if ($this->connect())
                        continue;
                }
                return $this->errsql(false, 'SQL出错:' . $arr[2] . '[' . $arr[0] . '][' . $arr[1] . ']' . $query);
            }
            return $rs;
        }
        return $this->errsql(false, 'SQL重试3次超时:' . $arr[2] . '[' . $arr[0] . '][' . $arr[1] . ']' . $query);
    }
}
