一个显示效果非常不错的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中拷贝构造函数、赋值运算符重载
Jul 25 PHP
使用PHP编写的SVN类
Jul 18 PHP
PHP json_decode函数详细解析
Feb 17 PHP
php读取excel文件示例分享(更新修改excel)
Feb 27 PHP
thinkphp在模型中自动完成session赋值示例代码
Sep 09 PHP
php中Array2xml类实现数组转化成XML实例
Dec 08 PHP
php支持中文字符串分割的函数
May 28 PHP
php字符串操作常见问题小结
Oct 11 PHP
php支付宝APP支付功能
Jul 29 PHP
Yii2框架实现利用mpdf创建pdf文件功能示例
Feb 08 PHP
php使用curl模拟多线程实现批处理功能示例
Jul 25 PHP
laravel框架模板之公共模板、继承、包含实现方法分析
Aug 30 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
基于mysql的论坛(7)
2006/10/09 PHP
PHP+FFMPEG实现将视频自动转码成H264标准Mp4文件
2014/09/24 PHP
PHP中foreach()用法汇总
2015/07/02 PHP
学习php设计模式 php实现门面模式(Facade)
2015/12/07 PHP
php实现的SSO单点登录系统接入功能示例分析
2016/10/12 PHP
用JavaScript事件串连执行多个处理过程的方法
2007/03/09 Javascript
JavaScript获取GridView中用户点击控件的行号,列号
2009/04/14 Javascript
JavaScript类和继承 constructor属性
2010/03/04 Javascript
Jquey拖拽控件Draggable使用方法(asp.net环境)
2010/09/28 Javascript
Js日期选择器并自动加入到输入框中示例代码
2013/08/02 Javascript
使用Node.js实现一个简单的FastCGI服务器实例
2014/06/09 Javascript
javascript为按钮注册回车事件(设置默认按钮)的方法
2015/05/09 Javascript
JS模仿手机端九宫格登录功能实现代码
2016/04/28 Javascript
artDialog+plupload实现多文件上传
2016/07/19 Javascript
js轮盘抽奖实例分析
2020/04/17 Javascript
jquery实现下拉框多选方法介绍
2017/01/03 Javascript
关于bootstrap日期转化,bootstrap-editable的简单使用,bootstrap-fileinput的使用详解
2017/05/12 Javascript
js 将canvas生成图片保存,或直接保存一张图片的实现方法
2018/01/02 Javascript
JS实现统计字符串中字符出现个数及最大个数功能示例
2018/06/04 Javascript
mpvue小程序循环动画开启暂停的实现方法
2019/05/15 Javascript
webpack.DefinePlugin与cross-env区别详解
2020/02/23 Javascript
解决antd的Form组件setFieldsValue的警告问题
2020/10/29 Javascript
Python中的CURL PycURL使用例子
2014/06/01 Python
跟老齐学Python之做一个小游戏
2014/09/28 Python
深入理解Python中的*重复运算符
2017/10/28 Python
python 中字典嵌套列表的方法
2018/07/03 Python
Python SQLAlchemy入门教程(基本用法)
2019/11/11 Python
python实现可下载音乐的音乐播放器
2020/02/25 Python
关于HTML5的安全问题开发人员需要牢记的
2012/06/21 HTML / CSS
HTML5 通过Vedio标签实现视频循环播放的示例代码
2020/08/05 HTML / CSS
Melissa鞋英国官方网站:Nonnon
2019/05/01 全球购物
社会学专业求职信
2014/07/17 职场文书
庆六一宣传标语
2014/10/08 职场文书
2014年学校教学工作总结
2014/12/06 职场文书
2014年绿化工作总结
2014/12/09 职场文书
基于redis+lua进行限流的方法
2022/07/23 Redis