<?php

class DatabaseAction extends CommonAction {

    public $config = '';
    // 列出所有數據表信息
    public function index() {
        //query是查功能，execute是增刪改功能
        $dbtables = M()->query('SHOW TABLE STATUS');
        $total = 0;
        foreach ($dbtables as $k => $v) {
            $dbtables[$k]['size'] = get_byte($v['Data_length'] + $v['Index_length']);
            $total+=$v['Data_length'] + $v['Index_length'];
        }
        $this->vlist = $dbtables;
        $this->total = get_byte($total);
        $this->tableNum = count($dbtables);
        $this->type = '數據表列表';
        $this->display();
    }

    //備份數據庫
    public function backup() {
        if (!IS_POST){
            $this->error("Access Denied");
        }            
        $M = M();
        //防止備份數據過程超時
        function_exists('set_time_limit') && set_time_limit(0); 
        $tables = I('key', array(), '');
        if (empty($tables)) {
            $this->error('請選擇要備份的數據表');
        }

        $time = time();//開始時間
        $path = $this->getDbPath() . "/yycmstables_" . date("Ymd") . "_" . get_randomstr(5);
     
        $pre = "# -----------------------------------------------------------\n";
        //取得表結構信息

        //1，表示表名和字段名會用``包著的,0 則不用``
 
        //M()->query("SET SQL_QUOTE_SHOW_CREATE = 1"); //Log會有警告信息DbMysql.class.php(109|80)        
        M()->execute("SET SQL_QUOTE_SHOW_CREATE = 1"); 
        $outstr = '';
       
        foreach ($tables as $table) {
            $outstr.="# 表的結構 {$table} \n";
            $outstr .= "DROP TABLE IF EXISTS `{$table}`;\n";
            $tmp = $M->query("SHOW CREATE TABLE {$table}");
            $outstr .= $tmp[0]['Create Table'] . " ;\n\n";
        }
        $sqlTable = $outstr;
        $outstr = "";
        $file_n = 1;
        $backedTable = array();
        //表中的數據
        foreach ($tables as $table) {
            $backedTable[] = $table;
            $outstr.="\n\n# 轉存表中的數據：{$table} \n";
            $tableInfo = $M->query("SHOW TABLE STATUS LIKE '{$table}'");
            $page = ceil($tableInfo[0]['Rows'] / 10000) - 1;
            for ($i = 0; $i <= $page; $i++) {
                $query = $M->query("SELECT * FROM {$table} LIMIT " . ($i * 10000) . ", 10000");
                foreach ($query as $val) {
                    $temSql = "";
                    $tn = 0;
                    $temSql = '';
                    foreach ($val as $v) {
                        $temSql.=$tn == 0 ? "" : ",";
                        $temSql.=$v == '' ? "''" : "'{$v}'";
                        $tn++;
                    }
                    $temSql = "INSERT INTO `{$table}` VALUES ({$temSql});\n";

                    $sqlNo = "\n# Time: " . date("Y-m-d H:i:s") . "\n" .
                            "# -----------------------------------------------------------\n" .
                            "# SQLFile Label：#{$file_n}\n# -----------------------------------------------------------\n\n\n";
                       if ($file_n == 1) {
                        $sqlNo = "# Description:備份的數據表[結構]：" . implode(",", $tables) . "\n".
                                 "# Description:備份的數據表[數據]：" . implode(",", $backedTable) . $sqlNo;
                    } else {
                        $sqlNo = "# Description:備份的數據表[數據]：" . implode(",", $backedTable) . $sqlNo;
                    }

                    if (strlen($pre) + strlen($sqlNo) + strlen($sqlTable) + strlen($outstr) + strlen($temSql) > C("USER_SQL_FILESIZE")) {
                        $file = $path . "_" . $file_n . ".sql";
                        $outstr = $file_n == 1 ? $pre . $sqlNo . $sqlTable . $outstr : $pre . $sqlNo . $outstr;
                       
                        if (!file_put_contents($file, $outstr, FILE_APPEND)) {
                            $this->error("備份文件寫入失敗！", U('Database/index'));
                        }
    
                        $sqlTable = $outstr = "";
                        $backedTable = array();
                        $backedTable[] = $table;
                        $file_n++;
                    }
                    $outstr.=$temSql;
                }
            }
        }
        if (strlen($sqlTable . $outstr) > 0) {
            $sqlNo = "\n# Time: " . date("Y-m-d H:i:s") . "\n" .
                    "# -----------------------------------------------------------\n" .
                    "# SQLFile Label：#{$file_n}\n# -----------------------------------------------------------\n\n\n";
            if ($file_n == 1) {
                $sqlNo = "# Description:備份的數據表[結構] " . implode(",", $tables) . "\n".
                         "# Description:備份的數據表[數據] " . implode(",", $backedTable) . $sqlNo;
            } else {
                $sqlNo = "# Description:備份的數據表[數據] " . implode(",", $backedTable) . $sqlNo;
            }
            $file = $path . "_" . $file_n . ".sql";
            $outstr = $file_n == 1 ? $pre . $sqlNo . $sqlTable . $outstr : $pre . $sqlNo . $outstr;
            //file_put_contents($file, $outstr, FILE_APPEND);
            if (!file_put_contents($file, $outstr, FILE_APPEND)) {
                $this->error("備份文件寫入失敗！" ,U('Database/index'));
            }
           

            $file_n++;
        }
        $time = time() - $time;
        $this->success("成功備份數據表，本次備份共生成了" . ($file_n - 1) . "個SQL文件。耗時：{$time} 秒",  U('Database/restore'));
    }

    /**
     * 還原數據庫內容
     */
    public function restore() {

               
        $size = 0;
        $pattern = "*.sql";
        $filelist = glob($this->getDbPath().'/'.$pattern);
        $fileArray = array(); 
        foreach ($filelist  as $i => $file) {
            //只讀取文件
            if (is_file($file)) { 
                $_size = filesize($file);    
                $size += $_size;
                $name = basename($file);  
                $pre = substr($name, 0, strrpos($name, '_')); 
                $number = str_replace(array($pre. '_', '.sql'), array('', ''), $name);     
                $fileArray[] = array(
                    'name' => $name,
                    'pre' => $pre,
                    'time' => filemtime($file),
                    'size' => $size,
                    'number' => $number,
                );
            }
        }   

        if(empty($fileArray)) $fileArray = array();        
        krsort($fileArray); //按備份時間倒序排列
        $this->vlist = $fileArray;
        $this->total = get_byte($size);
        $this->filenum = count($fileArray);
        $this->type = '備份文件列表';
        $this->display();
    }

    //讀取要導入的sql文件列表並排序後插入SESSION中
    private function getRestoreFiles() {
        
        $sqlfilepre = I('sqlfilepre', '');//獲取sql文件前綴
        if (empty($sqlfilepre)) {
            $this->error('請選擇要還原的數據文件！');
        }
        $pattern=$sqlfilepre. "*.sql";
        $sqlFiles = glob($this->getDbPath().'/'.$pattern);  
        if (empty($sqlFiles)) {
            $this->error('不存在對應的SQL文件！');
        }
            
        //將要還原的sql文件按順序組成數組，防止先導入不帶表結構的sql文件
        $files = array();
        foreach ($sqlFiles as $sqlFile) {
            $sqlFile = basename($sqlFile);
            $k = str_replace(".sql", "", str_replace($sqlfilepre . "_", "", $sqlFile));
            $files[$k] = $sqlFile;
        }
        unset($sqlFiles, $sqlfilepre);
        ksort($files);
        return $files;
    }

    //執行還原數據庫操作
    public function restoreData() {
        //ini_set("memory_limit", "256M");
        function_exists('set_time_limit') && set_time_limit(0); //防止備份數據過程超時
        //取得需要導入的sql文件
        if (!isset($_SESSION['cacheRestore']['files'])) {
            $_SESSION['cacheRestore']['starttime'] = time();
            $_SESSION['cacheRestore']['files'] = $this->getRestoreFiles();
        }        
        $files = $_SESSION['cacheRestore']['files'] ;
        if (empty($files)) {
            unset($_SESSION['cacheRestore']);
            $this->error('不存在對應的SQL文件');
        }
      
        //取得上次文件導入到sql的句柄位置
        $position = isset($_SESSION['cacheRestore']['position']) ? $_SESSION['cacheRestore']['position'] : 0;
        $M = M();
        $execute = 0;
        foreach ($files as $fileKey => $sqlFile) {        
        
            $file = $this->getDbPath() .'/'. $sqlFile;
          
            if (!file_exists($file))
                continue;
            $file = fopen($file, "r");
            $sql = "";
            fseek($file, $position); //將文件指針指向上次位置
            while (!feof($file)) {

                $tem = trim(fgets($file));
                //過濾,去掉空行、註釋行(#,--)
                if (empty($tem) || $tem[0] == '#' || ($tem[0] == '-' && $tem[1] == '-'))
                    continue;
                //統計一行字符串的長度
                $end = (int) (strlen($tem) - 1);
                //檢測一行字符串最後有個字符是否是分號，是分號則一條sql語句結束，否則sql還有一部分在下一行中

                if ($tem[$end] == ";") {
                    $sql.=$tem;                                        
                    $M->execute($sql);//query
                    $sql = "";
                    $execute++;
                    if ($execute > 500) {
                        $_SESSION['cacheRestore']['position'] = ftell($file);
                        $imported = isset($_SESSION['cacheRestore']['imported']) ? $_SESSION['cacheRestore']['imported'] : 0;
                        $imported += $execute;
                        $_SESSION['cacheRestore']['imported'] = $imported;
                        //echo json_encode(array("status" => 1, "info" => '如果導入SQL文件卷較大(多)導入時間可能需要幾分鐘甚至更久，請耐心等待導入完成，導入期間請勿刷新本頁，當前導入進度：<font color="red">已經導入' . $imported . '條Sql</font>', "url" => U('Database/restoreData', array(get_randomstr(5) => get_randomstr(5)))));
                        $this->success('如果SQL文件卷較大(多),則可能需要幾分鐘甚至更久,<br/>請耐心等待完成，<font color="red">請勿刷新本頁</font>，<br/>當前導入進度：<font color="red">已經導入' . $imported . '條Sql</font>', U('Database/restoreData', array(get_randomstr(5) => get_randomstr(5))));                       
                        exit();
                    }
                } else {
                    $sql.=$tem;
                }
            }
            fclose($file);
            unset($_SESSION['cacheRestore']['files'][$fileKey]);
            $position = 0;
        }
        $time = time() - $_SESSION['cacheRestore']['starttime'];
        unset($_SESSION['cacheRestore']);
        $this->success("導入成功，耗時：{$time} 秒鐘", U('Database/restore'));
    }


    //刪除sql文件
    public function delSqlFiles() {

        $id = I('id',0 , 'intval');
        $batchFlag = I('get.batchFlag', 0, 'intval');
        //批量刪除
        if ($batchFlag) {
           $files = I('key', array());
        }else {
            $files[] = I('sqlfilename' , '');
        }

        if (empty($files)) {
            $this->error('請選擇要刪除的sql文件');
        }
        
        foreach ($files as $file) {
            unlink($this->getDbPath(). '/' . $file);
        }
        $this->success("已刪除：" . implode(",", $files), U('Database/restore'));

    }

    //優化
    public function optimize() {

        $id = I('id',0 , 'intval');
        $batchFlag = I('get.batchFlag', 0, 'intval');
        //批量刪除
        if ($batchFlag) {
           $table = I('key', array());
        }else {
            $table[] = I('tablename' , '');
        }

        if (empty($table)) {
            $this->error('請選擇要優化的表');
        }
        
        $strTable = implode(', ', $table);
        if (!M()->query("OPTIMIZE TABLE {$strTable} ")) {
            $strTable = '';
        }

        $this->success("優化表成功" . $strTable, U('Database/index'));

    }

    //修復
    public function repair() {

        $id = I('id',0 , 'intval');
        $batchFlag = I('get.batchFlag', 0, 'intval');
        //批量刪除
        if ($batchFlag) {
           $table = I('key', array());
        }else {
            $table[] = I('tablename' , '');
        }

        if (empty($table)) {
            $this->error('請選擇修復的表');
        }

        $strTable = implode(', ', $table);
        if (!M()->query("REPAIR TABLE {$strTable} ")) {
            $strTable = '';
        }

        $this->success("修復表成功" . $strTable, U('Database/index'));

    }



    

    public function downFile() {
        if (empty($_GET['file']) || empty($_GET['type']) || !in_array($_GET['type'], array("zip", "sql"))) {
            $this->error("下載地址不存在");
        }
        $path = array("zip" => $this->getDbPath() . "Zip/", "sql" => $this->getDbPath(). '/');
        $filePath = $path[$_GET['type']] . $_GET['file'];
        if (!file_exists($filePath)) {
            $this->error("該文件不存在，可能是被刪除");
        }
        $filename = basename($filePath);
        header("Content-type: application/octet-stream");
        header('Content-Disposition: attachment; filename="' . $filename . '"');
        header("Content-Length: " . filesize($filePath));
        readfile($filePath);
    }


    //返回數據目錄
    public function getDbPath() {
        return C('USER_DATA_PATH'). '/resource/backupdata';
    }

}