/** * 调试输出接口 * @access public * @param Response $response Response对象 * @param array $log 日志信息 * @return bool */ public function output(Response $response, array $log = []) { $request = Request::instance(); $contentType = $response->getHeader('Content-Type'); $accept = $request->header('accept'); if (strpos($accept, 'application/json') === 0 || $request->isAjax()) { return false; } elseif (!empty($contentType) && strpos($contentType, 'html') === false) { return false; } // 获取基本信息 $runtime = number_format(microtime(true), 8, '.', '') - THINK_START_TIME; $reqs = number_format(1 / $runtime, 2); $mem = number_format((memory_get_usage() - THINK_START_MEM) / 1024, 2); // 页面Trace信息 if (isset($_SERVER['HTTP_HOST'])) { $uri = $_SERVER['SERVER_PROTOCOL'] . ' ' . $_SERVER['REQUEST_METHOD'] . ' : ' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; } else { $uri = 'cmd:' . implode(' ', $_SERVER['argv']); } $base = ['请求信息' => date('Y-m-d H:i:s', $_SERVER['REQUEST_TIME']) . ' ' . $uri, '运行时间' => number_format($runtime, 6) . 's [ 吞吐率:' . $reqs . 'req/s ] 内存消耗:' . $mem . 'kb 文件加载:' . count(get_included_files()), '查询信息' => Db::$queryTimes . ' queries ' . Db::$executeTimes . ' writes ', '缓存信息' => Cache::$readTimes . ' reads,' . Cache::$writeTimes . ' writes', '配置加载' => count(Config::get())]; if (session_id()) { $base['会话信息'] = 'SESSION_ID=' . session_id(); } $info = Debug::getFile(true); // 页面Trace信息 $trace = []; foreach ($this->config['trace_tabs'] as $name => $title) { $name = strtolower($name); switch ($name) { case 'base': // 基本信息 $trace[$title] = $base; break; case 'file': // 文件信息 $trace[$title] = $info; break; default: // 调试信息 if (strpos($name, '|')) { // 多组信息 $names = explode('|', $name); $result = []; foreach ($names as $name) { $result = array_merge($result, isset($log[$name]) ? $log[$name] : []); } $trace[$title] = $result; } else { $trace[$title] = isset($log[$name]) ? $log[$name] : ''; } } } // 调用Trace页面模板 ob_start(); include $this->config['trace_file']; return ob_get_clean(); }
public static function getId() { $user = static::get(); if (isset($user['id'])) { return $user['id']; } else { Debug::output(new Exception("不存在请求的user_id")); } }
/** * 日志写入接口 * @access public * @param array $log 日志信息 * @return bool */ public function save(array $log = []) { if (IS_AJAX || IS_CLI || IS_API || 'html' != Config::get('default_return_type')) { // ajax cli api方式下不输出 return false; } // 获取基本信息 $runtime = microtime(true) - START_TIME; $reqs = number_format(1 / $runtime, 2); $runtime = number_format($runtime, 6); $mem = number_format((memory_get_usage() - START_MEM) / 1024, 2); // 页面Trace信息 $base = ['请求信息' => date('Y-m-d H:i:s', $_SERVER['REQUEST_TIME']) . ' ' . $_SERVER['SERVER_PROTOCOL'] . ' ' . $_SERVER['REQUEST_METHOD'] . ' : ' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'], '运行时间' => "{$runtime}s [ 吞吐率:{$reqs}req/s ] 内存消耗:{$mem}kb 文件加载:" . count(get_included_files()), '查询信息' => \think\Db::$queryTimes . ' queries ' . \think\Db::$executeTimes . ' writes ', '缓存信息' => \think\Cache::$readTimes . ' reads,' . \think\Cache::$writeTimes . ' writes', '配置加载' => count(Config::get())]; if (session_id()) { $base['会话信息'] = 'SESSION_ID=' . session_id(); } $info = Debug::getFile(true); // 获取调试日志 $debug = []; foreach ($log as $line) { $debug[$line['type']][] = $line['msg']; } // 页面Trace信息 $trace = []; foreach ($this->config['trace_tabs'] as $name => $title) { $name = strtolower($name); switch ($name) { case 'base': // 基本信息 $trace[$title] = $base; break; case 'file': // 文件信息 $trace[$title] = $info; break; default: // 调试信息 if (strpos($name, '|')) { // 多组信息 $names = explode('|', $name); $result = []; foreach ($names as $name) { $result = array_merge($result, isset($debug[$name]) ? $debug[$name] : []); } $trace[$title] = $result; } else { $trace[$title] = isset($debug[$name]) ? $debug[$name] : ''; } } } // 调用Trace页面模板 ob_start(); include $this->config['trace_file']; echo ob_get_clean(); return true; }
/** * 将html字符串输出返回给游览器 * 内部调用fetch和render方法,而非直接使用模板引擎的方法 * */ public function display($templateFile = '', $charset = '', $contentType = '', $content = '', $prefix = '') { // 标记统计 Debug::mark('viewStartTime'); // 视图开始标签 Tag::listen('view_begin', $templateFile); // 解析并获取模板内容 $content = $this->fetch($templateFile, $content, $prefix); // 输出模板内容 $this->render($content, $charset, $contentType); // 视图结束标签 Tag::listen('view_end'); }
public function run(&$content) { if (C('HTML_CACHE_ON') && defined('HTML_FILE_NAME')) { //静态文件写入 // 如果开启HTML功能 检查并重写HTML文件 // 没有模版的操作不生成静态文件 if (!is_dir(dirname(HTML_FILE_NAME))) { mkdir(dirname(HTML_FILE_NAME), 0755, true); } if (false === file_put_contents(HTML_FILE_NAME, $content)) { Debug::throw_exception(L('_CACHE_WRITE_ERROR_') . ':' . HTML_FILE_NAME); } } }
/** * 显示页面Trace信息 * @access private */ private function showTrace() { // 系统默认显示信息 $files = get_included_files(); $info = []; foreach ($files as $key => $file) { $info[] = $file . ' ( ' . number_format(filesize($file) / 1024, 2) . ' KB )'; } $trace = []; Debug::remark('START', NOW_TIME); $base = ['请求信息' => date('Y-m-d H:i:s', $_SERVER['REQUEST_TIME']) . ' ' . $_SERVER['SERVER_PROTOCOL'] . ' ' . $_SERVER['REQUEST_METHOD'] . ' : ' . $_SERVER['PHP_SELF'], '运行时间' => Debug::getUseTime('START', 'END', 6) . 's', '内存开销' => MEMORY_LIMIT_ON ? G('START', 'END', 'm') . 'b' : '不支持', '查询信息' => N('db_query') . ' queries ' . N('db_write') . ' writes ', '文件加载' => count($files), '缓存信息' => N('cache_read') . ' gets ' . N('cache_write') . ' writes ', '配置加载' => count(Config::get())]; // 读取项目定义的Trace文件 $traceFile = MODULE_PATH . 'trace.php'; if (is_file($traceFile)) { $base = array_merge($base, include $traceFile); } $debug = Log::getLog(); $tabs = Config::get('trace_page_tabs'); foreach ($tabs as $name => $title) { switch (strtoupper($name)) { case 'BASE': // 基本信息 $trace[$title] = $base; break; case 'FILE': // 文件信息 $trace[$title] = $info; break; default: // 调试信息 $name = strtoupper($name); if (strpos($name, '|')) { // 多组信息 $array = explode('|', $name); $result = []; foreach ($array as $name) { $result += isset($debug[$name]) ? $debug[$name] : []; } $trace[$title] = $result; } else { $trace[$title] = isset($debug[$name]) ? $debug[$name] : ''; } } } unset($files, $info, $base, $debug); // 调用Trace页面模板 ob_start(); include Config::has('tmpl_trace_file') ? Config::get('tmpl_trace_file') : THINK_PATH . 'tpl/page_trace.tpl'; return ob_get_clean(); }
/** * 日志写入接口 * @access public * @param array $log 日志信息 * @return void */ public function save($log = []) { // 获取基本信息 $current_uri = $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; $runtime = number_format(microtime(true) - START_TIME, 6); $reqs = number_format(1 / $runtime, 2); // 页面Trace信息 $base = ['请求信息' => date('Y-m-d H:i:s', $_SERVER['REQUEST_TIME']) . ' ' . $_SERVER['SERVER_PROTOCOL'] . ' ' . $_SERVER['REQUEST_METHOD'] . ' : ' . $current_uri, '运行时间' => "{$runtime}s [ 吞吐率:{$reqs}req/s ]", '内存消耗' => number_format((memory_get_usage() - START_MEM) / 1024, 2) . 'kb', '查询信息' => \think\Db::$queryTimes . ' queries ' . \think\Db::$executeTimes . ' writes ', '缓存信息' => \think\Cache::$readTimes . ' reads,' . \think\Cache::$writeTimes . ' writes', '文件加载' => count(get_included_files()), '配置加载' => count(\think\Config::get()), '会话信息' => 'SESSION_ID=' . session_id()]; $info = \think\Debug::getFile(true); // 获取调试日志 $debug = []; foreach ($log as $line) { $debug[$line['type']][] = $line['msg']; } // 页面Trace信息 $trace = []; foreach ($this->tabs as $name => $title) { $name = strtolower($name); switch ($name) { case 'base': // 基本信息 $trace[$title] = $base; break; case 'file': // 文件信息 $trace[$title] = $info; break; default: // 调试信息 if (strpos($name, '|')) { // 多组信息 $names = explode('|', $name); $result = []; foreach ($names as $name) { $result = array_merge($result, isset($debug[$name]) ? $debug[$name] : []); } $trace[$title] = $result; } else { $trace[$title] = isset($debug[$name]) ? $debug[$name] : ''; } } } // 调用Trace页面模板 ob_start(); include $this->config['trace_file']; echo ob_get_clean(); }
/** * 自动定位模板文件 * @access private * @param string $templateFile 文件名 * @return string */ private function parseTemplateFile($templateFile) { if ('' == $templateFile) { // 如果模板文件名为空 按照默认规则定位 $templateFile = C('TEMPLATE_NAME'); } elseif (false === strpos($templateFile, C('TMPL_TEMPLATE_SUFFIX'))) { // 解析规则为 模板主题:模块:操作 不支持 跨项目和跨分组调用 $path = explode(':', $templateFile); $action = array_pop($path); $module = !empty($path) ? array_pop($path) : CONTROLLER_NAME; if (!empty($path)) { // 设置模板主题 $path = dirname(THEME_PATH) . '/' . array_pop($path) . '/'; } else { $path = THEME_PATH; } $templateFile = $path . $module . C('TMPL_FILE_DEPR') . $action . C('TMPL_TEMPLATE_SUFFIX'); } if (!File::exists_case($templateFile)) { Debug::throw_exception(L('_TEMPLATE_NOT_EXIST_') . '[' . $templateFile . ']'); } return $templateFile; }
/** * 写入缓存 * @access public * @param string $name 缓存变量名 * @param mixed $value 存储数据 * @param int $expire 有效时间 0为永久 * @return boolen */ public function set($name, $value, $expire = null) { Debug::record('cache_write', 1); if (is_null($expire)) { $expire = $this->options['expire']; } $filename = $this->filename($name); $data = serialize($value); if (C('DATA_CACHE_COMPRESS') && function_exists('gzcompress')) { //数据压缩 $data = gzcompress($data, 3); } if (C('DATA_CACHE_CHECK')) { //开启数据校验 $check = md5($data); } else { $check = ''; } $data = "<?php\n//" . sprintf('%012d', $expire) . $check . $data . "\n?>"; $result = file_put_contents($filename, $data); if ($result) { if ($this->options['length'] > 0) { // 记录缓存队列 $this->queue($name); } clearstatcache(); return true; } else { return false; } }
/** * 发送数据到客户端 * @access public * @return mixed * @throws \InvalidArgumentException */ public function send() { // 处理输出数据 $data = $this->getContent(); // Trace调试注入 if (Env::get('app_trace', Config::get('app_trace'))) { Debug::inject($this, $data); } if (!headers_sent() && !empty($this->header)) { // 发送状态码 http_response_code($this->code); // 发送头部信息 foreach ($this->header as $name => $val) { header($name . ':' . $val); } } echo $data; if (function_exists('fastcgi_finish_request')) { // 提高页面响应 fastcgi_finish_request(); } }
/** * 数据库调试 记录当前SQL * @access protected * @param boolean $start 调试开始标记 true 开始 false 结束 */ protected function debug($start) { if (!empty($this->config['debug'])) { // 开启数据库调试模式 if ($start) { Debug::remark('queryStartTime', 'time'); } else { $this->modelSql[$this->model] = $this->queryStr; // 记录操作结束时间 Debug::remark('queryEndTime', 'time'); $log = $this->queryStr . ' [ RunTime:' . Debug::getRangeTime('queryStartTime', 'queryEndTime') . 's ]'; // SQL性能分析 if (0 === stripos(trim($this->queryStr), 'select')) { $pdo = $this->linkID->query("EXPLAIN " . $this->queryStr); $result = $pdo->fetch(PDO::FETCH_ASSOC); if (strpos($result['extra'], 'filesort') || strpos($result['extra'], 'temporary')) { Log::record('SQL:' . $this->queryStr . '[' . $result['extra'] . ']', 'warn'); } $log .= '[ EXPLAIN : ' . var_export($result, true) . ' ]'; } Log::record('[ SQL ] ' . $log, 'sql'); } } }
/** * 加载运行时所需要的文件 * 检查调试模式创建目录 * * @return void */ public static function load_runtime_file() { try { // 载入临时的Helper文件,将重构 include CORE_PATH . 'Library/Helper' . EXT; // 调试模式下检查路径和文件 if (APP_DEBUG) { // 创建项目目录结构 if (!is_dir(LIB_PATH)) { throw new Exception("不存在项目目录结构"); } // 检查缓存目录 if (!is_dir(CACHE_PATH)) { // 如果不存在Runtime则创建 if (!is_dir(RUNTIME_PATH)) { mkdir(RUNTIME_PATH); } else { if (!is_writeable(RUNTIME_PATH)) { throw new Exception(RUNTIME_PATH . "is no writeable"); } } // 检查并创建Runtime下的缓存目录 foreach (array(CACHE_PATH, LOG_PATH, TEMP_PATH, DATA_PATH) as $key => $value) { if (!is_dir($value)) { mkdir($value); } } } } // 记录文件加载时间 Debug::mark('loadTime'); // 启动 App::run(); } catch (Exception $error) { exit($error->getMessage()); } }
/** * 404处理 * 调试模式会抛异常 * 部署模式下面传入url参数可以指定跳转页面,否则发送404信息 * @param string $msg 提示信息 * @param string $url 跳转URL地址 * @return void */ public static function _404($message = '', $url) { if (APP_DEBUG) { Debug::output(new Exception($message)); } else { static::send_http_status(404); exit; } }
/** * 数据库调试 记录当前SQL * @access protected * @param boolean $start 调试开始标记 true 开始 false 结束 */ protected function debug($start) { if (!empty($this->config['debug'])) { // 开启数据库调试模式 if ($start) { Debug::remark('queryStartTime', 'time'); } else { $this->modelSql[$this->model] = $this->queryStr; // 记录操作结束时间 Debug::remark('queryEndTime', 'time'); Log::record($this->queryStr . ' [ RunTime:' . Debug::getRangeTime('queryStartTime', 'queryEndTime') . 's ]', 'sql'); } } }
/** * 执行应用程序 * @access public * @param Request $request Request对象 * @return Response * @throws Exception */ public static function run(Request $request = null) { is_null($request) && ($request = Request::instance()); if ('ico' == $request->ext()) { throw new HttpException(404, 'ico file not exists'); } $config = self::initCommon(); try { // 开启多语言机制 if ($config['lang_switch_on']) { // 获取当前语言 $request->langset(Lang::detect()); // 加载系统语言包 Lang::load(THINK_PATH . 'lang' . DS . $request->langset() . EXT); if (!$config['app_multi_module']) { Lang::load(APP_PATH . 'lang' . DS . $request->langset() . EXT); } } // 获取应用调度信息 $dispatch = self::$dispatch; if (empty($dispatch)) { // 进行URL路由检测 $dispatch = self::routeCheck($request, $config); } // 记录当前调度信息 $request->dispatch($dispatch); // 记录路由信息 self::$debug && Log::record('[ ROUTE ] ' . var_export($dispatch, true), 'info'); // 监听app_begin Hook::listen('app_begin', $dispatch); switch ($dispatch['type']) { case 'redirect': // 执行重定向跳转 $data = Response::create($dispatch['url'], 'redirect')->code($dispatch['status']); break; case 'module': // 模块/控制器/操作 $data = self::module($dispatch['module'], $config, isset($dispatch['convert']) ? $dispatch['convert'] : null); break; case 'controller': // 执行控制器操作 $data = Loader::action($dispatch['controller'], $dispatch['params']); break; case 'method': // 执行回调方法 $data = self::invokeMethod($dispatch['method'], $dispatch['params']); break; case 'function': // 执行闭包 $data = self::invokeFunction($dispatch['function'], $dispatch['params']); break; case 'response': $data = $dispatch['response']; break; default: throw new \InvalidArgumentException('dispatch type not support'); } } catch (HttpResponseException $exception) { $data = $exception->getResponse(); } // 清空类的实例化 Loader::clearInstance(); // 输出数据到客户端 if ($data instanceof Response) { $response = $data; } elseif (!is_null($data)) { // 默认自动识别响应输出类型 $isAjax = $request->isAjax(); $type = $isAjax ? Config::get('default_ajax_return') : Config::get('default_return_type'); $response = Response::create($data, $type); } else { $response = Response::create(); } // 监听app_end Hook::listen('app_end', $response); // Trace调试注入 if (Config::get('app_trace')) { Debug::inject($response); } return $response; }
/** * set_category_id获得方式 */ protected function set_category_id() { if ($this->category_id_require && !isset($_GET[$this->category_query_name])) { Debug::output(new Exception('没有提供:' . $this->category_query_name)); } if ($this->category_id_auto_set && isset($_GET[$this->category_query_name])) { $this->category_id = $_GET[$this->category_query_name]; } }
/** * 数据库错误信息 * 并显示当前的SQL语句 * @access public * @return string */ public function error() { $this->error = mysql_error($this->_linkID); if ('' != $this->queryStr) { $this->error .= "\n [ SQL语句 ] : " . $this->queryStr; } Debug::trace($this->error, '', 'ERR'); return $this->error; }
/** * 监听标签的行为 * @param string $tag 标签名称 * @param mixed $params 传入参数 * @param mixed $extra 额外参数 * @param bool $once 只获取一个有效返回值 * @return mixed */ public static function listen($tag, &$params = null, $extra = null, $once = false) { $results = []; if (isset(self::$tags[$tag])) { foreach (self::$tags[$tag] as $name) { if (App::$debug) { Debug::remark('behavior_start', 'time'); } $result = self::exec($name, $tag, $params, $extra); if (!is_null($result) && $once) { return $result; } if (App::$debug) { Debug::remark('behavior_end', 'time'); if ($name instanceof \Closure) { $name = 'Closure'; } elseif (is_object($name)) { $name = get_class($name); } Log::record('[ BEHAVIOR ] Run ' . $name . ' @' . $tag . ' [ RunTime:' . Debug::getRangeTime('behavior_start', 'behavior_end') . 's ]', 'info'); } if (false === $result) { // 如果返回false 则中断行为执行 break; } $results[] = $result; } } return $once ? null : $results; }
/** * 发送数据到客户端 * @access public * @return mixed * @throws \InvalidArgumentException */ public function send() { // 处理输出数据 $data = $this->getContent(); // Trace调试注入 if (Env::get('app_trace', Config::get('app_trace'))) { Debug::inject($this, $data); } if (!headers_sent() && !empty($this->header)) { // 发送状态码 http_response_code($this->code); // 发送头部信息 foreach ($this->header as $name => $val) { header($name . ':' . $val); } } if (200 == $this->code) { $cache = Request::instance()->getCache(); if ($cache) { header('Cache-Control: max-age=' . $cache[1] . ',must-revalidate'); header('Last-Modified:' . gmdate('D, d M Y H:i:s') . ' GMT'); header('Expires:' . gmdate('D, d M Y H:i:s', $_SERVER['REQUEST_TIME'] + $cache[1]) . ' GMT'); $header['Content-Type'] = $this->header['Content-Type']; Cache::set($cache[0], [$data, $header], $cache[1]); } } echo $data; if (function_exists('fastcgi_finish_request')) { // 提高页面响应 fastcgi_finish_request(); } // 监听response_end Hook::listen('response_end', $this); }
protected function parseWhereItem($key, $val) { $whereStr = ''; if (is_array($val)) { if (is_string($val[0])) { if (preg_match('/^(EQ|NEQ|GT|EGT|LT|ELT)$/i', $val[0])) { // 比较运算 $whereStr .= $key . ' ' . $this->comparison[strtolower($val[0])] . ' ' . $this->parseValue($val[1]); } elseif (preg_match('/^(NOTLIKE|LIKE)$/i', $val[0])) { // 模糊查找 if (is_array($val[1])) { $likeLogic = isset($val[2]) ? strtoupper($val[2]) : 'OR'; if (in_array($likeLogic, array('AND', 'OR', 'XOR'))) { $likeStr = $this->comparison[strtolower($val[0])]; $like = array(); foreach ($val[1] as $item) { $like[] = $key . ' ' . $likeStr . ' ' . $this->parseValue($item); } $whereStr .= '(' . implode(' ' . $likeLogic . ' ', $like) . ')'; } } else { $whereStr .= $key . ' ' . $this->comparison[strtolower($val[0])] . ' ' . $this->parseValue($val[1]); } } elseif ('exp' == strtolower($val[0])) { // 使用表达式 $whereStr .= ' (' . $key . ' ' . $val[1] . ') '; } elseif (preg_match('/IN/i', $val[0])) { // IN 运算 if (isset($val[2]) && 'exp' == $val[2]) { $whereStr .= $key . ' ' . strtoupper($val[0]) . ' ' . $val[1]; } else { if (is_string($val[1])) { $val[1] = explode(',', $val[1]); } $zone = implode(',', $this->parseValue($val[1])); $whereStr .= $key . ' ' . strtoupper($val[0]) . ' (' . $zone . ')'; } } elseif (preg_match('/BETWEEN/i', $val[0])) { // BETWEEN运算 $data = is_string($val[1]) ? explode(',', $val[1]) : $val[1]; $whereStr .= ' (' . $key . ' ' . strtoupper($val[0]) . ' ' . $this->parseValue($data[0]) . ' AND ' . $this->parseValue($data[1]) . ' )'; } else { Debug::throw_exception(L('_EXPRESS_ERROR_') . ':' . $val[0]); } } else { $count = count($val); $rule = isset($val[$count - 1]) ? strtoupper($val[$count - 1]) : ''; if (in_array($rule, array('AND', 'OR', 'XOR'))) { $count = $count - 1; } else { $rule = 'AND'; } for ($i = 0; $i < $count; $i++) { $data = is_array($val[$i]) ? $val[$i][1] : $val[$i]; if ('exp' == strtolower($val[$i][0])) { $whereStr .= '(' . $key . ' ' . $data . ') ' . $rule . ' '; } else { $op = is_array($val[$i]) ? $this->comparison[strtolower($val[$i][0])] : '='; $whereStr .= '(' . $key . ' ' . $op . ' ' . $this->parseValue($data) . ') ' . $rule . ' '; } } $whereStr = substr($whereStr, 0, -4); } } else { //对字符串类型字段采用模糊匹配 if (C('DB_LIKE_FIELDS') && preg_match('/(' . C('DB_LIKE_FIELDS') . ')/i', $key)) { $val = '%' . $val . '%'; $whereStr .= $key . ' LIKE ' . $this->parseValue($val); } else { $whereStr .= $key . ' = ' . $this->parseValue($val); } } return $whereStr; }
/** * @covers think\Debug::dump * @todo Implement testDump(). */ public function testDump() { if (strstr(PHP_VERSION, 'hhvm')) { return; } $var = []; $var["key"] = "val"; $output = Debug::dump($var, false, $label = "label"); $array = explode("array", json_encode($output)); if (IS_WIN) { $this->assertEquals("(1) {\\n [\\\"key\\\"] => string(3) \\\"val\\\"\\n}\\n\\r\\n\"", end($array)); } else { if (strstr(PHP_OS, 'Darwin')) { $this->assertEquals("(1) {\\n [\\\"key\\\"] => string(3) \\\"val\\\"\\n}\\n\\n\"", end($array)); } else { $this->assertEquals("(1) {\\n 'key' =>\\n string(3) \\\"val\\\"\\n}\\n\\n\"", end($array)); } } }
/** * 执行某个行为 * @param mixed $class 要执行的行为 * @param string $tag 方法名(标签名) * @param Mixed $params 传人的参数 * @param mixed $extra 额外参数 * @return mixed */ public static function exec($class, $tag = '', &$params = null, $extra = null) { App::$debug && Debug::remark('behavior_start', 'time'); if (is_callable($class)) { $result = call_user_func_array($class, [&$params, $extra]); $class = 'Closure'; } elseif (is_object($class)) { $result = call_user_func_array([$class, $tag], [&$params, $extra]); $class = get_class($class); } else { $obj = new $class(); $result = $tag && is_callable([$obj, $tag]) ? $obj->{$tag}($params, $extra) : $obj->run($params, $extra); } if (App::$debug) { Debug::remark('behavior_end', 'time'); Log::record('[ BEHAVIOR ] Run ' . $class . ' @' . $tag . ' [ RunTime:' . Debug::getRangeTime('behavior_start', 'behavior_end') . 's ]', 'info'); } return $result; }
/** * 数据库调试 记录当前SQL及分析性能 * @access protected * @param boolean $start 调试开始标记 true 开始 false 结束 * @return void */ protected function debug($start) { if (!empty($this->config['debug'])) { // 开启数据库调试模式 if ($start) { Debug::remark('queryStartTime', 'time'); } else { // 记录操作结束时间 Debug::remark('queryEndTime', 'time'); $runtime = Debug::getRangeTime('queryStartTime', 'queryEndTime'); $log = $this->queryStr . ' [ RunTime:' . $runtime . 's ]'; $result = []; // SQL性能分析 if ($this->config['sql_explain'] && 0 === stripos(trim($this->queryStr), 'select')) { $result = $this->getExplain($this->queryStr); } // SQL监听 $this->trigger($this->queryStr, $runtime, $result); } } }
/** * 数据库调试 记录当前SQL * @access protected * @param boolean $start 调试开始标记 true 开始 false 结束 */ protected function debug($start) { if ($this->config['debug']) { // 开启数据库调试模式 if ($start) { Debug::remark('queryStartTime', 'time'); } else { $this->modelSql[$this->model] = $this->queryStr; //$this->model = '_think_'; // 记录操作结束时间 Debug::remark('queryEndTime', 'time'); Log::record($this->queryStr . ' [ RunTime:' . Debug::getUseTime('queryStartTime', 'queryEndTime') . 's ]', 'SQL'); } } if (SLOG_ON && $start) { \think\Slog::sql($this->queryStr, $this->_linkID); } }
/** * 数据库调试 记录当前SQL * @access protected * @param boolean $start 调试开始标记 true 开始 false 结束 */ protected function debug($start) { if (!empty($this->config['debug'])) { // 开启数据库调试模式 if ($start) { Debug::remark('queryStartTime', 'time'); } else { $this->modelSql[$this->model] = $this->queryStr; // 记录操作结束时间 Debug::remark('queryEndTime', 'time'); $log = $this->queryStr . ' [ RunTime:' . Debug::getRangeTime('queryStartTime', 'queryEndTime') . 's ]'; // SQL性能分析 if (0 === stripos(trim($this->queryStr), 'select')) { $result = $this->getExplain($this->queryStr); Log::record('[ EXPLAIN : ' . var_export($result, true) . ' ]', 'sql'); } Log::record('[ SQL ] ' . $log, 'sql'); } } }
/** * @covers think\Debug::dump * @todo Implement testDump(). */ public function testDump() { $var = array(); $var["key"] = "val"; $output = \think\Debug::dump($var, false, $label = "label"); if (IS_WIN) { $this->assertEquals("(1) {\\n 'key' =>\\n string(3) \\\"val\\\"\\n}\\n\\r\\n\"", end(explode("array", json_encode($output)))); } else { $this->assertEquals("(1) {\\n 'key' =>\\n string(3) \\\"val\\\"\\n}\\n\\n\"", end(explode("array", json_encode($output)))); } }
/** * 浏览器友好的变量输出 * @param mixed $var 变量 * @param boolean $echo 是否输出 默认为true 如果为false 则返回输出字符串 * @param string $label 标签 默认为空 * @return void|string */ function dump($var, $echo = true, $label = null) { return \think\Debug::dump($var, $echo, $label); }
echo '<li style="border-bottom:1px solid #EEE;font-size:14px;padding:0 12px">' . (is_numeric($k) ? '' : $k . ' : ') . htmlentities(print_r($val, true), ENT_COMPAT, 'utf-8') . '</li>'; } } ?> </ol> </div> <?php } ?> </div> </div> <div id="think_page_trace_close" style="display:none;text-align:right;height:15px;position:absolute;top:10px;right:12px;cursor:pointer;"><img style="vertical-align:top;" src="data:image/gif;base64,R0lGODlhDwAPAJEAAAAAAAMDA////wAAACH/C1hNUCBEYXRhWE1QPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS4wLWMwNjAgNjEuMTM0Nzc3LCAyMDEwLzAyLzEyLTE3OjMyOjAwICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M1IFdpbmRvd3MiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MUQxMjc1MUJCQUJDMTFFMTk0OUVGRjc3QzU4RURFNkEiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MUQxMjc1MUNCQUJDMTFFMTk0OUVGRjc3QzU4RURFNkEiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDoxRDEyNzUxOUJBQkMxMUUxOTQ5RUZGNzdDNThFREU2QSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDoxRDEyNzUxQUJBQkMxMUUxOTQ5RUZGNzdDNThFREU2QSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PgH//v38+/r5+Pf29fTz8vHw7+7t7Ovq6ejn5uXk4+Lh4N/e3dzb2tnY19bV1NPS0dDPzs3My8rJyMfGxcTDwsHAv769vLu6ubi3trW0s7KxsK+urayrqqmop6alpKOioaCfnp2cm5qZmJeWlZSTkpGQj46NjIuKiYiHhoWEg4KBgH9+fXx7enl4d3Z1dHNycXBvbm1sa2ppaGdmZWRjYmFgX15dXFtaWVhXVlVUU1JRUE9OTUxLSklIR0ZFRENCQUA/Pj08Ozo5ODc2NTQzMjEwLy4tLCsqKSgnJiUkIyIhIB8eHRwbGhkYFxYVFBMSERAPDg0MCwoJCAcGBQQDAgEAACH5BAAAAAAALAAAAAAPAA8AAAIdjI6JZqotoJPR1fnsgRR3C2jZl3Ai9aWZZooV+RQAOw==" /></div> </div> <div id="think_page_trace_open" style="height:30px;float:right;text-align:right;overflow:hidden;position:fixed;bottom:0;right:0;color:#000;line-height:30px;cursor:pointer;"> <div style="background:#232323;color:#FFF;padding:0 6px;float:right;line-height:30px;font-size:14px"><?php echo \think\Debug::getUseTime() . 's '; ?> </div> <img width="30" style="" title="ShowPageTrace" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyBpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBXaW5kb3dzIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjVERDVENkZGQjkyNDExRTE5REY3RDQ5RTQ2RTRDQUJCIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjVERDVENzAwQjkyNDExRTE5REY3RDQ5RTQ2RTRDQUJCIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6NURENUQ2RkRCOTI0MTFFMTlERjdENDlFNDZFNENBQkIiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6NURENUQ2RkVCOTI0MTFFMTlERjdENDlFNDZFNENBQkIiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz5fx6IRAAAMCElEQVR42sxae3BU1Rk/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"> </div> <script type="text/javascript"> (function(){ var tab_tit = document.getElementById('think_page_trace_tab_tit').getElementsByTagName('span'); var tab_cont = document.getElementById('think_page_trace_tab_cont').getElementsByTagName('div'); var open = document.getElementById('think_page_trace_open'); var close = document.getElementById('think_page_trace_close').children[0]; var trace = document.getElementById('think_page_trace_tab'); var cookie = document.cookie.match(/thinkphp_show_page_trace=(\d\|\d)/); var history = (cookie && typeof cookie[1] != 'undefined' && cookie[1].split('|')) || [0,0]; open.onclick = function(){
/** * 空方法执行请求方法属性的检查 * 查找相对应的get, post, put, delete等方法并执行 * * @return void */ public function _empty() { try { $method = $this->_method; // 当前http请求方法 $current = strtolower($_SERVER['REQUEST_METHOD']); if (in_array($current, $method)) { $class = $current . '_' . ACTION_NAME; // 是否存在this/get_login if (method_exists($this, $class)) { $this->{$class}(); } else { if (method_exists($this, $current)) { $this->{$current}(); } else { throw new Exception("不存在指定的控制器方法"); } } } else { throw new Exception("不存在'{$method}'方法"); } } catch (Exception $e) { Debug::output($e); } }
/** * 执行controller->action * * @return void */ public static function exec() { // Controller name 安全过滤 if (!preg_match('/^[A-Za-z](\\w)*$/', CONTROLLER_NAME)) { $module = false; } else { $module = Import::controller(GROUP_NAME, CONTROLLER_NAME); } // 执行空控制器 try { if (!$module) { $module = Import::controller(GROUP_NAME, 'Empty'); if (!$module) { throw new Exception("Controller不存在,\"" . CONTROLLER_NAME . "\""); } } } catch (Exception $error) { Debug::output($error); } // 获取控制器操作名 $action = ACTION_NAME; // 定义模板名称 Config::set('TEMPLATE_NAME', THEME_PATH . CONTROLLER_NAME . '/' . $action . '.html'); try { // Action name 安全过滤 if (!preg_match('/^[A-Za-z](\\w)*$/', $action)) { throw new ReflectionException(); } // 对当前控制器的方法执行操作映射 $method = new ReflectionMethod($module, $action); // public方法 if ($method->isPublic()) { // 映射执行 $class = new ReflectionClass($module); // 前置操作 if ($class->hasMethod('_before_' . $action)) { $before = $class->getMethod('_before_' . $action); // public并执行 if ($before->isPublic()) { $before->invoke($module); } } // URL参数绑定检测 if (Config::get('URL_PARAMS_BIND') && $method->getNumberOfParameters() > 0) { switch ($_SERVER['REQUEST_METHOD']) { case 'POST': $vars = $_POST; break; case 'PUT': parse_str(file_get_contents('php://input'), $vars); break; default: $vars = $_GET; } $params = $method->getParameters(); foreach ($params as $param) { $name = $param->getName(); if (isset($vars[$name])) { $args[] = $vars[$name]; } elseif ($param->isDefaultValueAvailable()) { $args[] = $param->getDefaultValue(); } else { Debug::throw_exception(L('_PARAM_ERROR_') . ':' . $name); } } $method->invokeArgs($module, $args); } else { $method->invoke($module); } // 后置操作 if ($class->hasMethod('_after_' . $action)) { $after = $class->getMethod('_after_' . $action); // public并执行 if ($after->isPublic()) { $after->invoke($module); } } } else { throw new ReflectionException(); } } catch (ReflectionException $e) { // 方法调用发生异常后 引导到__call方法处理 $method = new ReflectionMethod($module, '__call'); $method->invokeArgs($module, array($action, '')); } }