一个显示效果非常不错的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 相关文章推荐
MayFish PHP的MVC架构的开发框架
Aug 13 PHP
一个显示某段时间内每个月的方法 返回由这些月份组成的数组
May 16 PHP
PHP中可以自动分割查询字符的Parse_str函数使用示例
Jul 25 PHP
PHP中使用数组指针函数操作数组示例
Nov 19 PHP
PHP中imagick函数的中文解释
Jan 21 PHP
PHP调试函数和日志记录函数分享
Jan 31 PHP
PHP编程中尝试程序并发的几种方式总结
Mar 21 PHP
Zend Framework常用校验器详解
Dec 09 PHP
浅谈PHP的数据库接口和技术
Dec 09 PHP
PHP函数按引用传递参数及函数可选参数用法示例
Jun 04 PHP
Yii2框架实现登陆添加验证码功能示例
Jul 12 PHP
thinkphp5框架扩展redis类方法示例
May 06 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中通过正则表达式下载内容中的远程图片的函数代码
2012/01/10 PHP
php根据日期判断星座的函数分享
2014/02/13 PHP
php获取mysql字段名称和其它信息的例子
2014/04/14 PHP
php中的mongodb select常用操作代码示例
2014/09/06 PHP
php5.3不能连接mssql数据库的解决方法
2014/12/27 PHP
UPUPW 更新 64 位 Apache 系列 PHP 7.0 正式版
2015/12/08 PHP
根据分辨率不同,调用不同的css文件
2006/08/25 Javascript
Javascript定义类(class)的三种方法详解
2015/03/13 Javascript
JS建造者模式基本用法实例分析
2015/06/30 Javascript
js变形金刚文字特效代码分享
2015/08/20 Javascript
基于AngularJS前端云组件最佳实践
2016/10/20 Javascript
js实现一个可以兼容PC端和移动端的div拖动效果实例
2016/12/09 Javascript
vue自定义filters过滤器
2018/04/26 Javascript
layui的select联动实现代码
2019/09/28 Javascript
vue动态禁用控件绑定disable的例子
2019/10/28 Javascript
vuex入门最详细整理
2020/03/04 Javascript
[01:04:48]VGJ.S vs TNC Supermajor 败者组 BO3 第一场 6.6
2018/06/07 DOTA
介绍Python中内置的itertools模块
2015/04/29 Python
举例讲解Python设计模式编程的代理模式与抽象工厂模式
2016/01/16 Python
python面试题小结附答案实例代码
2019/04/11 Python
详解Python sys.argv使用方法
2019/05/10 Python
详解pandas使用drop_duplicates去除DataFrame重复项参数
2019/08/01 Python
Django中使用CORS实现跨域请求过程解析
2019/08/05 Python
Python3简单爬虫抓取网页图片代码实例
2019/08/26 Python
Python算法中的时间复杂度问题
2019/11/19 Python
Python paramiko使用方法代码汇总
2020/11/20 Python
百联网上商城:i百联
2017/01/28 全球购物
精灵市场:Pixie Market
2019/06/18 全球购物
医护人员英文求职信范文
2013/11/26 职场文书
医院合作协议书
2014/08/19 职场文书
2015年信访工作总结
2015/04/07 职场文书
MYSQL 无法识别中文的永久解决方法
2021/06/03 MySQL
Nginx反向代理学习实例教程
2021/10/24 Servers
搞笑Gif:这么白这么长的腿像极了一楼的女朋友
2022/03/21 杂记
Mysql中@和@@符号的详细使用指南
2022/06/05 MySQL
MySQL索引失效十种场景与优化方案
2023/05/08 MySQL