<?php
/**
* Interæ¡æ¶æ ¸å¿æä»¶ä¹é误å¤ç
* æ¬ç»ä»¶ä¾èµæ§ï¼å¯ç¬ç«ä½¿ç¨ã
* æ¬æä»¶çé误å¤çæè·¯åèè¿ä»¥ä¸ç¨åºï¼å¨æ¤ä¸å¹¶è´è°¢ï¼
* - 论åç¨åºMybb{@link http://www.mybbchina.net/}
* - PHPæ¡æ¶Slightphp{@link http://phpchina.com/bbs/thread-150396-1-1.html}
* - PHPæ¡æ¶DooPHP{@link http://doophp.com/blog/article/diagnostic-debug-view-in-doophp}
*
* @author Horse Luke<hide@address.com>
* @copyright Horse Luke, 2009
* @license the Apache License, Version 2.0 (the "License"). {@link http://www.apache.org/licenses/LICENSE-2.0}
* @version $Id: Error.php 167 2011-02-26 09:46:38Z hide@address.com $
* @package Inter_PHP_Framework
*/
class Inter_Error
{
/**
* æ¬ç±»çæ°ç»é
ç½®ã便®ç´¢å¼ï¼ä»ä¸å°ä¸ä¸ºï¼
* debugModeï¼
* æ¯å¦å¼å¯debug模å¼ï¼è¥ä¸ºtrueï¼å°å¨å¨æµè§å¨æ¾ç¤ºè¯¦ç»ä¿¡æ¯ãå¦åå°ä¸æ¾ç¤ºã
* åæ°éæ©ï¼é»è®¤ä¸ºfalseï¼å¯éåæ°trueæè
falseã
* åæ°ç±»åï¼bool
*
* friendlyExceptionPageï¼
* ç³»ç»éå°exceptionçæ¶åæåºçå好ç½é¡µæä»¶å®æ´è·¯å¾
* åæ°éæ©ï¼é»è®¤ä¸ºç©ºãæ¬åæ°åªæå½debugMode为falseæ¶æææ
* 请æå®ä¸ºç½é¡µæä»¶ç宿´è·¯å¾ï¼ä½ä¸éè¦æ¯ç»å¯¹è·¯å¾ï¼ãè¥æä»¶ä¸å卿è
为空ï¼åä¸è¿è¡ä»»ä½æä½ã
* å卿¶åéårequire模å¼ãæä»¥è¯·èªè¡ä¿è¯é²æ¢xssçæ»å»ï¼
* åæ°ç±»åï¼string
*
* logTypeï¼
* 对é误è¿è¡å
æ¬é误追踪å¨å
ç详ç»è®°å½ï¼detailï¼ãè¿æ¯åªéè¦ç®åè®°å½ï¼simpleï¼ãå使ç¨PHP彿°error_logè¿è¡è®°å½ãï¼
* falseå°ä¸è¿è¡ä»»ä½è®°å½ã
* åæ°éæ©ï¼é»è®¤ä¸ºfalseãå¯éæ©'detail'æè
'simple'æè
å¸å°å¼falseã
* ä¸å»ºè®®å¨ç产ç¯å¢è¿è¡è¯¦ç»è®°å½ï¼detailï¼ï¼å¦åå¨é«è®¿é®éçæ
åµä¸ï¼æ¥å¿çæ¡ç®å°é常混乱ï¼
* åæ°ç±»åï¼bool|string
*
* logDirï¼
* è®°å½æ¥å¿çæä»¶å¤¹ï¼ç®å½è·¯å¾ï¼ï¼ç»å°¾ä¸è¦å«æææ ã
* åæ°éæ©ï¼ é»è®¤ä¸ºç©ºãæ¬åæ°åªæå½logTypeä¸ä¸ºfalseæ¶æææï¼
* å¹¶ä¸å½ä¸ºè¥ä¸ºç©ºãæè
䏿¯ç®å½ãæè
ç®å½ä¸åå¨ï¼åæç
§php.iniç设置è¿è¡é误记å½å¤çã
* åæ°ç±»åï¼string
*
* suffixï¼
* è®°å½æ¥å¿çæä»¶åç¼ã
* åæ°ç±»åï¼string
*
* variablesï¼
* æå®è¦æ£æµåè¾åºçåéåã
* åæ°ç±»åï¼array
*
* ignoreERRORï¼
* ä¸è¦è®°å½çé误类åã
* æ¯å¦ï¼ä¸æ³è®°å½E_NOTICEæè
E_USER_NOTICEé误ï¼åå°æ¤è®¾ç½®ä¸ºarray(E_NOTICE, E_USER_NOTICE)
* å¯ä¼ å
¥ç常éï¼è¯·èªè¡æ¥æ¾ï¼PHPæå -> å½±åPHPè¡ä¸ºçæ©å± -> Error Handling -> é¢å®ä¹å¸¸éã{@link http://docs.php.net/manual/zh/errorfunc.constants.php}
* åæ°ç±»åï¼array
*
* @var array
*/
public static $conf = array(
'debugMode' => false,
'friendlyExceptionPage' => '',
'logType' => false,
'logDir' => '',
'suffix' => '-Inter-ErrorLog.log',
'variables' => array("_GET", "_POST", "_SESSION", "_COOKIE"),
'ignoreERROR' => array(),
);
/**
* (ç§æ)ææé误ãå¼å¸¸çä¿¡æ¯å卿°ç»
* @var array
*/
private static $_allError = array();
/**
* (ç§æ)æ¯å¦åå©PHPçregister_shutdown_function注åäºæ¬ç±»éææ¹æ³error_displayåerror_display
*
* @var unknown_type
*/
private static $_registered = false;
/**
* (ç§æ)å½å请æ±èæ¬çURI
* @var string
*/
private static $_request_uri = null;
/**
* (ç§æ)éè¯¯ä»£å·æ å°çéè¯¯ææ¬
* @link http://docs.php.net/manual/zh/errorfunc.constants.php
* @var array
*/
private static $_errorText = array(
'1'=>'E_ERROR',
'2'=>'E_WARNING',
'4'=>'E_PARSE',
'8'=>'E_NOTICE',
'16'=>'E_CORE_ERROR',
'32'=>'E_CORE_WARNING',
'64'=>'E_COMPILE_ERROR',
'128'=>'E_COMPILE_WARNING',
'256'=>'E_USER_ERROR',
'512'=>'E_USER_WARNING',
'1024'=>'E_USER_NOTICE',
'2047'=>'E_ALL',
'2048'=>'E_STRICT',
'4096'=>'E_RECOVERABLE_ERROR',
'8192'=>'E_DEPRECATED',
'16384'=>'E_USER_DEPRECATED',
);
/**
* å¤çPHPæåºçexception
*
* @param Exception $e
*/
public static function exception_handler(Exception $e){
self::init();
$errorInfo = array();
$errorInfo['time'] = time();
$errorInfo['type'] = 'EXCEPTION';
$errorInfo['name'] = get_class($e);
$errorInfo['code'] = $e->getCode();
$errorInfo['message'] = $e->getMessage();
$errorInfo['file'] = $e->getFile();
$errorInfo['line'] = $e->getLine();
$errorInfo['trace'] = self::_format_trace($e->getTrace());
self::$_allError[] = $errorInfo;
//$debugMode为falseæ¶åï¼æ ¹æ®self::$friendlyExceptionPageè¿è¡å好é误æä½
if(false == self::$conf['debugMode']){
if(is_file(self::$conf['friendlyExceptionPage'])){
require(self::$conf['friendlyExceptionPage']);
}
}
}
/**
* å¤çPHPåç°çé误
*
* @param integer $errno é误代å·
* @param string $errstr é误信æ¯
* @param string $errfile é误æå¨æä»¶
* @param string $errline é误æå¨è¡
*/
public static function error_handler($errno, $errstr, $errfile, $errline) {
//对errorç±»åè¿è¡ç´è§åå¤ç~
self::init();
if( empty(self::$conf['ignoreERROR']) || !in_array($errno, self::$conf['ignoreERROR']) ){
$errorInfo = array();
$errorInfo['time'] = time();
$errorInfo['type'] = 'ERROR';
if(!empty(self::$_errorText[$errno])){
$errorInfo['name'] = self::$_errorText[$errno];
}else{
$errorInfo['name'] = '_UNKNOWN_';
}
$errorInfo['code'] = $errno;
$errorInfo['message'] = $errstr;
$errorInfo['file'] = $errfile;
$errorInfo['line'] = $errline;
$trace = debug_backtrace();
unset($trace[0]); //è°ç¨è¯¥ç±»èªèº«çerror_handleræ¹æ³æäº§ççtraceï¼æ
å é¤
$errorInfo['trace'] = self::_format_trace($trace);
self::$_allError[] = $errorInfo;
}
//éè¦æå¨å¯¹FATAL ERRORé误è¿è¡åæ¢
if( in_array($errno, array(1, 4, 16, 64, 256, 4096 )) ){
die();
}
}
/**
* åå§åæ¬ç±»
*/
public static function init(){
if( false == self::$_registered ){
register_shutdown_function(array('Inter_Error', 'write_errorlog'));
register_shutdown_function(array('Inter_Error', 'error_display'));
self::$_request_uri = self::_get_request_uri();
self::$_registered = true;
}
}
/**
* è·årequest_uri
* @return string
*/
protected static function _get_request_uri(){
if(isset($_SERVER['REQUEST_URI'])){
return $_SERVER['REQUEST_URI'];
}
if(isset($_SERVER['PHP_SELF'])){
if(isset($_SERVER['argv'][0])){
return $_SERVER['PHP_SELF']. '?'. $_SERVER['argv'][0];
}elseif(isset($_SERVER['QUERY_STRING'])){
return $_SERVER['PHP_SELF']. '?'. $_SERVER['QUERY_STRING'];
}else{
return $_SERVER['PHP_SELF'];
}
}else{
return '_UNKNOWN_URI_';
}
}
/**
* (ç§æ)对éè¯¯åæº¯è¿½è¸ªä¿¡æ¯è¿è¡æ ¼å¼åè¾åºå¤çã
*
* @param array $trace éè¯¯åæº¯è¿½è¸ªä¿¡æ¯æ°ç»
* @return array $trace éè¯¯åæº¯è¿½è¸ªä¿¡æ¯æ°ç»
*/
private static function _format_trace($trace){
$return = array();
//éæ¡è¿½è¸ªè®°å½å¤ç
foreach ($trace as $stack => $detail){
if(!empty($detail['args'])){
$args_string = self::_args_to_string($detail['args']);
}else{
$args_string = '';
}
//è§è追踪记å½ï¼ææ
¨PHP太è¿èªç±ï¼è¿traceè®°å½ä¹æ¯ä¸å°½ç¸å-_-||ï¼
$return[$stack]['class'] = isset($trace[$stack]['class']) ? $trace[$stack]['class'] : '';
$return[$stack]['type'] = isset($trace[$stack]['type']) ? $trace[$stack]['type'] : '';
//åªæåå¨functionçæ¶åï¼æå¯è½åå¨argsï¼æ
以æ¤åå¹¶ä¹
$return[$stack]['function'] = isset($trace[$stack]['function']) ? $trace[$stack]['function'].'('.$args_string.')' : '';
$return[$stack]['file']=isset($trace[$stack]['file']) ? $trace[$stack]['file'] :'' ;
$return[$stack]['line']=isset($trace[$stack]['line']) ? $trace[$stack]['line'] :'' ;
}
return $return;
}
/**
* (ç§æ)å°åæ°è½¬å为å¯è¯»çå符串
* åæ±åå°$e->getTraceAsString()çææ
*
* @param array $args
* @return string
*/
private static function _args_to_string($args){
$string = '';
$argsAll = array();
foreach ($args as $key => $value){
if(true == is_object($value)){
$argsAll[$key] = 'Object('.get_class($value).')';
}elseif(true == is_numeric($value)){
$argsAll[$key] = $value;
}elseif(true == is_string($value)){
$temp = $value;
if(!extension_loaded('mbstring')){
if(strlen($temp) > 300){
$temp = substr($temp, 0 ,300).'...';
}
}else{
if(mb_strlen($temp) > 300){
$temp = mb_substr($temp, 0 ,300).'...';
}
}
$argsAll[$key] = "'{$temp}'";
$temp = null;
}elseif(true == is_bool($value)){
if(true == $value){
$argsAll[$key] = 'true';
}else{
$argsAll[$key] = 'false';
}
}else{
$argsAll[$key] = gettype($value);
}
}
$string = implode(',', $argsAll);
return $string;
}
/**
* åå
¥é误æ¥å¿
*/
public static function write_errorlog(){
if( (false != (bool)self::$conf['logType']) && !empty(self::$_allError) ){
$logText = '';
foreach (self::$_allError as $key => $errorInfo){
//为é¿å
PHP5.1.0å以ä¸çæ¬å
³äºæ¶åºçSTRICT ERRORï¼å¨è¿è¡å请使ç¨date_default_timezone_set设置ä¹~
$logText .= date("Y-m-d H:i:s", $errorInfo['time']). "\t".
self::$_request_uri."\t".
$errorInfo['type']. "\t".
$errorInfo['name']. "\t".
'Code '. $errorInfo['code']. "\t".
$errorInfo['message']. "\t".
$errorInfo['file']. "\t".
'Line '. $errorInfo['line']. "\n";
if('detail' == self::$conf['logType'] && !empty($errorInfo['trace'])){
$prefix = "TRACE\t#";
foreach ( $errorInfo['trace'] as $stack => $trace ){
$logText .= $prefix. $stack. "\t". $trace['file']. "\t". $trace['line']. "\t". $trace['class']. $trace['type']. $trace['function']. "\n";
}
}
}
if(empty(self::$conf['logDir']) || false == is_dir(self::$conf['logDir'])){
error_log($logText);
}else{
$logFilename= date("Y-m-d",time()). self::$conf['suffix'];
error_log($logText, 3, self::$conf['logDir']. DIRECTORY_SEPARATOR. $logFilename);
}
}
}
/**
* æ¾ç¤ºé误
*/
public static function error_display(){
if(false != self::$conf['debugMode'] && !empty(self::$_allError) ){
$htmlText = '';
foreach (self::$_allError as $key => $errorInfo){
//é误div头
$htmlText .= '<div class="intererrorblock">
<div class="intererrortitle">['.$errorInfo['name'].'][Code '.$errorInfo['code'].'] '.$errorInfo['message'].'</div>
<div class="intererrorsubtitle">Line '.$errorInfo['line'].' On <a href="'.$errorInfo['file'].'">'.$errorInfo['file'].'</a></div>
<div class="intererrorcontent">
';
//traceæ¾ç¤ºåº
if(empty($errorInfo['trace'])){
$htmlText .= 'No Traceable Information.';
}else{
$htmlText .= '<table width="100%" border="1" cellpadding="1" cellspacing="1" rules="rows">
<tr>
<th scope="col">#</th>
<th scope="col">File</th>
<th scope="col">Line</th>
<th scope="col">Class::Method(Args)</th>
</tr>';
foreach ($errorInfo['trace'] as $stack => $trace){
$htmlText .= '<tr>
<td>'.$stack.'</td>
<td><a href="'.$trace['file'].'">'.$trace['file'].'</a></td>
<td>'.$trace['line'].'</td>
<td>'.$trace['class']. $trace['type']. htmlspecialchars($trace['function']) .'</td>
</tr>';
}
$htmlText .= '</table>';
}
//é误divå°¾
$htmlText .= ' </div>
</div>
';
}
//è¾åº
echo <<<END
<style type="text/css">
<!--
.intererrorblock {
font-size: 12pt;
background-color: #FFC;
text-align: left;
vertical-align: middle;
display: inline-block;
border-collapse: collapse;
word-break: break-all;
padding: 3px;
width: 100%;
}
.intererrorblock a:link {
color: #00F;
text-decoration: none;
}
.intererrorblock a:visited {
text-decoration: none;
color: #00F;
}
.intererrorblock a:hover {
text-decoration: underline;
color: #00F;
}
.intererrorblock a:active {
text-decoration: none;
color: #00F;
}
.intererrortitle {
color: #FFF;
background-color: #963;
padding: 3px;
font-weight: bold;
}
.intererrorsubtitle {
padding: 3px;
font-weight: bold;
color: #F00;
}
.intererrorcontent {
font-size: 11pt;
color: #000;
background-color: #FFF;
padding: 3px;
}
.intererrorcontent table{
font-size:14px;
word-break: break-all;
background-color:#D4D0C8;
border-color:#000000;
}
.intererrorblock table a:link {
color: #00F;
text-decoration: none;
}
.intererrorblock table a:visited {
text-decoration: none;
color: #00F;
}
.intererrorblock table a:hover {
text-decoration: underline;
color: #00F;
}
.intererrorblock table a:active {
text-decoration: none;
color: #00F;
}
-->
</style>
{$htmlText}
END;
self::show_variables();
}
}
/**
* æå®åé忣æµåæ¾ç¤º
*/
public static function show_variables(){
$variables_link = '';
$variables_content = '';
foreach( self::$conf['variables'] as $key ){
$variables_link .= '<a href="#variables'.$key.'">$'.$key.'</a> ';
$variables_content .= '<div class="variablessubtitle"><a name="variables'.$key.'" id="variables'.$key.'"></a><strong>$'.$key.'</strong></div>
<div class="variablescontent">';
if(!isset($GLOBALS[$key])){
$variables_content .= '$'. $key .' IS NOT SET.';
}else{
$variables_content .= nl2br(htmlspecialchars(var_export($GLOBALS[$key], true)));
}
$variables_content .= '</div>';
}
//è¾åº
echo <<<END
<style type="text/css">
<!--
.variablesblock {
font-size: 12pt;
background-color: #CCC;
text-align: left;
vertical-align: middle;
display: inline-block;
border-collapse: collapse;
word-break: break-all;
padding: 3px;
width: 100%;
color: #000;
}
.variablesblock a:link {
color: #000;
text-decoration: none;
}
.variablesblock a:visited {
text-decoration: none;
color: #000;
}
.variablesblock a:hover {
text-decoration: underline;
color: #000;
}
.variablesblock a:active {
text-decoration: none;
color: #000;
}
.variablessubtitle {
padding: 3px;
font-weight: bold;
border: 1px solid #FFF;
}
.variablescontent {
font-size: 11pt;
color: #000;
background-color: #FFF;
padding: 3px;
}
-->
</style>
<div class="variablesblock">
<div class="variablessubtitle">Variables: {$variables_link}</div>
{$variables_content}
</div>
END;
}
}