<?php

namespace loyalsoft;

/**
 * 调试辅助脚本,目前主要集中在输出功能.
 * @version
 *          2.0.1 添加检查PHP version和extensions的方法. --gwang 2019年8月16日 15:15:48
 *          1.0.0 Created at 2016-7-1. by --gwang
 * @author gwang (mail@wanggangzero.cn)
 * @copyright © 2016-7-1, SJZ LoyalSoft Corporation & gwang. All rights reserved.
 */
class DebugHelper {

    /**
     * @return bool 调试输出是否开启
     */
    private static function isDebugging() {
        return defined("GAME_ONLINE") && !GAME_ONLINE;                          #  线上版屏蔽输出
    }

    /**
     * 尝试从array中取出一个命名变量, Ps.可以取消5.3中的Notice
     * @param arry $arr
     * @param string $strFieldName
     * @return mixed
     */
    private static function arr_get($arr, $strFieldName) {
        if (isset($arr[$strFieldName])) {
            return $arr[$strFieldName];
        }
        return null;
    }

//
// <editor-fold defaultstate="collapsed" desc="assert">

    /**
     * 逻辑断言，不受系统断言状态影响，可以用于生产环境的简单判断，比如：参数非法，参数缺失等
     * @param type $condition
     * @param int/string $err 如果是int值，则当作Errcode处理，否则当作字符串处理
     */
    static function assert($condition, $err) {
        if (!$condition) {                                                      # 断言失败
            if (is_int($err)) {                                                 # err是int值,则按照错误码查找错误提示信息
                $errMsg = GameConfig::errmsg_getItem($err);
                $msg = "";
                if (null != $errMsg) {
                    $msg = $errMsg->msg;
                }
                Err($err, $msg);
                CLog::err("断言失败!", $msg);
            } else {                                                            # 不是int值,则当做普通错误信息来处理
                Err(ErrCode::err_assert, $err);
                CLog::err("断言失败!", $err);
            }
        }
    }

    /**
     * 接管系统的断言失败处理函数（调试用，若要检查参数是否完整，请用Resp::assert()）
     * 注意：方法内部依赖于GAME_ONLINE标志，若为内网调试则打开断言，
     *       若为线上环境则关闭断言（提升安全性和部分性能）
     */
    static function cover_assert_handler() {
        assert_options(ASSERT_ACTIVE, !GAME_ONLINE);                            # 设置断言标志
        assert_options(ASSERT_BAIL, true);
        assert_options(ASSERT_WARNING, false);
        assert_options(ASSERT_CALLBACK, array('DebugHelper', 'my_assert_handler')); # 设置回调函数
    }

    /**
     *  断言处理函数
     * @param type $file
     * @param type $line
     * @param type $code
     * @param type $desc
     */
    private static function my_assert_handler($file, $line, $code, $desc) {
        $msg = "<hr>Assertion Failed:
  File '$file'<br />
  Line '$line'<br />
  Code '$code'<br />
  Desc '{$desc}'<br />
  <hr />";
        Err(Err::err_assert, $msg);                                             # 给客户端返回值
//        trigger_error($msg, E_USER_ERROR);                                    # 抛出一个err
    }

// </editor-fold>
//

    /**
     * 浏览器打印调试信息
     * @param mixed $msg 对象或数组将会被序列化为json串
     */
    public static function debug($msg) {
        if (self::isDebugging()) {
            if (is_array($msg) || is_object($msg)) {
                $msg = json_encode($msg);
            }
            echoLine('[' . TimeUtil::dtCurrent() . ']| ' . $msg);
        }
    }

    /**
     * var_dump输出变量,统一入口的话, 上线的时候通过统一变量关闭,防止漏删除的意外输出.
     * @param mixed $obj
     */
    public static function var_dump($obj) {
        if (!self::isDebugging()) {
            return;
        }
        $pos = self::get_call_stack();
        $stack = explode('<br/>', $pos);
        $n = 3 > count($stack) ? count($stack) : 3;
        for ($i = 0; $i < $n; $i++) {
            echoLine($stack[$i]);
        }
        echoLine();                                                             # 换个行
        var_dump($obj);
    }

    /**
     * 浏览器输出调用堆栈
     * @return void
     */
    public static function print_stack_trace() {
        /*         * * * *
         *  因为debug_print_backtrace()格式不太易读，自己调整下输出样式
         * * * */
        $html = self::get_call_stack();
        self::debug($html);
    }

    /**
     * 获得调用堆栈
     * @param int $levelnum 想要获得的调用层次,default is 3
     * @return string formated string
     */
    static public function get_call_stack($levelnum = 3) {
        $array = debug_backtrace();
        $outHtml = PHP_EOL;
        array_shift($array);                                                    # 移除get_call_stack函数自身
        $n = 1;
        foreach ($array as $row) {
            $outHtml .= "\t" . CommUtil::str2UTF8(self::arr_get($row, 'file'))  # windows下路径(gb2312)转码
                    . ', row: ' . self::arr_get($row, 'line') . ', method: '    # line
                    . self::arr_get($row, 'function') . ";<br/>" . PHP_EOL;     # func
            if ($levelnum <= $n++) {
                break;
            }
        }
        return $outHtml;
    }

    /**
     * 浏览器弹窗提示
     * @param string $msg
     */
    public static function alert($msg) {
        if (self::isDebugging()) {
            echo "<Script language=\"javascript\">alert(\"" . $msg . "\");</Script>";
        }
    }

}
