首先在 Think\Think::start() 静态方法中,使用 register_shutdown_function 函数注册致命错误处理方法。
register_shutdown_function('Think\Think::fatalError');
在 Think\Think::fatalError 方法中,首先记录错误日志,这个暂且不说。接着使用 error_get_last 函数获取错误信息,不过TP只匹配E_ERROR、E_PARSE、E_CORE_ERROR、E_COMPILE_ERROR、E_USER_ERROR 这五种错误类型,然后使用 ob_end_clean 函数清空并关闭输出缓冲区(这样做的目的是,不显示PHP自身的错误信息提示,而是采用自定义的错误提示),最后调用自定义错误提示方法。
// 致命错误捕获
static public function fatalError() {
Log::save(); // 记录日志
if ($e = error_get_last()) {
switch($e['type']){
case E_ERROR:
case E_PARSE:
case E_CORE_ERROR:
case E_COMPILE_ERROR:
case E_USER_ERROR:
ob_end_clean();
self::halt($e);
break;
}
}
}
self::halt($e) 方法细节:
// 错误输出
static public function halt($error)
{
$e = array();
if (APP_DEBUG || IS_CLI) {
//调试模式下输出错误信息
if (!is_array($error)) {
// 如果错误信息格式不是数组,使用回溯跟踪函数debug_backtrace,可以定位到错误文件及错误行号。
$trace = debug_backtrace();
$e['message'] = $error;
$e['file'] = $trace[0]['file'];
$e['line'] = $trace[0]['line'];
// 开启并打印输出缓冲区的内容
ob_start();
debug_print_backtrace();
$e['trace'] = ob_get_clean();
} else {
$e = $error;
}
if(IS_CLI){
// 如果开启了命令行模式,直接输出错误信息。
exit(iconv('UTF-8','gbk',$e['message']).PHP_EOL.'FILE: '.$e['file'].'('.$e['line'].')'.PHP_EOL.$e['trace']);
}
} else {
//否则定向到错误页面
$error_page = C('ERROR_PAGE');
if (!empty($error_page)) {
// 获取错误文件的URL路径,重定向至错误的文件,一般不会执行这段代码。
redirect($error_page);
} else {
// 在线上部署时,出于安全考虑,不应显示过多的敏感信息,
// 比如:错误文件及路径,错误行号,错误原因。
// C('SHOW_ERROR_MSG')为false时,显示tp预定义好的错误提示:C('ERROR_MESSAGE') 页面错误!请稍后再试~。
$message = is_array($error) ? $error['message'] : $error;
$e['message'] = C('SHOW_ERROR_MSG')? $message : C('ERROR_MESSAGE');
}
}
// 最后一步就是加载错误模板,想用户展示错误信息
// 路径:项目根\ThinkPHP\Tpl\think_exception.tpl
// 至于模板是什么样子,自己可以随意写。
// 包含异常页面模板
$exceptionFile = C('TMPL_EXCEPTION_FILE',null,THINK_PATH.'Tpl/think_exception.tpl');
include $exceptionFile;
exit;
}
版权声明:本文为dreamsqifan原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。