<?php
/**
* MVC主程序入口
*
* @author hzqghost <313143468@qq.com>
* @copyright 2020 yipart.com
* @version 1.0.0
* @license GPL
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
*/
final class mvc{
    /**
    * 系统全量配置
    */
    public static $cfg = null;
    /**
    * 接收的参数,GET + POST + php://input
    *
    * 经过了 htmlspecialchars 转义，有反转需求时使用 htmlspecialchars_decode()
    *
    * 关于 $_SESSION 为安全计，前端模板取值，使用 echo $session['变量']; 仅只读
    */
    public static $req = []; //
    public static $argv = null; //
    /**
    * 上传的文件列表
    *
    * 对 $cfg['upload'] 的规则进行过滤
    */
    public static $reqfiles = [];
    /**
    * 网址传参，纯值类型，不带键名
    */
    private static $reqUriValues = [];
    /**
    * 当前视图名称
    * @ignore
    */
    public static $activeViewFile = null;
    /**
    * 错误代码
    * @ignore
    */
    public static $errorCode = [];
    /**
    * 本地化内容,如语言包等
    * @ignore
    */
    public static $localeData = [];
    public static $localeData2 = [];
    /**
    * 本地化内容,如语言包等
    * @ignore
    */
    public static $_SERVER_PATH_INFO = '';
    /**
    * 类静态地图
    */
    private static $classMap = null;
    /**
    * 当前类名
    * @ignore
    */
    public static $class = null;
    /**
    * 当前方法名或html文件
    * @ignore
    */
    public static $method = null;
    /**
    * 是否请求pathinfo
    * @ignore
    */
    private static $isPathInfo = false;
    /**
    * 数据库连接与事务计数
    * @ignore
    */
    public static $dblinks = [];
    public static $transactionRegi = [];
    public static $transactionError = []; //事务执行过程中的错误信息
    /**
    * websocket 客户端
    */
    public static $websocketClient = null;
    /**
    * 退出判定
    */
    public static $exitif = true;
    /**
    * 是否是导出输出模式
    */
    public static $isExportOutput = false;
    /**
    * 导出输出模式下的数据
    */
    public static $ExportOutputData = null;
    /**
    * 对象容器
    * @ignore
    */
    public static $container = null;
    /**
    * 事件列表
    * @ignore
    */
    public static $listenList=null;
    /**
    * 共同应答参数列表
    * @ignore
    */
    public static $answerList=[];
    /**
    * 临时缓存
    * @ignore
    */
    public static $tempCache=[];
    
    /**
     * 类静态地图&自动加载注册
     * @return void
     * @ignore
     */
    private static function autoload_register(){
        //公共类映射地图
        if(isset(self::$cfg['classMap'])){
            self::$classMap = self::$cfg['classMap'];
        }

        //加载MVC全局通用函数
        require_once(self::$cfg['mvcDir'] . '/function.php');

        //加载工程全局通用函数(如果有的话)
        @include_once(self::$cfg['projectDir'] . '/function/function.php');

        //加载站点全局通用函数(如果有的话)
        @include_once(self::$cfg['projectDir'] . '/function/'. self::$cfg['site'] .'.php');

        //注册__autoload()函数
        spl_autoload_register ('\mvc::autoload');

        //注册 composer
        require( self::$cfg['mvcDir'] . '/../composer/vendor/autoload.php');
    }
    /**
     * 仅MVC主程使用
     * @param string $dir
     * @return string
     * @ignore
    */
    private static function formatDir($dir){
        return str_replace('\\','/',$dir);
    }
    /**
     * 自动加载类
     * @return void
     * @ignore
     */
    public static function autoload($class)
    {
        $pos = strpos($class, '\\');
        $first = substr($class,0,$pos);
        if(isset(self::$classMap[$class])){
            /* 静态类加载地图,一般在config/common.php配置 */
            $classFile = self::$classMap[$class];
        }elseif($pos === false){
            //mvc 一级基类
            $classFile = self::formatDir(self::$cfg['mvcDir'] . '/' .$class .'.php');
        }elseif($first =='lib'){
            //mvc lib类
            $classFile = self::formatDir(self::$cfg['mvcDir'] .'/'. $class .'.lib.php');
        }elseif($first =='helper'){
            //mvc static类
            $classFile = self::formatDir(self::$cfg['mvcDir'] .'/helper.php');
        }elseif($first =='kit'){
            //套件
            $classFile = self::formatDir(self::$cfg['mvcDir'] .'/'. $class .'.php');
        }elseif($first =='three'){
            //手动封的第三方类库
            $classFile = self::formatDir(self::$cfg['mvcDir'] .'/../'. $class .'.php');
        }else{
            $classFile = self::formatDir(self::$cfg['projectDir'] . '/' .$class .'.php');
        }
        $php_errormsg = null;
        include_once($classFile);
        if(DEBUG && $php_errormsg != null){
            $backtrace = debug_backtrace();
            dump('mvc::autoload');
            dump($class,$classFile);
            dd($backtrace);
        }
    }

    /**
     * 加载配置文件
     * @param string $tag 配置文件标识 如 config/import.php 就传 import
     * @return \mvc::$import
     */
    public static function loadConfig($tag)
    {
        if(empty(\mvc::$cfg[$tag])){
            \mvc::$cfg[$tag] = include_once(self::$cfg['projectDir'].'/config/'.$tag.'.php');
        }
    }

    /**
     * 格式化参数
     * @param $value
     * @param $isJson
     * @return string
     */
    public static function addslashes($value,$isJson = false)
    {
        if(!$isJson && is_string($value) && in_array(substr($value, 0, 1), ['{', '[']) && in_array(substr($value, -1), ['}', ']']) && json_decode($value))
            $isJson = true;
        return $isJson ? htmlspecialchars($value, ENT_NOQUOTES) : escape($value);
        //return $isJson ? htmlspecialchars($value, ENT_NOQUOTES) : htmlspecialchars(escape($value), ENT_QUOTES);
    }

    /**
     * 初始化容器
     * @return void
     * @ignore
     */
    private static function _initContainer(){
        $providers  = include_once(self::$cfg['projectDir'].'/config/providers.php');
        $providers  = array_merge(self::$cfg['providers'], $providers);
        if(!$providers)
            return;
        foreach($providers as $abstract=>$item){
            self::createClosure($abstract,$item['params'],$item['single']);
        }
    }
    /**
    * 处理容器匿名函数,绑定及延迟创建依赖类
    * @param string $abstract 类名 来自 providers.php 的 key
    * @param array  $params 实例参数  来自 providers.php 的 value
    * @param bool   $single   是否为单例模式
    * @return void
    * @ignore
    */
    public static function createClosure($abstract, $params, $single){
        if(isset(self::$container[$abstract]))
            return false;
        if(substr($abstract,0,1)!='\\') $abstract = '\\'.$abstract;
        $class = new ReflectionClass($abstract);
        $constructor = $class->getConstructor();
        $args = null;
        if($constructor){
            foreach($constructor->getParameters() as $k => $v){
                $has = array_key_exists($k,$params);
                if($has){
                    $relyClassName = $v->getClass(); //获取当前参数的类名，如果有的话
                    if($relyClassName==null){
                        //不是一个类为普通传值
                        $args[$k] = $params[$k];
                    }else{
                        //是一个依赖类
                        $item = $params[$k];
                        self::createClosure($item['class'],$item['params'],$item['single']);
                        $args[$k] = function()use($item){
                            //通过匿名函数延迟创建依赖类
                            return make($item['class']);
                        };
                    }
                }else if(!$has && $v->isDefaultValueAvailable()){
                    $args[$k] = $v->getDefaultValue();
                }else{
                    throw new \Exception ($abstract . '::__construct'. ' ' .  lg('参数') .' $'.$v->name . ' ' .lg('没有传值') );
                }
            }
        }
        self::$container[$abstract] = [
            'single' => $single,
            'object' => null,
            'create' => function($newParams)use($abstract,$params,$args,$single){
                if($args){
                    foreach($args as $k=>$v){
                        if($v instanceOf Closure){
                            //创建依赖类
                            $args[$k]=$v();
                        }
                    }
                }
                $class = new ReflectionClass($abstract);
                $constructor = $class->getConstructor();
                if($constructor && $constructor->getParameters()){
                    if(!empty($newParams)){
                        //使用新的参数值
                        foreach($constructor->getParameters() as $k => $v){
                            if(array_key_exists($v->name,$newParams)){
                                $args[$k] = $newParams[$v->name];
                            }
                        }
                    }
                    return $class->newInstanceArgs($args);
                }else{
                    return new $abstract();
                }
            }
        ];
    }
    /**
     * 初始化事件
     * @return void
     * @ignore
     */
    private static function _initEvent(){
        $eventProviders  = include_once(self::$cfg['projectDir'].'/config/events.php');
        if(!$eventProviders)
            return;
        self::$listenList = $eventProviders;
    }
    /**
     * 通知事件
     * @return void
     * @ignore
     */
    public static function notifyEvent($object,$enentName,$isCheckHas=true,$listen=null,$isAsynchronous=false) {

        if(!$listen){
            $firstCall = true;
            $isAsynchronous = substr($enentName,0,1) == '&'; //是否为异步
            if($isAsynchronous) $enentName = substr($enentName,1);
            $listen = ($isAsynchronous) ? self::$listenList['asyn'][self::$cfg['site']] : self::$listenList['sync'];
        }
        list($eventNameGroup,$eventSubName) = explode('.',$enentName);
        if($eventSubName=='*'){
            foreach($listen as $k=>$v){
                list($eventClass,$eventMethod) = explode('.',$k);
                if($eventNameGroup == $eventClass){
                    self::notifyEvent($object, $k, $isCheckHas, $listen,$isAsynchronous);
                }
            }
        }else{
            if(isset($listen[$enentName])){
                if($isAsynchronous){
                    //异步事件处理
                    $params = self::$answerList;
                    $item = $listen[$enentName];
                    $params['___isAsyn'] = 1;
                    $params['___timeout'] = $item['timeout'];
                    $params['___isStore'] = $item['isStore'];
                    $action = url($item['action'],$params);
                    $phpfile = self::$cfg['dir']['site'] . '/index.php';
                    if($item['isStore']){
                        //持久化
                        $storePath = self::$cfg['dir']['cache'].'/asynEvent';
                        $storeFile = $storePath.'/' . md5($action);
                        if(!file_exists($storeFile)){
                            \helper\file::mkdir($storePath);
                            file_put_contents($storeFile,$action.PHP_EOL);
                        }else{
                            \helper\file::touch($storeFile);
                        }
                    }
                    if(MVC_OS_IS_WIN){
                        pclose(popen('start /B php '.$phpfile.' '.$action." &", 'r'));
                    }else{
                        pclose(popen('php '.$phpfile.' '.$action.' > /dev/null &', 'r'));
                    }
                }else{
                    foreach($listen[$enentName] as $k=>$v){
                        list($eventClass,$eventMethod) = explode('.',$k);
                        $eventObject = make($eventClass);
                        $eventObject->$eventMethod($object);
                        if($v!=null){
                            self::notifyEvent($eventObject, $k, true, $listen[$enentName],$isAsynchronous);
                        }
                    }
                }
            }else if($isCheckHas){
                throw new \Exception ( lg('不存在的事件名') .':  '.$enentName );
            }
        }
    }
    /**
     * 加载本地化信息
     * @param string $locale 域
     * @param string $type 类型 如 language
     * @return void
     * @ignore
     */
    public static function loadLocale($locale, $type){
        //语言包处理
        //if(self::$cfg['lang']==self::$cfg['localeDefaultLanguage']){
        if($_SESSION['lang']==self::$cfg['localeDefaultLanguage']){
            //默认语言没有语言包
            self::$localeData[$locale][$type] = null;
        }elseif($_SESSION['lang']!=''){
            $langfile = self::$cfg['dir']['locale'].'/'. $locale .'/'.$type.'/'.$_SESSION['lang'].'/field.php';
            $l1 = require($langfile);
            $langfile = self::$cfg['dir']['locale'].'/'. $locale .'/'.$type.'/'.$_SESSION['lang'].'/ui.php';
            $l2 = require($langfile);
            $langfile = self::$cfg['dir']['locale'].'/'. $locale .'/'.$type.'/'.$_SESSION['lang'].'/msg.php';
            $l3 = require($langfile);
            self::$localeData[$locale][$type] = array_merge($l3,$l2,$l1);
            unset($l1,$l2,$l3);
            return self::$localeData[$locale][$type];
        }
    }
    /**
     * 绑定错误处理
     * @return void
     */
    public static function bindError()
    {
        if(DEBUG && !MVC_SAPI_IS_CLI){
            error_reporting(E_ALL& ~E_NOTICE); //E_ALL& ~E_NOTICE
            ini_set("track_errors",true); //开启 $php_errormsg 变量
            register_shutdown_function('\mvc::rare_shutdown_catch_error_debug');
        }else if(!MVC_SAPI_IS_CLI){
            error_reporting(0);
            register_shutdown_function('\mvc::rare_shutdown_catch_error');
        }
    }
    
    /**
     * 分析网址
     * @return void
     */
    public static function parseurl($url){
        $pos = strpos($url,'?');
        if($pos !== false){
            if(MVC_SAPI_IS_CLI){
                $_GET = urlQuery2Array($url);
            }
            $url = substr($url,0,$pos);
        }
        $section = preg_split('/\/[v|k]\//',rtrim($url,'/').'/');
        $info = explode('/',trim($section[0],'/'));
        $uri_params = isset($section[1]) ? $section[1] : '';
        $method = array_pop($info);
        $pathinfo   = implode('\\',$info);
        if($pathinfo==''){
            $pathinfo = '\\'.\mvc::$cfg['site'].'\\action\\indexAction';
        }else{
            $pathinfo = '\\'.\mvc::$cfg['site'].'\\action\\'.$pathinfo.'Action';
        }
        if($method=='') $method = 'index';
        $class  = $pathinfo;
        
        //传数格式化处理
        if($uri_params){
            //网址传参
            //xxx.com/pn/a/1/b/2 键值对 参数$a=1,参数$b=2
            //xxx.com/pv/1/2 纯值 第一个参数，第二个参数
            $uri_params = trim($uri_params,'/');
            $uri_param_type = (stripos($url,'/k/')!==false) ? 'k' : 'v';
            $arr = explode('/',$uri_params);
            if(end($arr)=='pathinfo'){
                self::$isPathInfo = true;
                array_pop($arr);
            }
            $count = count($arr);
            $req = [];
            $reqUriValues = [];
            if($uri_param_type == 'k'){
                for($i=0;$i<$count-1;$i=$i+2){
                    $req[$arr[$i]] = $arr[$i+1];
                }
            }else{
                for($i=0;$i<$count;$i++){
                    $reqUriValues[$i] =  self::addslashes(urldecode($arr[$i]));
                }
            }
        }
        return [$class,$method,$uri_params,$req,$reqUriValues];	    
    }
    
    /**
     * MVC初始化
     * @param array $commonCfg 通用配置来自[工程目录]/config/commom.php
     * @todo 上传的文件类型与大小判断还未完成
     * @return void
     */
    public static function init($commonCfg,$argv){
        //配置文件处理
        self::$cfg = $commonCfg;
        $database  = require_once($commonCfg['projectDir'].'/config/database.php');
        self::$cfg = array_merge(self::$cfg, $database);
        $siteCfg   = require_once($commonCfg['projectDir'].'/config/'.$commonCfg['site'].'.php');
        self::$cfg = array_merge(self::$cfg, $siteCfg);

        //自动加载
        self::autoload_register();

        //初始化容器
        self::_initContainer();

        //初始化事件
        self::_initEvent();

        //加载本地化信息
        self::loadLocale(self::$cfg['locale'],'language');

        //错误处理
        self::bindError();

        //网址处理
        if(MVC_SAPI_IS_CLI){
            self::$argv = $argv;
            self::$_SERVER_PATH_INFO = trim($argv[1],'&');
        }else{
            self::$_SERVER_PATH_INFO = $_SERVER['PATH_INFO'];
        }
        list($class,$method,$uri_params,$req,$reqUriValues) = self::parseurl(self::$_SERVER_PATH_INFO);
        self::$class = $class;
        self::$method = $method;
        self::$cfg['class']      = self::$class;
        self::$cfg['method']     = self::$method;
        if($req){
            foreach ($req as $k=>$v){
                self::$req[$k] = $v;
            }
        }
        if($reqUriValues){
            foreach ($reqUriValues as $k=>$v){
                self::$reqUriValues[$k] = $v;
            }
        }
        
        foreach($_GET as $k=>$v){
            self::$req[$k] = $v;
        }
        if(!empty($_POST)){
            $post = $_POST;
        }else{
            $input = file_get_contents('php://input');
            $post = json_decode($input,true);
        }
        if($post){
            foreach($post as $k=>$v){
                self::$req[$k] = $v;
            }
        }

        //TODO 临时解决json无法解析问题
        foreach(self::$req as $k => &$v){
            if($k=='json') {
                continue;
            }
            switch (gettype($v)){
                    case "array":
                        //TODO 临时解决
                        foreach ($v as $k2 => &$item)
                        {
                            //三维数组
                            if(gettype($item) == 'array')
                            {
                                foreach ($item as &$vv)
                                {
                                    //四维数组
                                    if(gettype($vv) == 'array')
                                    {
                                        foreach ($vv as &$vvv)
                                        {
                                            $vvv = self::addslashes($vvv);
                                        }
                                    }
                                    else
                                    {
                                        $vv = self::addslashes($vv);
                                    }
                                }
                            }
                            else
                            {
                                $item = self::addslashes($item);
                                if($k=='where' && strpos($k2, '[')>0 && strpos($k2, ']')==false){
                                    unset(self::$req[$k][$k2]);
                                    self::$req[$k][$k2.']'] = $item;
                                }
                            }
                        }
                        break;
                    case "boolean":
                    case "integer":
                    case "double":
                    case "string":
                    case "object":
                    case "resource":
                    case "NULL":
                        if(self::isJson($v)){
                            $v = self::addslashes($v,true);
                        }else if(stripos($k, 'base64_') === 0){
                            //base64 urldecode 会有问题
                        }else{
                            $v = self::addslashes($v);
                        }
                    break;
            }
        }
        foreach($_FILES as $k => $vs){
            self::$reqfiles[$k] = $vs;
        }

        //禁止使用 _GET,_POST,_FILES标识
        unset($_GET);
        unset($_POST);
        unset($_FILES);

        // 错误代码
        self::$errorCode = [
            //通用
            1=>lg('提醒'),
            200=>lg('成功'),
            300=>lg('校验错误'),
            400=>lg('请求无效'),
            401=>lg('未授权'),
            402=>lg('禁止删除'),
            403=>lg('禁止访问'),
            404=>lg('没有找到资源'),
            500=>lg('服务端异常错误'),

            //数据操作
            1001=>lg('数据类型有误'),
            1002=>lg('不存在'),
            1003=>lg('读失败'),
            1004=>lg('写失败'),
            2000=>lg('执行数据库操作失败'),
            2001=>lg('提交的数据检验失败'),
            2002=>lg('出现重复的记录'),
            2003=>lg('超出范围'),
            2004=>lg('数据约束'),
        ];
    }

    public static function isJson($string)
    {
        if (is_string($string)) {
            @json_decode($string);
            return (json_last_error() === JSON_ERROR_NONE);
        }
        return false;
    }

    /**
     * 通过路由action与method获取视图模板名
     * @param stirng $action
     * @param stirng $method
     * @return strinng
     * @ignore
     */
    public static function getViewByAction($action,$method){
        $arr = explode('\\action\\',$action);
        return str_replace('\\','/',substr($arr[1],0,-6)) . '/'. $method;
    }
    /**
     * 执行当前路由映射
     * @return void
     * @throws Exception 模型与方法方法如果不存在将抛出异常
     */
    public static function execute(){
        //用户临时缓存
        self::$tempCache = [];
        //路由重定向
        $redirects  = include_once(self::$cfg['projectDir'].'/config/redirect.php');
        if($redirects){
            $key = substr(self::$class,0,-6) . '.' . self::$method;
            if(isset($redirects[$key])){
                $val = $redirects[$key]();
                if($val){
                    list(self::$class,self::$method) = explode('.',$val);
                    self::$class .= 'Action';
                }
            }
        }

        //调用辅助
        if(DEBUG){
            if(self::$method=='phpinfo' || self::$isPathInfo){
                $arr = self::$cfg;
                unset($arr['db']);
                unset($arr['safe']);
                dump($arr);
                phpinfo();
                exit();
            }
            if(isset(self::$req['runtime'])) $timeBegin = getUnixTimestamp();
        }
        //单例模式
        if(substr(self::$method,-5)=='.html'){
            $viewfile = self::getViewByAction(self::$class,substr(self::$method,0,-5));
            self::$req['viewfile'] = $viewfile;
            self::$method = '_CallHtml';
        }

        //创建控制器类
        $action = new self::$class();
        /* 检查类模型方法是否存在 */
        if (false == method_exists (self::$class, self::$method)) {
            throw new \Exception (self::$class.'->'.self::$method.'()'.lg('方法不存在') );
        }
        $params = [];
        $paramsWithKey = []; //用于前置与后置方法传参
        /* 反射, 配对参数对应关系,及处理默认值 */
        $reflector = new \ReflectionMethod($action,self::$method);
        $reflectorParameters = $reflector->getParameters();
        if(self::$reqUriValues){
            foreach($reflectorParameters as $k => $v){
                if(array_key_exists($k,self::$reqUriValues)){
                    self::$req[$v->name] = self::$reqUriValues[$k];
                }
            }
        }
        /*配对参数对应关系,及处理默认值 $paramsWithKey 用于 _CallBefore _CallEnd 调用*/
        foreach($reflectorParameters as $k => $v){
            if(array_key_exists($v->name, self::$req)){
                $params[$k] = self::$req[$v->name];
                $paramsWithKey[$v->name] = $params[$k];
            }elseif($v->isDefaultValueAvailable()){
                $params[$k] = $v->getDefaultValue();
                $paramsWithKey[$v->name] = $params[$k];
            }else{
                throw new \Exception (self::$class . '::'. self::$method . ' ' .  lg('参数') .' $'.$v->name . ' ' .lg('没有传值') );
            }
        }
        /* 非DEBUG模式下，先清空输出缓冲区 */
        if(!DEBUG) ob_clean();
        //ob_start();
        /* 执行Action前置 _callBegin方法 如果存在, 命令行下无效*/
        if (!MVC_SAPI_IS_CLI && method_exists (self::$class, '_CallBefore')) {
            call_user_func_array ( array ( $action , '_CallBefore' ), [self::$method,$paramsWithKey] );
        }

        /*异步调用处理 在命令行模式下*/
        if(MVC_SAPI_IS_CLI && isset(self::$req['___isAsyn'])){
            if(self::$req['___timeout']!='')
                set_time_limit(self::$req['___timeout']);
            //这是一个异步调用，设置即使客户端退出，进程依然运行
            ignore_user_abort(true);
            if(self::$req['___isStore']){
                $php_errormsg=null;
                error_reporting(E_ALL& ~E_NOTICE);
                $storePath = self::$cfg['dir']['cache'].'/asynEvent';
                $storeFile = $storePath.'/'.md5(self::$_SERVER_PATH_INFO);
                register_shutdown_function(function($storeFile){
                    if(file_exists($storeFile)){
                        if($php_errormsg==null){
                            \helper\file::remove($storeFile);
                        }else{
                            \helper\file::appendToFile($storeFile,$php_errormsg);
                        }
                    }
                },$storeFile);
            }
        }

        /*由定时器开始的任务 在命令行模式下*/
        if(MVC_SAPI_IS_CLI && isset(self::$req['___timer'])){
            error_reporting(0);
            set_time_limit(self::$req['___timeout']);
            ignore_user_abort(true);
            $timer = self::$req['___timer'];
            $processActionFile = '';
            if($timer!=''){
                $processActionDir =  self::$cfg['dir']['project'].'/timer/process/action';
                $processActionFile =  $processActionDir.'/'.$timer.'.'.getmypid();
                \helper\file::mkdir($processActionDir);
                file_put_contents($processActionFile,'');
            }
            register_shutdown_function(function($timer,$processActionFile){
                //如果是一个定时器任务,执行完成后，删除锁文件
                if($timer!=''){
                    @unlink(self::$cfg['dir']['project'].'/timer/process/lock_'.$timer);
                    @unlink($processActionFile);
                }
            },$timer,$processActionFile);
        }

        /* 执行Action */
        call_user_func_array ( array ( $action , self::$method ), $params );

        /* 执行Action后置 _callEnd 如果存在*/
        if (!MVC_SAPI_IS_CLI && method_exists (self::$class, '_CallEnd')) {
            call_user_func_array ( array ( $action , '_CallEnd' ), [self::$method,$paramsWithKey] );
        }

        //$out  =  ob_get_contents();
        //ob_end_flush();
        if(DEBUG){
            if(isset(self::$req['runtime'])){
                $useMemory = memory_get_usage();  //占用的内存
                $peakMemory = memory_get_peak_usage(); //分配的内存
                echo '<div style="background-color:#fff9ff;border: 1px #000 dashed;padding:20px;margin-top:20px">';
                echo lg('执行时间').': '. ((getUnixTimestamp() - $timeBegin) / 1000) .' ' . lg('秒'). "<br>\r\n";
                echo lg('占用内存').': '. sprintf("%01.2f", ($useMemory/1024) ) .' ' . lg('KB'). "<br>\r\n";
                echo lg('分配内存').': '. sprintf("%01.2f", ($peakMemory/1024) ) .' ' . lg('KB'). "<br>\r\n";
                dump(get_included_files());
                echo '</div>';
            }
        }
    }

    /**
     * Debug错误处理
     * @return void
     * @ignore
     */
    public static function rare_shutdown_catch_error_debug(){
        $error=error_get_last();
        if(strpos($error['message'],'Vtiful\Kernel')>0){
            echo ('<div style="color:red;padding:20px;text-align:center;border: 1px #faa dashed;">打开excel文件出错，请使用xlsx格式</div>');
            exit();
            return false;
        }
        if($error && in_array($error['type'],array(1,4,16,64,256,4096,E_ALL))){
            if(strpos($error['file'],'eval()')!==false){
                $error['message'] .= "\r\n<br>".self::$activeViewFile.' :'.$error['line'];
                $error['message'] .= "\r\n<br>".$error['file'];
                $error['file'] = self::$activeViewFile;
            }
            $codes = file($error['file']);
            $begin = $error['line'] - 20;
            $end   = $error['line'] + 20;
            $begin = ($begin<0) ? 0 : $begin;
            $end   = ($end>count($codes)) ? count($codes) : $end;
            $code_string = '<div style="background-color:#fff;color:#000000;border: 1px #ddd solid;padding:5px;">
                            <code style="font-size:16px;line-height:20px;border-bottom: 1px #eee dashed;word-break:break-all;white-space: pre-wrap;" >'.PHP_EOL;
            for($i=$begin;$i<=$end;$i++){
                if($i+1==$error['line']) $code_string.= '<div style="background-color:#FFCCCC;font-weight:bold">';
                $code_string.= '<span style="color:#666">'.str_pad(($i+1),5,' ',STR_PAD_LEFT) . "</span>\t".htmlspecialchars($codes[$i]);
                if($i+1==$error['line']) $code_string.= '</div>';
            }
            $code_string = phpCodeColor($code_string);
            $code_string .= '</code></div>';
            $arr = explode("\n",$error['message']);
            $error_message = str_replace("\n","<br>",$error['message']);
            $msg  = '<div style="font-weight:bold;font-size:16px;word-break: break-all">'.lg('发生异常').'<br>'.$arr[0].'</div>';
            $msg .= "<div style='color:#069;font-size:18px;'><hr>".json_encode(self::$req)."</div>";
            $msg .= '<div style="background-color:#fff;color:#666;border: 1px #ddd solid;padding:10px;color:#000;font-size:14px">'. $error_message .'</div>';
            $msg .= '<pre>'. $error['file'] . ' ('.$error['line'].')';
            $msg .= $code_string;

            echo '<div style="clear:both;word-wrap: break-word;font-family: monaco, Consolas, Menlo, Courier, monospace;font-size: 14px;border: 1px #c66 dashed;border-radius: 5px;margin:20px;padding: 20px;background-color:#FFFFE1">';
            echo $msg;
            echo '</div>';
            exit;
        }
    }
    /**
     * 非Debug错误处理
     * @ignore
     * @return void
     */
    public static function rare_shutdown_catch_error(){
        $error=error_get_last();
        if(strpos($error['message'],'Vtiful\Kernel')>0){
            echo ('<div style="color:red;padding:20px;text-align:center;border: 1px #faa dashed;">打开excel文件出错，请使用xlsx格式</div>');
            return false;
        }
        if($error && in_array($error['type'],array(1,4,16,64,256,4096,E_ALL))){
            $message = sprintf("%s\r\n%s", json_encode(self::$req),$error['message']);
            if(strpos($error['file'],'eval()')!==false){
                $message = self::$activeViewFile.' :('. $error['line'] . ") " . $message;
            }
            errorLog('program',$message);
            echo ('<div style="color:red;padding:20px;text-align:center;border: 1px #faa dashed;">'.lg('系统出现异常错误').'</div>');
        }
    }
}

/**
* 公共应答参数传递类(trait)
*
* action,model基类都内置 use 这个类
*
* 可用于多链路事件传递参数等
*
* 生命周期,同一PHP进程下
*/
trait answer{
    /**
     * 返回参数是否存在
     * @param stirng $name
     * @return bool
     */
    function exists($name){
        return array_key_exists($name, \mvc::$answerList);
    }
    /**
     * 返回参数的值
     * @param stirng $name
     * @throws Exception 如果参数没有被定义，将抛出一个错误，如果不想抛出，可先用 exists 判断
     * @return mixed
     */
    function get($name){
        if($this->exists($name))
            return \mvc::$answerList[$name];
        else{
            throw new \Exception( lg('answer 中没有定义') .': '. $name . ' '. lg('变量'));
        }
    }
    /**
     * 返回参数的值
     * @param stirng $name
     * @param mixed $value
     * @return void
     */
    function set($name, $value){
        \mvc::$answerList[$name] = $value;
    }
    /**
     * 清空所有参数与值
     * @return void
     */
    function clear(){
        \mvc::$answerList = [];
    }
}