一个显示效果非常不错的PHP错误、异常处理类


Posted in PHP onMarch 21, 2014

一、效果图:


二、实现代码
<?php
// 自定义异常函数
set_exception_handler('handle_exception');
// 自定义错误函数
set_error_handler('handle_error');
/**
 * 异常处理
 *
 * @param mixed $exception 异常对象
 * @author blog.snsgou.com
 */
function handle_exception($exception) {
 Error::exceptionError($exception);
}
/**
 * 错误处理
 *
 * @param string $errNo 错误代码
 * @param string $errStr 错误信息
 * @param string $errFile 出错文件
 * @param string $errLine 出错行
 * @author blog.snsgou.com
 */
function handle_error($errNo, $errStr, $errFile, $errLine) {
 if ($errNo) {
  Error::systemError($errStr, false, true, false);
 }
}
/**
 * 系统错误处理
 *
 * @author blog.snsgou.com
 */
class Error {
 public static function systemError($message, $show = true, $save = true, $halt = true) {
  list($showTrace, $logTrace) = self::debugBacktrace();
  if ($save) {
   $messageSave = '<b>' . $message . '</b><br /><b>PHP:</b>' . $logTrace;
   self::writeErrorLog($messageSave);
  }
  if ($show) {
   self::showError('system', "<li>$message</li>", $showTrace, 0);
  }
  if ($halt) {
   exit();
  } else {
   return $message;
  }
 }
 /**
  * 代码执行过程回溯信息
  *
  * @static
  * @access public
  */
 public static function debugBacktrace() {
  $skipFunc[] = 'Error->debugBacktrace';
  $show = $log = '';
  $debugBacktrace = debug_backtrace();
  ksort($debugBacktrace);
  foreach ($debugBacktrace as $k => $error) {
   if (!isset($error['file'])) {
    // 利用反射API来获取方法/函数所在的文件和行数
    try {
     if (isset($error['class'])) {
      $reflection = new ReflectionMethod($error['class'], $error['function']);
     } else {
      $reflection = new ReflectionFunction($error['function']);
     }
     $error['file'] = $reflection->getFileName();
     $error['line'] = $reflection->getStartLine();
    } catch (Exception $e) {
     continue;
    }
   }
   $file = str_replace(SITE_PATH, '', $error['file']);
   $func = isset($error['class']) ? $error['class'] : '';
   $func .= isset($error['type']) ? $error['type'] : '';
   $func .= isset($error['function']) ? $error['function'] : '';
   if (in_array($func, $skipFunc)) {
    break;
   }
   $error['line'] = sprintf('%04d', $error['line']);
   $show .= '<li>[Line: ' . $error['line'] . ']' . $file . '(' . $func . ')</li>';
   $log .= !empty($log) ? ' -> ' : '';
   $log .= $file . ':' . $error['line'];
  }
  return array($show, $log);
 }
 /**
  * 异常处理
  *
  * @static
  * @access public
  * @param mixed $exception
  */
 public static function exceptionError($exception) {
  if ($exception instanceof DbException) {
   $type = 'db';
  } else {
   $type = 'system';
  }
  if ($type == 'db') {
   $errorMsg = '(' . $exception->getCode() . ') ';
   $errorMsg .= self::sqlClear($exception->getMessage(), $exception->getDbConfig());
   if ($exception->getSql()) {
    $errorMsg .= '<div class="sql">';
    $errorMsg .= self::sqlClear($exception->getSql(), $exception->getDbConfig());
    $errorMsg .= '</div>';
   }
  } else {
   $errorMsg = $exception->getMessage();
  }
  $trace = $exception->getTrace();
  krsort($trace);
  $trace[] = array('file' => $exception->getFile(), 'line' => $exception->getLine(), 'function' => 'break');
  $phpMsg = array();
  foreach ($trace as $error) {
   if (!empty($error['function'])) {
    $fun = '';
    if (!empty($error['class'])) {
     $fun .= $error['class'] . $error['type'];
    }
    $fun .= $error['function'] . '(';
    if (!empty($error['args'])) {
     $mark = '';
     foreach ($error['args'] as $arg) {
      $fun .= $mark;
      if (is_array($arg)) {
       $fun .= 'Array';
      } elseif (is_bool($arg)) {
       $fun .= $arg ? 'true' : 'false';
      } elseif (is_int($arg)) {
       $fun .= (defined('SITE_DEBUG') && SITE_DEBUG) ? $arg : '%d';
      } elseif (is_float($arg)) {
       $fun .= (defined('SITE_DEBUG') && SITE_DEBUG) ? $arg : '%f';
      } else {
       $fun .= (defined('SITE_DEBUG') && SITE_DEBUG) ? '\'' . htmlspecialchars(substr(self::clear($arg), 0, 10)) . (strlen($arg) > 10 ? ' ...' : '') . '\'' : '%s';
      }
      $mark = ', ';
     }
    }
    $fun .= ')';
    $error['function'] = $fun;
   }
   if (!isset($error['line'])) {
    continue;
   }
   $phpMsg[] = array('file' => str_replace(array(SITE_PATH, '\\'), array('', '/'), $error['file']), 'line' => $error['line'], 'function' => $error['function']);
  }
  self::showError($type, $errorMsg, $phpMsg);
  exit();
 }
 /**
  * 记录错误日志
  *
  * @static
  * @access public
  * @param string $message
  */
 public static function writeErrorLog($message) {
  return false; // 暂时不写入
  $message = self::clear($message);
  $time = time();
  $file = LOG_PATH . '/' . date('Y.m.d') . '_errorlog.php';
  $hash = md5($message);
  $userId = 0;
  $ip = get_client_ip();
  $user = '<b>User:</b> userId=' . intval($userId) . '; IP=' . $ip . '; RIP:' . $_SERVER['REMOTE_ADDR'];
  $uri = 'Request: ' . htmlspecialchars(self::clear($_SERVER['REQUEST_URI']));
  $message = "<?php exit;?>\t{$time}\t$message\t$hash\t$user $uri\n";
  // 判断该$message是否在时间间隔$maxtime内已记录过,有,则不用再记录了
  if (is_file($file)) {
   $fp = @fopen($file, 'rb');
   $lastlen = 50000;  // 读取最后的 $lastlen 长度字节内容
   $maxtime = 60 * 10;  // 时间间隔:10分钟
   $offset = filesize($file) - $lastlen;
   if ($offset > 0) {
    fseek($fp, $offset);
   }
   if ($data = fread($fp, $lastlen)) {
    $array = explode("\n", $data);
    if (is_array($array))
     foreach ($array as $key => $val) {
      $row = explode("\t", $val);
      if ($row[0] != '<?php exit;?>') {
       continue;
      }
      if ($row[3] == $hash && ($row[1] > $time - $maxtime)) {
       return;
      }
     }
   }
  }
  error_log($message, 3, $file);
 }
 /**
  * 清除文本部分字符
  *
  * @param string $message
  */
 public static function clear($message) {
  return str_replace(array("\t", "\r", "\n"), " ", $message);
 }
 /**
  * sql语句字符清理
  *
  * @static
  * @access public
  * @param string $message
  * @param string $dbConfig
  */
 public static function sqlClear($message, $dbConfig) {
  $message = self::clear($message);
  if (!(defined('SITE_DEBUG') && SITE_DEBUG)) {
   $message = str_replace($dbConfig['database'], '***', $message);
   //$message = str_replace($dbConfig['prefix'], '***', $message);
   $message = str_replace(C('DB_PREFIX'), '***', $message);
  }
  $message = htmlspecialchars($message);
  return $message;
 }
 /**
  * 显示错误
  *
  * @static
  * @access public
  * @param string $type 错误类型 db,system
  * @param string $errorMsg
  * @param string $phpMsg
  */
 public static function showError($type, $errorMsg, $phpMsg = '') {
  global $_G;
  $errorMsg = str_replace(SITE_PATH, '', $errorMsg);
  ob_end_clean();
  $host = $_SERVER['HTTP_HOST'];
  $title = $type == 'db' ? 'Database' : 'System';
  echo <<<EOT
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
 <title>$host - $title Error</title>
 <meta http-equiv="Content-Type" content="text/html; charset={$_G['config']['output']['charset']}" />
 <meta name="ROBOTS" content="NOINDEX,NOFOLLOW,NOARCHIVE" />
 <style type="text/css">
 <!--
 body { background-color: white; color: black; font: 9pt/11pt verdana, arial, sans-serif;}
 #container {margin: 10px;}
 #message {width: 1024px; color: black;}
 .red {color: red;}
 a:link {font: 9pt/11pt verdana, arial, sans-serif; color: red;}
 a:visited {font: 9pt/11pt verdana, arial, sans-serif; color: #4e4e4e;}
 h1 {color: #FF0000; font: 18pt "Verdana"; margin-bottom: 0.5em;}
 .bg1 {background-color: #FFFFCC;}
 .bg2 {background-color: #EEEEEE;}
 .table {background: #AAAAAA; font: 11pt Menlo,Consolas,"Lucida Console"}
 .info {
  background: none repeat scroll 0 0 #F3F3F3;
  border: 0px solid #aaaaaa;
  border-radius: 10px 10px 10px 10px;
  color: #000000;
  font-size: 11pt;
  line-height: 160%;
  margin-bottom: 1em;
  padding: 1em;
 }
 .help {
  background: #F3F3F3;
  border-radius: 10px 10px 10px 10px;
  font: 12px verdana, arial, sans-serif;
  text-align: center;
  line-height: 160%;
  padding: 1em;
 }
 .sql {
  background: none repeat scroll 0 0 #FFFFCC;
  border: 1px solid #aaaaaa;
  color: #000000;
  font: arial, sans-serif;
  font-size: 9pt;
  line-height: 160%;
  margin-top: 1em;
  padding: 4px;
 }
 -->
 </style>
</head>
<body>
<div id="container">
<h1>$title Error</h1>
<div class='info'>$errorMsg</div>
EOT;
  if (!empty($phpMsg)) {
   echo '<div class="info">';
   echo '<p><strong>PHP Debug</strong></p>';
   echo '<table cellpadding="5" cellspacing="1" width="100%" class="table"><tbody>';
   if (is_array($phpMsg)) {
    echo '<tr class="bg2"><td>No.</td><td>File</td><td>Line</td><td>Code</td></tr>';
    foreach ($phpMsg as $k => $msg) {
     $k++;
     echo '<tr class="bg1">';
     echo '<td>' . $k . '</td>';
     echo '<td>' . $msg['file'] . '</td>';
     echo '<td>' . $msg['line'] . '</td>';
     echo '<td>' . $msg['function'] . '</td>';
     echo '</tr>';
    }
   } else {
    echo '<tr><td><ul>' . $phpMsg . '</ul></td></tr>';
   }
   echo '</tbody></table></div>';
  }
  echo <<<EOT
</div>
</body>
</html>
EOT;
  exit();
 }
}
/**
 * DB异常类
 *
 * @author blog.snsgou.com
 */
class DbException extends Exception {
 protected $sql;
 protected $dbConfig; // 当前数据库配置信息
 public function __construct($message, $code = 0, $sql = '', $dbConfig = array()) {
  $this->sql = $sql;
  $this->dbConfig = $dbConfig;
  parent::__construct($message, $code);
 }
 public function getSql() {
  return $this->sql;
 }
 public function getDbConfig() {
  return $this->dbConfig;
 }
}
PHP 相关文章推荐
php-accelerator网站加速PHP缓冲的方法
Jul 30 PHP
php 表单数据的获取代码
Mar 10 PHP
PHP IN_ARRAY 函数使用注意事项
Jul 24 PHP
php curl 登录163邮箱并抓取邮箱好友列表的代码(经测试)
Apr 07 PHP
Window下PHP三种运行方式图文详解
Jun 11 PHP
利用PHP函数计算中英文字符串长度的方法
Nov 11 PHP
PHP中shuffle数组值随便排序函数用法
Nov 21 PHP
php安装swoole扩展的方法
Mar 19 PHP
PHP浮点比较大小的方法
Feb 14 PHP
对PHP依赖注入的理解实例分析
Oct 09 PHP
php在windows环境下获得cpu内存实时使用率(推荐)
Feb 08 PHP
Thinkphp5.0框架视图view的循环标签用法示例
Oct 12 PHP
一漂亮的PHP图片验证码实例
Mar 21 #PHP
PHP中nowdoc和heredoc使用需要注意的一点
Mar 21 #PHP
php获取表单中多个同名input元素的值
Mar 20 #PHP
php中的静态变量的基本用法
Mar 20 #PHP
php遍历文件夹下的所有文件和子文件夹示例
Mar 20 #PHP
php数据库备份还原类分享
Mar 20 #PHP
PHP实现微信公众平台音乐点播
Mar 20 #PHP
You might like
php配置php-fpm启动参数及配置详解
2013/11/04 PHP
php限制ip地址范围的方法
2015/03/31 PHP
详解PHP错误日志的获取方法
2015/07/20 PHP
PHP使用逆波兰式计算工资的方法
2015/07/29 PHP
PHP SESSION机制的理解与实例
2019/03/22 PHP
js表数据排序 sort table data
2009/02/18 Javascript
Js,alert出现乱码问题的解决方法
2013/06/19 Javascript
jQuery3.0中的buildFragment私有函数详解
2016/08/16 Javascript
Javascript 使用ajax与C#获取文件大小实例详解
2017/01/13 Javascript
Vue获取DOM元素样式和样式更改示例
2017/03/07 Javascript
Vue开发中整合axios的文件整理
2017/04/29 Javascript
微信小程序中实现手指缩放图片的示例代码
2018/03/13 Javascript
vue-cli脚手架build目录下utils.js工具配置文件详解
2018/09/14 Javascript
使用post方法实现json往返传输数据的方法
2019/03/30 Javascript
vue项目中将element-ui table表格写成组件的实现代码
2019/06/12 Javascript
JS扁平化输出数组的2种方法解析
2019/09/17 Javascript
[02:36]DOTA2亚洲邀请赛小组赛精彩集锦:EE凭借法力虚空拿下4杀
2017/03/30 DOTA
[10:24]郎朗助力完美“圣”典,天籁交织奏响序曲
2016/12/18 DOTA
举例讲解Python中的list列表数据结构用法
2016/03/12 Python
Python Flask-web表单使用详解
2017/11/18 Python
对Python中for复合语句的使用示例讲解
2018/11/01 Python
Python实现的旋转数组功能算法示例
2019/02/23 Python
python找出列表中大于某个阈值的数据段示例
2019/11/24 Python
python二元表达式用法
2019/12/04 Python
python生成13位或16位时间戳以及反向解析时间戳的实例
2020/03/03 Python
使用pytorch 筛选出一定范围的值
2020/06/28 Python
Python图像读写方法对比
2020/11/16 Python
python实现企业微信定时发送文本消息的实例代码
2020/11/25 Python
Scrapy+Selenium自动获取cookie爬取网易云音乐个人喜爱歌单
2021/02/01 Python
乌克兰巴士票购买网站:inBus
2021/03/12 全球购物
幼儿园教师教学反思
2014/02/06 职场文书
《赵州桥》教学反思
2014/02/17 职场文书
交通事故调解协议书
2014/04/16 职场文书
搞笑车尾标语
2014/06/23 职场文书
六一文艺汇演主持词
2015/06/30 职场文书
ztree+ajax实现文件树下载功能
2021/05/18 Javascript