<?php

namespace loyalsoft;

/**
 * Json转换工具,兼容UTF8格式
 * @author gwang
 * @version: <br/>
 *   v1.3.0  优化decode方法, 检查是否json是合法json的同时, 将结果传递出来.
 *           去掉没有用到 json_last_error_msg()函数.
 *           去掉preg_replace的e参数用法, 完全使用preg_replace_callback.
 *           将v1.1.0中的改动移动到独立的方法中, 不干扰decode方法了.
 *                                                       gwang 2016.4.28  <br/>
 *   v1.1.0  (未完成测试)在json_decode中集成反序列化的功能.如果参数中给出了目标类型/对象,
 *         则返回目标类型的实例. Ps.数据还不能处理          gwang 2017.xx.xx <br/>
 *   v1.0.2  encode时增加判断,防止对string二次编码.        gwang 2016.10.17 <br/>
 *   v1.0.1  decode时加上了判断,如果不是json格式则返回原串. gwang 2016.4.28  <br/>
 *   v1.0.0  file copy and created.                       gwang 2016.4.28  <br/>
 */
class JsonUtil {

    /**
     * 对给定字符串尝试进行解析
     * @param string $string
     * @param mixed $obj (out) 解析出的对象
     * @return boolean 解析是否成功
     */
    private static function is_json($string, &$obj) {
        if (!is_string($string)) {                                              # 不是字符串类型的对象
            return false;
        }
        if (!preg_match('/^[[|{]/', ltrim($string))) {                          # 字符串开头不是[或{ 则判定为非json格式
//            CLogUtil::debuglog('捕获不符合json格式的字符串' . $string);
            return false;
        }
        ob_start();                                                             # 截获意外输出
        $obj = json_decode($string);                                            # 解析json
        ob_end_clean();                                                         # 抛弃输出
        return (json_last_error() == JSON_ERROR_NONE);                          # 返回
    }

    /**
     * 修正对字符串中中文的编码, Ps.(json_encode之后中文会转换为\u7956\u56fd形式的编码)
     * @param mixed $obj
     * @return string
     */
    public static function encode($obj) {
        $json = is_string($obj) ? $obj : json_encode($obj);                     # 防止对字符串二次编码
        if (!isset($GLOBALS['OS'])) {
            $GLOBALS['OS'] = "win32";
        }
        if ($GLOBALS['OS'] == "linux") {                                        # 还原中文字符串
            return preg_replace_callback('#\\\u([0-9a-f]{4})#i', function ($matches) {
                return iconv('UCS-2LE', 'UTF-8', pack('H4', $matches[1]));
            }, $json);
        } else if ($GLOBALS['OS'] == "win32") {                                 # 还原中文字符串
            return preg_replace_callback('#\\\u([0-9a-f]{4})#i', function ($matches) {
                return iconv('UCS-2BE', 'UTF-8', pack('H4', $matches[1]));
            }, $json);
        }
        return $json;
    }

    /**
     * 对于异常的参数,记录到日志
     * @param type $string
     * @return obj
     */
    public static function decode($string) {
        $obj = $string;
        if (!self::is_json($string, $obj)) {
//            CLogUtil_HP::log("decoding: " . gettype($string) . " "              # json解析失败,记录日志
//                    . substr(var_export($string, true), 0, 30) . '...', "JsonUtil");
//            CLogUtil_HP::log(DebugHelper::get_call_stack(), "JsonUtil");
        }
        return $obj;
    }

    /**
     * 研发中..., 想着直接一步到位, 反序列化成目标类型的对象. -by gwang 2017年8月8日 09:49:09
     * @param string $str json字符串
     * @param mixed $tarType 目标类型
     * @return obj
     */
    public static function decode2Object($str, $tarType = null) {
        $obj = self::decode($str);
        if ($tarType != null) {
            $c = 'stdClass';
            if (is_string($tarType)) {                                          # 类型名称
                $c = $tarType;
            } else if (is_object($tarType)) {                                   # 目标类型的对象,数据还不能处理
                $c = get_class($tarType);
            }
            $desObj = new $c;
            CommUtil::loadObject($obj, $desObj);                                # 装到目标类型的箱子中
            $obj = $desObj;
        }
        return $obj;
    }

}
