<?php

namespace loyalsoft;

include_once __DIR__ . '/../../main.php';
require_once ROOTDIR . '/util/OpenSSLVerify.php';                               # Ps. 这个文件和类名没有对应关系.


/**
 * Description of config
 * 数据中转功能.
 * 通过远程地址向服务器刷入Cmem的key-value数据. key和value通过gzcompress和base64处理.
 * @version
 *          2.0.0  增加了将数据写入到PHP文件的功能(CodeGen_Folder),配合客户端生成的读取代码,可以避免从
 *               redis中获取数据, 而是直接从代码中获取数据, 在有代码缓存的情况下,
 *               相当于直接使用内存中的数据, 节省了与redis的网络通讯时间(平均单次数据获取>1ms),
 *               如果一段代码中使用到的配置数据比较多, 那么节省的时间消耗相当明显. -gwang 2020.11.27
 *          1.0.1  通过openssl,RSA验证发送方具有刷数据的权限. -gwang 2017
 *          1.0.0  基础功能, 没有安全功能. -gwang 2017
 * @author gwang email:wanggangzero@qq.com
 * @copyright © 2015-12-1, SJZ LoyalSoft Corporation & gwang. All rights reserved.
 */

/**
 * Description of kvflush
 * 写一个key=>value对到目标CMEM
 * @author gwang
 */
class kvflush {

    /**
     * RSA 密钥路径
     */
    const keyPath = ROOTDIR . "/util/key";

    /**
     * 刷入数据
     */
    public static function flush() {
        InitZoneId();                                                           # 初始化分区信息 Ps. $_REQUEST["zoneid"]
        $params = HttpUtil::getQueryParas();
        $key = CommUtil::zb64decode(urldecode($params["key"]));
        $val = CommUtil::zb64decode(urldecode($params["val"]));
        $user = urldecode($params["user"]);
        $type = urldecode($params['type']);
        $sign = urldecode($params['sign']);
        $myrsa = new MyRsa(self::keyPath);
        if (!$myrsa->verify_arr($params, $sign)) {                              # 验证下签名
            exit("签名验证失败!");
        }
        unset($myrsa);
        switch ($type) {
            case 'hash':
                $val = json_decode($val);                                       # 需要还原为Obj才能解开
                $ret = self::FlushToMem_hmSet($key, $val);
                self::FlushToFile_hash($key, $val);
                break;
            case 'normal':
                $ret = self::FlushToMem_set($key, $val);
                self::FlushToFile_str($key, $val);
                break;
            default :
                $ret = self::FlushToMem_set($key, $val);
                self::FlushToFile_str($key, $val);
                break;
        }
        echo($ret ? "success" : "[$key] flush to mem failed!");
        self::log($user, $key);                                                 # 记录日志
    }

    static function log($user, $modelname) {
        $ip = HttpUtil::clientIP();
        $who = $user . ':' . "($ip)";
        $key = "log-flush";
        CLog::warn($who . "flushing  [$modelname]", __CLASS__);                 # 写文件
        $ret = gMem()->hset($key, $who, TimeUtil::tsYmdHis());                  # 写入mem
        return $ret;
    }

    /**
     * 普通写入方法
     * @param string $key
     * @param mixed $value
     * @return boolean
     */
    public static function FlushToMem_set($key, $value) {
        $mem = gMem();
        $mem->delete($key);
        $ret = $mem->set($key, $value);                                         # 写入mem
        return $ret;
    }

    /**
     * 写入哈希表
     * @param string $key
     * @param obj/assoc_arr $assoc_array
     * @return type
     */
    public static function FlushToMem_hmSet($key, $assoc_array) {
        if (!is_array($assoc_array)) {
            if (is_object($assoc_array)) {
                $assoc_arr = array();
                foreach ($assoc_array as $k => $v) {                            # 组合成符合hmset的关联数组
                    $assoc_arr[$k] = JsonUtil::encode($v);
                }
                $assoc_array = $assoc_arr;
            } else {
                return false;
            }
        }
        $ret = TRUE;
        $mem = gMem();
        if (count($assoc_arr) > 0) {
            $mem->delete($key);
            $ret = $mem->hmset($key, $assoc_array);                             # 写入
        }
        return $ret;
    }

// <editor-fold defaultstate="collapsed" desc=" 私有方法 ">

    /**
     * 获取输出路径
     * @return type
     */
    private static function getFolder() {
        if (defined('CodeGen_Folder')) {
            if (!dir(CodeGen_Folder)) {
                if (mkdir(CodeGen_Folder)) {
                    return CodeGen_Folder;
                }
            } else {
                return CodeGen_Folder;
            }
        }
        return ROOTDIR . '/configs/data/';
    }

    /**
     * 输出到文件
     * @param type $key
     * @param type $value
     */
    private static function writePHPFile($key, $value) {
        if (defined('CodeGen_Enabled') && CodeGen_Enabled) {                    # (部署环境)是否允许代码生成
            $file = fopen(self::getFolder() . $key . ".php", 'w');
            $str = "<?php " . PHP_EOL . " return  unserialize(gzinflate(base64_decode('" . base64_encode(gzdeflate(serialize($value), 9)) . "')));";
            fwrite($file, $str);
            fclose($file);
        }
    }

    /**
     * 向文件中输出一个字符串
     * @param type $key
     * @param type $value
     */
    private static function FlushToFile_str($key, $value) {
        if ($key == "gamecfg-client") {
            self::writePHPFile($key, $value);
        } else {
            self::writePHPFile($key, JsonUtil::decode($value));
        }
    }

    /**
     * 向文件中输入一个hash对象
     * @param type $key
     * @param type $assoc_array
     */
    private static function FlushToFile_hash($key, $assoc_array) {
        self::writePHPFile($key, $assoc_array);
    }

// </editor-fold>
}

HttpUtil::PostOnly();
kvflush::flush();                                                               # do it
