<?php

namespace loyalsoft;

/**
 * 服务端程序入口(单一入口)
 * method: post/get 皆可
 * params: 一个经过(压缩)转码的json串.
 * @author gwang (mail@wanggangzero.cn)
 * @version 2018.11.08
 * @modifys
 *      2018.11.08 将index.php原来的代码整理到Index类中，使用到的全局变量用类的静态变量打理起来。
 *                 给$req的全局静态变量增加req()访问方式。
 *                 对于逻辑中的ErrResp, 添加Err方法，直接截断逻辑，返回响应结果。   -- gwang
 *      2018.04.05 去掉返回值中的content-encoding信息,开发了配套的fiddler插件,不需要自动识别机制了. --gwang
 *      2017.11.15 调整传输层协议, get的返回值也采用deflate编码.
 *      2017.07.19 代码优化, 格式整理
 *      2016.4.26  引入客户端检查机制,来源为模拟客户端时输出调试信息.
 *      2016.4.25  echo 中的连字符改为逗号表达式.
 */
class Index {

    /**
     * @var boolean 请求方式是否为Post
     */
    static $isPost = false;

    /**
     * @var string 调试信息
     */
    static $debugMsg = "";

    /**
     * @var string user-agent
     */
    static $ua = "";

    /**
     * @var boolean 调试模式
     */
    static $isDebugging = false;

    /**
     * @var int 超大响应的判断标准50kb
     */
    static $bigRespInKbs = 50;

    static function Init() {
        ob_start();                                                             # 开启一重output_buffer截获意外输出.
        self::$isPost = (isset($_SERVER['REQUEST_METHOD']) ? ($_SERVER['REQUEST_METHOD'] == 'PUT' || $_SERVER['REQUEST_METHOD'] == 'POST' ) : FALSE); # 判断是否为Post方式
        self::$debugMsg = "";                                                   # 记录调试信息
        self::$ua = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : "";                # 客户端标志
        self::$isDebugging = isset(self::$ua) && \strncmp(self::$ua, "WebRequest", 10) == 0; # 是否将ob_buff的输出信息附加到返回值
        self::$bigRespInKbs = self::$isDebugging ? 1 : 50;                      # 记录超大返回值,测试模式1k,正式50k.
        include_once __DIR__ . '/Services/AppServer.php';                       # 引入主程序
        $data = query_string();                                                 # 取参数
        if (self::$isDebugging) {                                               # 调试模式=>性能记录
            $rt = new RenderTime();                                             # 计时器, 默认开始计时
        }
# 传输层解码(压缩、去除控制字符)
# 目前规则: Post发送数据时使用deflate编码, GET方式发送数据时使用base64编码
        $data = self::$isPost ? gzinflate($data) : base64_decode($data);
//$data = base64_decode($data);
# 加密层解码, 加密传输, 保障数据安全
# ....
        $req = JsonUtil::decode($data);                                         # 请求数据, 数据层协议JSON
        if (self::$isDebugging) {                                               # 刷调试信息
            $rt->end();                                                         # 计时器结束
            self::$debugMsg .= '<br/>解压耗时:' . $rt->getRenderTime();          # 解码耗时
            # 计时器开始
        }
        new Req($req);                                                          # 初始化req()
    }

    static function Run() {
        if (self::$isDebugging) {                                               # 调试模式=>性能记录
            $rt = new RenderTime();                                             # 计时器, 默认开始计时
            $rt->start();
        }
        $app = new AppServer();                                                 # 初始化业务框架
        $resp = $app->api();                                                    # 分发处理逻辑
        if (self::$isDebugging) {                                               # 组织调试信息
            $rt->end();                                                         # 计时器结束
            $ms = round($rt->getRenderTime(false) / 1000, 2);                   # 耗时换算成毫秒
            $msi = $ms > 50 ? "<font color='red'>$ms</font>" : $ms;             # 超过50ms用红色显示
            self::$debugMsg .= "<br/>逻辑耗时: $msi 毫秒";                        # 逻辑耗时
            self::$debugMsg .= '<br/>内存分配:' . number_format(memory_get_peak_usage() / 1024) . 'kb,';
            self::$debugMsg .= '内存占用:' . number_format(memory_get_usage() / 1024) . 'kb';  # 内存使用数量(kb)
        }
        return $resp;
    }

    static function OverPass($resp) {
        $ret = JsonUtil::encode($resp);                                         # 返回值, 数据层协议:JSON
        $errinfo = ob_get_clean();                                              # 检查并截获异常输出
        if (self::$isDebugging) {                                               # 附加调试信息和异常输出
            $ret .= '<br/><hr/><font color=\'#6666FF\'>' . self::$debugMsg;     # 附加调试信息,
            if (strlen($errinfo) > 0) {                                         # 将捕获的异常输出附加到返回值
                $ret .= '<br/><br/>Unexpected output:<br/>' . $errinfo;
            }
            $ret .= '</font>';
        } else {
            if (!empty(self::$debugMsg)) {
                CLog::err(self::$debugMsg);
            }
        }
# 返回数据, 加密层编码, 加密传输, 保障数据安全
# ....
# 返回数据, 传输层编码(一律deflate压缩)
        if (php_sapi_name() === 'cli') {
            # 命令行的就算了, 要不肉眼看不懂了
        } else {
            $ret = gzdeflate($ret);                                             # 不管请求时使用的是什么方法, 返回时一律使用deflate压缩
        }
        $retLen = strlen($ret);                                                 # 计算下返回值长度.
        if ($retLen > (1024 * self::$bigRespInKbs)) {                           # 长度大于50k时记录超大返回值日志.
            CLog::warn("Big Response(" . req()->cmd . "):\n"                    # 带有\n转义符的时候, 必须用双引号
                    . "\t" . substr(JsonUtil::encode($resp), 0, 30)             # 返回串(截断为30bytes)
                    . "..., about " . CommUtil::floatToInt($retLen / 1024) . "kbs.", "Index"); # 返回串长度
        }
        return $ret;
    }

    /**
     * 直接返回错误resp并结束运行
     * @param int $err
     * @param string $msg
     */
    static function Err($err, $msg = "") {
        if (ErrCode::ok === $err) {
            CLog::err("Err()方法的参数err==ok, 那你还调个蛋的Err()!!!");
        }
        if (Index::$isDebugging) {                                              # 组织调试信息
            Index::$debugMsg .= "<br/><font color='red'>发生错误逻辑中断($err)：$msg</font>"; # 错误信息用红色显示
            Index::$debugMsg .= '<br/>内存分配:' . number_format(memory_get_peak_usage() / 1024) . 'kb,';
            Index::$debugMsg .= '内存占用:' . number_format(memory_get_usage() / 1024) . 'kb';  # 内存使用数量(kb)
        }
        if (is_string($err)) {                                                  # 防御Err中直接输入字符串的货
            $msg = "发生错误($err)直接中断逻辑执行." . $msg;
            CLog::err($msg);
            $err = ErrCode::err_innerfault;
        }
        $resp = Resp::err($err, $msg);
        $resp->AfterProc();
        exit(Index::OverPass($resp));                                           # 直接返回错误信息
    }
}

Index::Init();
$resp = Index::Run();
exit(Index::OverPass($resp));
