CErrorHandler


system.base
继承 class CErrorHandler » CApplicationComponent » CComponent
实现 IApplicationComponent
源自 1.0
版本 $Id: CErrorHandler.php 3540 2012-01-16 10:17:01Z mdomba $
源码
CErrorHandler是用来处理未捕获的PHP错误和异常。

它根据应用程序的运行模式, 显示相应的错误。 它选择优先的语言显示错误。

CErrorHandler使用两种视图:
  • 开发视图,名为exception.php;
  • 生产视图,名为error<StatusCode>.php;
<StatusCode>表示PHP错误代码(例如error500.php)。 本地化视图的命名是选择在本地语言的子目录下的代码 (例如,zh_cn/error500.php)。

开发视图是当应用程序为调试模式时显示的视图 (即YII_DEBUG定义为true)。 这种视图显示了详细的错误信息和源代码。 生产视图是当应用程序为产品模式时显示给最终用户的视图。 出于安全原因,它们只显示了错误信息, 没有其它机密信息。

CErrorHandler按以下顺序查找视图模板:
  1. themes/ThemeName/views/system: when a theme is active.
  2. protected/views/system
  3. 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<|| ($lines=@file($file))===false || ($lineCount=count($lines))<=$errorLine)
        return 
'';

    
$halfLines=(int)($maxLines/2);
    
$beginLine=$errorLine-$halfLines>$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>';
}

渲染围绕错误行源代码。