<?php
// +----------------------------------------------------------------------
// | ShopXO 国内领先企业级B2C免费开源电商系统
// +----------------------------------------------------------------------
// | Copyright (c) 2011~2099 http://shopxo.net All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( https://opensource.org/licenses/mit-license.php )
// +----------------------------------------------------------------------
// | Author: Devil
// +----------------------------------------------------------------------
namespace app\install\controller;

use think\facade\Db;
use app\service\SystemService;

/**
 * 安装程序
 * @author   Devil
 * @blog    http://gong.gg/
 * @version 1.0.0
 * @date    2018-11-30
 * @desc    description
 */
class Index extends Common
{
    // 编码类型
    private $charset_type_list;

    // 安装日志上报
    private $behavior_obj;

    /**
     * 构造方法
     * @author   Devil
     * @blog    http://gong.gg/
     * @version 1.0.0
     * @date    2018-11-30
     * @desc    description
     */
    public function __construct()
    {
        parent::__construct();

        // 编码类型
        $this->charset_type_list = [
            'utf8mb4'   => [
                'charset'   => 'utf8mb4',
                'collate'   => 'utf8mb4_general_ci',
                'version'   => 5.6,
            ],
            'utf8'      => [
                'charset'   => 'utf8',
                'collate'   => 'utf8_general_ci',
                'version'   => 5.0,
            ],
        ];

        // 安装日志上报类库
        $this->behavior_obj = new \base\Behavior();
    }

    /**
     * 是否已安装
     * @author   Devil
     * @blog    http://gong.gg/
     * @version 1.0.0
     * @date    2018-12-28
     * @desc    description
     */
    private function IsInstall()
    {
        // 是否已安装
        if(file_exists(ROOT.'config/database.php'))
        {
            exit('你已经安装过该系统，重新安装需要先删除 ./config/database.php 文件');
        }
    }

    /**
     * 协议
     * @author   Devil
     * @blog    http://gong.gg/
     * @version 1.0.0
     * @date    2018-12-28
     * @desc    description
     */
    public function Index()
    {
        $this->IsInstall();
        $this->behavior_obj->ReportInstallLog(['msg'=>'协议阅读']);
        return MyView();
    }

    /**
     * 检查
     * @author   Devil
     * @blog    http://gong.gg/
     * @version 1.0.0
     * @date    2018-12-28
     * @desc    description
     */
    public function Check()
    {
        $this->IsInstall();
        $this->behavior_obj->ReportInstallLog(['msg'=>'环境检测']);
        return MyView();
    }

    /**
     * 创建数据库
     * @author   Devil
     * @blog    http://gong.gg/
     * @version 1.0.0
     * @date    2018-12-28
     * @desc    description
     */
    public function Create()
    {
        $this->IsInstall();
        $this->behavior_obj->ReportInstallLog(['msg'=>'数据信息填写']);

        MyViewAssign('charset_type_list' , $this->charset_type_list);
        return MyView();
    }

    /**
     * 完成
     * @author   Devil
     * @blog    http://gong.gg/
     * @version 1.0.0
     * @date    2018-12-28
     * @desc    description
     */
    public function Successful()
    {
        SystemService::SystemInstallCheck();
        return MyView();
    }

    /**
     * 确认、安装应用模块创建数据库配置文件
     * @author   Devil
     * @blog    http://gong.gg/
     * @version 1.0.0
     * @date    2018-12-28
     * @desc    description
     */
    public function Confirm()
    {
        // 是否ajax
        if(!IS_AJAX)
        {
            MyViewAssign('msg', '非法访问');
            return MyView('public/error');
        }

        // 参数
        $params = input('post.');
        $ret = $this->ParamsCheck($params);
        if($ret['code'] != 0)
        {
            $this->behavior_obj->ReportInstallLog(['msg'=>'参数校验['.$ret['msg'].']']);
            return $ret;
        }

        // 配置文件校验
        if(file_exists(ROOT.'config/database.php'))
        {
            $this->behavior_obj->ReportInstallLog(['msg'=>'你已经安装过该系统，重新安装需要先删除[./config/database.php 文件]']);
            return DataReturn('你已经安装过该系统，重新安装需要先删除[./config/database.php 文件]', -1);
        }

        // 安装应用数据库配置文件生成
        return $this->CreateDbConfig(APP_PATH.'install/config', $params);
    }

    /**
     * 安装
     * @author   Devil
     * @blog    http://gong.gg/
     * @version 1.0.0
     * @date    2018-12-28
     * @desc    description
     */
    public function Add()
    {
        // 是否ajax
        if(!IS_AJAX)
        {
            MyViewAssign('msg', '非法访问');
            return MyView('public/error');
        }

        // 开始安装
        $params = input('post.');
        $db = $this->DbObj($params);
        if(!is_object($db))
        {
            $this->behavior_obj->ReportInstallLog(['msg'=>'数据库连接失败']);
            return DataReturn('数据库连接失败', -1);
        }

        // mysql版本
        $ret = $this->IsVersion($db, $params['DB_CHARSET']);
        if($ret['code'] != 0)
        {
            return $ret;
        }

        // 创建数据表
        $ret = $this->CreateTable($db, $params);
        if($ret['code'] != 0)
        {
            return $ret;
        }

        // 生成数据库配置文件
        $ret = $this->CreateDbConfig(ROOT.'config', $params);
        if($ret['code'] == 0)
        {
            $ret['msg'] = '安装成功';
            $this->behavior_obj->ReportInstallLog(['msg'=>'安装成功']);
        }
        return $ret;
    }

    /**
     * 生成配置文件
     * @author   Devil
     * @blog    http://gong.gg/
     * @version 1.0.0
     * @date    2018-12-28
     * @desc    description
     * @param   [string]         $dir       [目录地址]
     * @param   [array]          $params    [输入参数]
     */
    private function CreateDbConfig($dir, $params = [])
    {
        // 配置文件信息处理
        $db_str=<<<php
<?php
// +----------------------------------------------------------------------
// | ShopXO 国内领先企业级B2C免费开源电商系统
// +----------------------------------------------------------------------
// | Copyright (c) 2011~2099 http://shopxo.net All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( https://opensource.org/licenses/mit-license.php )
// +----------------------------------------------------------------------
// | Author: Devil
// +----------------------------------------------------------------------

// +----------------------------------------------------------------------
// | 数据库配置
// +----------------------------------------------------------------------
return [
    // 默认使用的数据库连接配置
    'default'         => 'mysql',

    // 自定义时间查询规则
    'time_query_rule' => [],

    // 自动写入时间戳字段
    // true为自动识别类型 false关闭
    // 字符串则明确指定时间字段类型 支持 int timestamp datetime date
    'auto_timestamp'  => true,

    // 时间字段取出后的默认时间格式
    'datetime_format' => 'Y-m-d H:i:s',

    // 数据库连接配置信息
    'connections'     => [
        'mysql' => [
            // 数据库类型
            'type'            => '{$params['DB_TYPE']}',
            // 服务器地址
            'hostname'        => '{$params['DB_HOST']}',
            // 数据库名
            'database'        => '{$params['DB_NAME']}',
            // 用户名
            'username'        => '{$params['DB_USER']}',
            // 密码
            'password'        => '{$params['DB_PWD']}',
            // 端口
            'hostport'        => '{$params['DB_PORT']}',
            // 数据库连接参数
            'params'          => [
                \PDO::ATTR_CASE => \PDO::CASE_LOWER,
                \PDO::ATTR_EMULATE_PREPARES => true,
            ],
            // 数据库编码默认采用utf8mb4
            'charset'         => '{$params['DB_CHARSET']}',
            // 数据库表前缀
            'prefix'          => '{$params['DB_PREFIX']}',

            // 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器)
            'deploy'          => 0,
            // 数据库读写是否分离 主从式有效
            'rw_separate'     => false,
            // 读写分离后 主服务器数量
            'master_num'      => 1,
            // 指定从服务器序号
            'slave_no'        => '',
            // 是否严格检查字段是否存在
            'fields_strict'   => true,
            // 是否需要断线重连
            'break_reconnect' => false,
            // 监听SQL
            'trigger_sql'     => true,
            // 开启字段缓存
            'fields_cache'    => false,
        ]
    ]
];
?>
php;
        if(@file_put_contents($dir.'/database.php', $db_str) === false)
        {
            $this->behavior_obj->ReportInstallLog(['msg'=>'配置文件创建失败['.$dir.']']);
            return DataReturn('配置文件创建失败', -1);
        }
        return DataReturn('操作成功', 0);
    }

    /**
     * @author   Devil
     * @blog    http://gong.gg/
     * @version 1.0.0
     * @date    2018-12-28
     * @desc    description
     * @param   [object]          $db      [db对象]
     * @param   [array]           $params  [输入参数]
     */
    private function CreateTable($db, $params)
    {
        if(!file_exists(ROOT.'config/shopxo.sql'))
        {
            $this->behavior_obj->ReportInstallLog(['msg'=>'数据库sql文件不存在']);
            return DataReturn('数据库sql文件不存在', -1);
        }

        // sql文件
        $sql = file_get_contents(ROOT.'config/shopxo.sql');

        // 替换表前缀
        if($params['DB_PREFIX'] != 'sxo_')
        {
            $sql = str_replace("`sxo_", " `{$params['DB_PREFIX']}", $sql);
        }

        // 编码替换,utf8mb4则做替换操作
        $charset = $this->charset_type_list[$params['DB_CHARSET']];
        if($charset['charset'] != 'utf8mb4')
        {
            $sql = str_replace('SET NAMES utf8mb4;', "SET NAMES {$charset['charset']};", $sql);
            $sql = str_replace('CHARSET=utf8mb4', "CHARSET={$charset['charset']}", $sql);
            $sql = str_replace('utf8mb4_general_ci', "{$charset['collate']}", $sql);
        }

        // 转为数组
        $sql_all = preg_split("/;[\r\n]+/", $sql);

        $success = 0;
        $failure = 0;
        foreach($sql_all as $v)
        {
            if (!empty($v))
            {
                if($db->execute($v) !== false)
                {
                    $success++;
                } else {
                    $failure++;
                }               
            }
        }

        $result = [
            'success'   => $success,
            'failure'   => $failure,
        ];
        $this->behavior_obj->ReportInstallLog(['msg'=>'sql运行 成功['.$success.']条, 失败['.$failure.']条']);
        if($failure > 0)
        {
            return DataReturn('sql运行失败['.$failure.']条', -1);
        }

        return DataReturn('success', 0, $result);
    }

    /**
     * 数据库版本校验
     * @author   Devil
     * @blog    http://gong.gg/
     * @version 1.0.0
     * @date    2018-12-28
     * @desc    description
     * @param   [object]          $db           [db对象]
     * @param   [string]          $db_charset   [数据库编码]
     */
    private function IsVersion($db, $db_charset)
    {
        $data = $db->query('SELECT VERSION() AS `version`');
        if(empty($data[0]['version']))
        {
            $this->behavior_obj->ReportInstallLog(['msg'=>'查询数据库版本失败']);
            return DataReturn('查询数据库版本失败', -1);
        } else {
            $mysql_version = str_replace('-log', '', $data[0]['version']);
            if($mysql_version < $this->charset_type_list[$db_charset]['version'])
            {
                $msg = '数据库版本过低、需要>='.$this->charset_type_list[$db_charset]['version'].'、当前'.$mysql_version;
                $this->behavior_obj->ReportInstallLog(['msg'=>$msg, 'mysql_version'=>$mysql_version]);
                return DataReturn($msg, -1);
            }
        }
        return DataReturn('success', 0);
    }

    /**
     * 获取数据库操作对象
     * @author   Devil
     * @blog    http://gong.gg/
     * @version 1.0.0
     * @date    2018-12-28
     * @desc    description
     * @param   array           $params  [输入参数]
     * @param   string          $db_name [数据库名称]
     */
    private function DbObj($params = [], $db_name = '')
    {
        return Db::connect('mysql');
    }

    /**
     * 参数校验
     * @author   Devil
     * @blog    http://gong.gg/
     * @version 1.0.0
     * @date    2018-12-28
     * @desc    description
     * @param   [array]           $params [输入参数]
     */
    private function ParamsCheck($params = [])
    {
        // 请求类型
        $p = [
            [
                'checked_type'      => 'empty',
                'key_name'          => 'DB_TYPE',
                'error_msg'         => '请选择数据库类型',
            ],
            [
                'checked_type'      => 'in',
                'key_name'          => 'DB_CHARSET',
                'checked_data'      => array_column($this->charset_type_list, 'charset'),
                'error_msg'         => '请选择数据编码',
            ],
            [
                'checked_type'      => 'empty',
                'key_name'          => 'DB_HOST',
                'error_msg'         => '请填写数据库服务器地址',
            ],
            [
                'checked_type'      => 'empty',
                'key_name'          => 'DB_PORT',
                'error_msg'         => '请填写数据库端口',
            ],
            [
                'checked_type'      => 'empty',
                'key_name'          => 'DB_NAME',
                'error_msg'         => '请填写数据库名',
            ],
            [
                'checked_type'      => 'empty',
                'key_name'          => 'DB_USER',
                'error_msg'         => '请填写数据库用户名',
            ],
            [
                'checked_type'      => 'empty',
                'key_name'          => 'DB_PWD',
                'error_msg'         => '请填写数据库密码',
            ],
            [
                'checked_type'      => 'empty',
                'key_name'          => 'DB_PREFIX',
                'error_msg'         => '请填写数据表前缀',
            ],
        ];
        $ret = ParamsChecked($params, $p);
        if($ret !== true)
        {
            return DataReturn($ret, -1);
        }
        return DataReturn('success', 0);
    }
}
?>