包 | system.base |
---|---|
继承 | class CErrorHandler » CApplicationComponent » CComponent |
实现 | IApplicationComponent |
源自 | 1.0 |
版本 | $Id: CErrorHandler.php 3540 2012-01-16 10:17:01Z mDOMba $ |
源码 |
CErrorHandler是用来处理未捕获的PHP错误和异常。
它根据应用程序的运行模式, 显示相应的错误。 它选择优先的语言显示错误。
CErrorHandler使用两种视图:
开发视图是当应用程序为调试模式时显示的视图 (即YII_DEBUG定义为true)。 这种视图显示了详细的错误信息和源代码。 生产视图是当应用程序为产品模式时显示给最终用户的视图。 出于安全原因,它们只显示了错误信息, 没有其它机密信息。
CErrorHandler按以下顺序查找视图模板:
maxSourceLines属性可以指定在开发视图中 显示的最大源代码行数。
CErrorHandler 是一个核心应用组件,它能随时通过 CApplication::getErrorHandler()访问。
它根据应用程序的运行模式, 显示相应的错误。 它选择优先的语言显示错误。
CErrorHandler使用两种视图:
- 开发视图,名为
exception.php
; - 生产视图,名为
error<StatusCode>.php
;
开发视图是当应用程序为调试模式时显示的视图 (即YII_DEBUG定义为true)。 这种视图显示了详细的错误信息和源代码。 生产视图是当应用程序为产品模式时显示给最终用户的视图。 出于安全原因,它们只显示了错误信息, 没有其它机密信息。
CErrorHandler按以下顺序查找视图模板:
-
themes/ThemeName/views/system
: when a theme is active. protected/views/system
framework/views
maxSourceLines属性可以指定在开发视图中 显示的最大源代码行数。
CErrorHandler 是一个核心应用组件,它能随时通过 CApplication::getErrorHandler()访问。
公共属性
属性 | 类型 | 描述 | 定义在 |
---|---|---|---|
adminInfo | string | 应用程序管理员信息(可以是一个名字或电子邮件链接)。它会显示在最终用户的错误面页,默认为‘the webmaster’。 | CErrorHandler |
behaviors | array | 这个应用组件附加的行为。 这此行为将在应用组件调用init时附加在应用组件上。 请参照CModel::behaviors如何指定此属性值。 | CApplicationComponent |
discardOutput | boolean | 是否抛弃错误显示之前的输出内容,默认为true。 | CErrorHandler |
error | array | 返回当前正在处理的错误的详细信息。 | CErrorHandler |
errorAction | string | 被用来显示外部错误信息的控制其动作的路由(例如‘site/error’)。 在动作中,它通过Yii::app()->errorHandler->error得到相关的错误信息。 这个属性默认为null,意味着CErrorHandler将处理错误显示。 | CErrorHandler |
isInitialized | boolean | 检查应用组件是否已经初始化。 | CApplicationComponent |
maxSourceLines | integer | 显示最大源代码行数,默认25。 | CErrorHandler |
maxTraceSourceLines | integer | 显示最大跟踪源代码行数,默认10。 | CErrorHandler |
受保护属性
属性 | 类型 | 描述 | 定义在 |
---|---|---|---|
versionInfo | string | 返回服务版本信息。 | CErrorHandler |
公共方法
方法 | 描述 | 定义在 |
---|---|---|
__call() | 如果类中没有调的方法名,则调用这个方法。 | CComponent |
__get() | 返回一个属性值、一个事件处理程序列表或一个行为名称。 | CComponent |
__isset() | 检查一个属性是否为null。 | CComponent |
__set() | 设置一个组件的属性值。 | CComponent |
__unset() | 设置一个组件的属性为null。 | CComponent |
asa() | 返回这个名字的行为对象。 | CComponent |
attachBehavior() | 附加一个行为到组件。 | CComponent |
attachBehaviors() | 附加一个行为列表到组件。 | CComponent |
attachEventHandler() | 为事件附加一个事件处理程序。 | CComponent |
canGetProperty() | 确定属性是否可读。 | CComponent |
canSetProperty() | 确定属性是否可写。 | CComponent |
detachBehavior() | 从组件中分离一个行为。 | CComponent |
detachBehaviors() | 从组件中分离所有行为。 | CComponent |
detachEventHandler() | 分离一个存在的事件处理程序。 | CComponent |
disableBehavior() | 禁用一个附加行为。 | CComponent |
disableBehaviors() | 禁用组件附加的所有行为。 | CComponent |
enableBehavior() | 启用一个附加行为。 | CComponent |
enableBehaviors() | 启用组件附加的所有行为。 | CComponent |
evaLuateExpression() | 计算一个PHP表达式,或根据组件上下文执行回调。 | CComponent |
getError() | 返回当前正在处理的错误的详细信息。 | CErrorHandler |
getEventHandlers() | 返回一个事件的附加处理程序列表。 | CComponent |
getIsInitialized() | 检查应用组件是否已经初始化。 | CApplicationComponent |
handle() | 处理异常/错误事件。 | CErrorHandler |
hasEvent() | 确定一个事件是否定义。 | CComponent |
hasEventHandler() | 检查事件是否有附加的处理程序。 | CComponent |
hasProperty() | 确定属性是否被定义。 | CComponent |
init() | 初始化应用组件。 | CApplicationComponent |
raiseEvent() | 发起一个事件。 | CComponent |
受保护方法
方法 | 描述 | 定义在 |
---|---|---|
argumentsToString() | 转换参数数组为字符串表现。 | CErrorHandler |
getExactTrace() | 返回问题发生的详细跟踪信息。 | CErrorHandler |
getVersionInfo() | 返回服务版本信息。 | CErrorHandler |
getViewFile() | 确定应该使用的视图文件。 | CErrorHandler |
getViewFileInternal() | 查看指定目录下的视图。 | CErrorHandler |
handleError() | 处理PHP错误。 | CErrorHandler |
handleException() | 处理异常。 | CErrorHandler |
isAjaxRequest() | 当前请求是否是一个AJAX(XMLHttpRequest)请求。 | CErrorHandler |
isCoreCode() | 返回是否从应用程序代码中调用堆栈。 | CErrorHandler |
render() | 渲染视图 | CErrorHandler |
renderSourceCode() | 渲染围绕错误行源代码。 | CErrorHandler |
属性详细
adminInfo
属性
public string $adminInfo;
应用程序管理员信息(可以是一个名字或电子邮件链接)。它会显示在最终用户的错误面页,默认为‘the webmaster’。
discardOutput
属性
public boolean $discardOutput;
是否抛弃错误显示之前的输出内容,默认为true。
error
属性
只读
public array getError()
返回当前正在处理的错误的详细信息。 该错误返回的数组包含如下信息:
- code - HTTP状态码(例如403‘500)
- type - 错误类型(例如‘CHttpException’,‘PHP Error’)
- message - 错误信息
- file - 发生错误的PHP脚本文件名
- line - 发生错误的代码行号
- trace - 错误的调用堆栈
- source - 发生错误的上下文源码
errorAction
属性
public string $errorAction;
被用来显示外部错误信息的控制其动作的路由(例如‘site/error’)。 在动作中,它通过Yii::app()->errorHandler->error得到相关的错误信息。 这个属性默认为null,意味着CErrorHandler将处理错误显示。
maxSourceLines
属性
public integer $maxSourceLines;
显示最大源代码行数,默认25。
maxTraceSourceLines
属性
(可用自 v1.1.6)
public integer $maxTraceSourceLines;
显示最大跟踪源代码行数,默认10。
versionInfo
属性
只读
protected string getVersionInfo()
返回服务版本信息。 如果应用是生产模式,将返回空字符串。
方法详细
argumentsToString()
方法
protected string argumentsToString(array $args)
| ||
$args | array | 要转换的参数数组 |
{return} | string | 参数数组的字符串表现 |
protected function argumentsToString($args)
{
$count=0;
$isAssoc=$args!==array_values($args);
foreach($args as $key => $value)
{
$count++;
if($count>=5)
{
if($count>5)
unset($args[$key]);
else
$args[$key]='...';
continue;
}
if(is_object($value))
$args[$key] = get_class($value);
else if(is_bool($value))
$args[$key] = $value ? 'true' : 'false';
else if(is_string($value))
{
if(strlen($value)>64)
$args[$key] = '"'.substr($value,0,64).'..."';
else
$args[$key] = '"'.$value.'"';
}
else if(is_array($value))
$args[$key] = 'array('.$this->argumentsToString($value).')';
else if($value===null)
$args[$key] = 'null';
else if(is_resource($value))
$args[$key] = 'resource';
if(is_string($key))
{
$args[$key] = '"'.$key.'" => '.$args[$key];
}
else if($isAssoc)
{
$args[$key] = $key.' => '.$args[$key];
}
}
$out = implode(", ", $args);
return $out;
}
转换参数数组为字符串表现。
getError()
方法
public array getError()
| ||
{return} | array | 错误详细信息,如果没有错误返回null。 |
public function getError()
{
return $this->_error;
}
返回当前正在处理的错误的详细信息。 该错误返回的数组包含如下信息:
- code - HTTP状态码(例如403‘500)
- type - 错误类型(例如‘CHttpException’,‘PHP Error’)
- message - 错误信息
- file - 发生错误的PHP脚本文件名
- line - 发生错误的代码行号
- trace - 错误的调用堆栈
- source - 发生错误的上下文源码
getExactTrace()
方法
protected array getExactTrace(Exception $exception)
| ||
$exception | Exception | 未捕获到的异常 |
{return} | array | 问题出现的详细跟踪信息 |
protected function getExactTrace($exception)
{
$traces=$exception->getTrace();
foreach($traces as $trace)
{
// property access exception
if(isset($trace['function']) && ($trace['function']==='__get' || $trace['function']==='__set'))
return $trace;
}
return null;
}
返回问题发生的详细跟踪信息。
getVersionInfo()
方法
protected string getVersionInfo()
| ||
{return} | string | 服务版本信息。生产模式将返回空字符串。 |
protected function getVersionInfo()
{
if(YII_DEBUG)
{
$version='<a href="http://www.yiiframework.com/">Yii Framework</a>/'.Yii::getVersion();
if(isset($_SERVER['SERVER_SOFTWARE']))
$version=$_SERVER['SERVER_SOFTWARE'].' '.$version;
}
else
$version='';
return $version;
}
返回服务版本信息。 如果应用是生产模式,将返回空字符串。
getViewFile()
方法
protected string getViewFile(string $view, integer $code)
| ||
$view | string | 视图名(无论是‘exception’还是‘error’) |
$code | integer | HTTP状态码 |
{return} | string | 视图文件路径 |
protected function getViewFile($view,$code)
{
$viewPaths=array(
Yii::app()->getTheme()===null ? null : Yii::app()->getTheme()->getSystemViewPath(),
Yii::app() instanceof CWebApplication ? Yii::app()->getSystemViewPath() : null,
YII_PATH.DIRECTORY_SEPARATOR.'views',
);
foreach($viewPaths as $i=>$viewPath)
{
if($viewPath!==null)
{
$viewFile=$this->getViewFileInternal($viewPath,$view,$code,$i===2?'en_us':null);
if(is_file($viewFile))
return $viewFile;
}
}
}
确定应该使用的视图文件。
getViewFileInternal()
方法
protected string getViewFileInternal(string $viewPath, string $view, integer $code, string $srcLanguage=NULL)
| ||
$viewPath | string | 包含视图的目录 |
$view | string | 视图名(无论是‘exception’还是‘error’) |
$code | integer | HTTP状态码 |
$srcLanguage | string | 视图文件的语言 |
{return} | string | 视图文件的路径 |
protected function getViewFileInternal($viewPath,$view,$code,$srcLanguage=null)
{
$app=Yii::app();
if($view==='error')
{
$viewFile=$app->findLocalizedFile($viewPath.DIRECTORY_SEPARATOR."error{$code}.php",$srcLanguage);
if(!is_file($viewFile))
$viewFile=$app->findLocalizedFile($viewPath.DIRECTORY_SEPARATOR.'error.php',$srcLanguage);
}
else
$viewFile=$viewPath.DIRECTORY_SEPARATOR."exception.php";
return $viewFile;
}
查看指定目录下的视图。
handle()
方法
public void handle(CEvent $event)
| ||
$event | CEvent | 包含异常/错误信息的事件 |
public function handle($event)
{
// set event as handled to prevent it from being handled by other event handlers
$event->handled=true;
if($this->discardOutput)
{
// the following manual level counting is to deal with zlib.output_compression set to On
for($level=ob_get_level();$level>0;--$level)
{
@ob_end_clean();
}
}
if($event instanceof CExceptionEvent)
$this->handleException($event->exception);
else // CErrorEvent
$this->handleError($event);
}
处理异常/错误事件。 当捕获到异常或PHP错误时 调用此方法。
handleError()
方法
protected void handleError(CErrorEvent $event)
| ||
$event | CErrorEvent | PHP错误事件 |
protected function handleError($event)
{
$trace=debug_backtrace();
// skip the first 3 stacks as they do not tell the error position
if(count($trace)>3)
$trace=array_slice($trace,3);
$traceString='';
foreach($trace as $i=>$t)
{
if(!isset($t['file']))
$trace[$i]['file']='unknown';
if(!isset($t['line']))
$trace[$i]['line']=0;
if(!isset($t['function']))
$trace[$i]['function']='unknown';
$traceString.="#$i {$trace[$i]['file']}({$trace[$i]['line']}): ";
if(isset($t['object']) && is_object($t['object']))
$traceString.=get_class($t['object']).'->';
$traceString.="{$trace[$i]['function']}()\n";
unset($trace[$i]['object']);
}
$app=Yii::app();
if($app instanceof CWebApplication)
{
switch($event->code)
{
case E_WARNING:
$type = 'PHP warning';
break;
case E_NOTICE:
$type = 'PHP notice';
break;
case E_USER_ERROR:
$type = 'User error';
break;
case E_USER_WARNING:
$type = 'User warning';
break;
case E_USER_NOTICE:
$type = 'User notice';
break;
case E_RECOVERABLE_ERROR:
$type = 'Recoverable error';
break;
default:
$type = 'PHP error';
}
$this->_error=$data=array(
'code'=>500,
'type'=>$type,
'message'=>$event->message,
'file'=>$event->file,
'line'=>$event->line,
'trace'=>$traceString,
'traces'=>$trace,
);
if(!headers_sent())
header("HTTP/1.0 500 PHP Error");
if($this->isAjaxRequest())
$app->displayError($event->code,$event->message,$event->file,$event->line);
else if(YII_DEBUG)
$this->render('exception',$data);
else
$this->render('error',$data);
}
else
$app->displayError($event->code,$event->message,$event->file,$event->line);
}
处理PHP错误。
handleException()
方法
protected void handleException(Exception $exception)
| ||
$exception | Exception | 捕获的异常 |
protected function handleException($exception)
{
$app=Yii::app();
if($app instanceof CWebApplication)
{
if(($trace=$this->getExactTrace($exception))===null)
{
$fileName=$exception->getFile();
$errorLine=$exception->getLine();
}
else
{
$fileName=$trace['file'];
$errorLine=$trace['line'];
}
$trace = $exception->getTrace();
foreach($trace as $i=>$t)
{
if(!isset($t['file']))
$trace[$i]['file']='unknown';
if(!isset($t['line']))
$trace[$i]['line']=0;
if(!isset($t['function']))
$trace[$i]['function']='unknown';
unset($trace[$i]['object']);
}
$this->_error=$data=array(
'code'=>($exception instanceof CHttpException)?$exception->statusCode:500,
'type'=>get_class($exception),
'errorCode'=>$exception->getCode(),
'message'=>$exception->getMessage(),
'file'=>$fileName,
'line'=>$errorLine,
'trace'=>$exception->getTraceAsString(),
'traces'=>$trace,
);
if(!headers_sent())
header("HTTP/1.0 {$data['code']} ".get_class($exception));
if($exception instanceof CHttpException || !YII_DEBUG)
$this->render('error',$data);
else
{
if($this->isAjaxRequest())
$app->displayException($exception);
else
$this->render('exception',$data);
}
}
else
$app->displayException($exception);
}
处理异常。
isAjaxRequest()
方法
protected boolean isAjaxRequest()
| ||
{return} | boolean | 当前请求是否是AJAX请求。 |
protected function isAjaxRequest()
{
return isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH']==='XMLHttpRequest';
}
当前请求是否是一个AJAX(XMLHttpRequest)请求。
isCoreCode()
方法
protected boolean isCoreCode(array $trace)
| ||
$trace | array | 跟踪数据 |
{return} | boolean | 是否从应用程序代码中调用堆栈。 |
protected function isCoreCode($trace)
{
if(isset($trace['file']))
{
$systemPath=realpath(dirname(__FILE__).'/..');
return $trace['file']==='unknown' || strpos(realpath($trace['file']),$systemPath.DIRECTORY_SEPARATOR)===0;
}
return false;
}
返回是否从应用程序代码中调用堆栈。
render()
方法
protected void render(string $view, array $data)
| ||
$view | string | 视图名(不带扩展名的文件名)。 参见getViewFile如何定位视图文件。 |
$data | array | 传递给视图的数据 |
protected function render($view,$data)
{
if($view==='error' && $this->errorAction!==null)
Yii::app()->runController($this->errorAction);
else
{
// additional information to be passed to view
$data['version']=$this->getVersionInfo();
$data['time']=time();
$data['admin']=$this->adminInfo;
include($this->getViewFile($view,$data['code']));
}
}
渲染视图
renderSourceCode()
方法
protected string renderSourceCode(string $file, integer $errorLine, integer $maxLines)
| ||
$file | string | 源文件路径 |
$errorLine | integer | 错误行号 |
$maxLines | integer | 显示最大行数 |
{return} | string | 渲染结果 |
protected function renderSourceCode($file,$errorLine,$maxLines)
{
$errorLine--; // adjust line number to 0-based from 1-based
if($errorLine<0 || ($lines=@file($file))===false || ($lineCount=count($lines))<=$errorLine)
return '';
$halfLines=(int)($maxLines/2);
$beginLine=$errorLine-$halfLines>0 ? $errorLine-$halfLines:0;
$endLine=$errorLine+$halfLines<$lineCount?$errorLine+$halfLines:$lineCount-1;
$lineNumberWidth=strlen($endLine+1);
$output='';
for($i=$beginLine;$i<=$endLine;++$i)
{
$isErrorLine = $i===$errorLine;
$code=sprintf("<span class=\"ln".($isErrorLine?' error-ln':'')."\">%0{$lineNumberWidth}d</span> %s",$i+1,CHtml::encode(str_replace("\t",' ',$lines[$i])));
if(!$isErrorLine)
$output.=$code;
else
$output.='<span class="error">'.$code.'</span>';
}
return '<div class="code"><pre>'.$output.'</pre></div>';
}
渲染围绕错误行源代码。