实例讲解如何在PHP的Yii框架中进行错误和异常处理


Posted in PHP onMarch 17, 2016

Yii已经默认已经在CApplication上实现了异常和错误的接管,这是通过php的set_exception_handler,set_error_handler实现的。通过这两个PHP内置函数,可以对程序中未捕获的异常以及错误进行接管处理,从而提高程序的可维护性。这在大型系统是至关重要的,当发生错误时,我们希望能将相关详细信息记录,甚至是即时发送报警,从而缩短故障修复时间,提高整个系统的稳定性。
默认情况下,Yii会将异常处理分配给CApplication::handleException, 将错误处理分配给CApplication::handleError,但是可以通过在入口文件中定义YII_ENABLE_EXCEPTION_HANDLER, YII_ENABLE_ERROR_HANDLER两个常量为false禁止使用Yii的异常和错误接管机制。
以下内容中,将异常和错误统称为错误,如有必要会进行详细区分说明。YII_DEBUG常量(默认为false, 可以在入口文件中设置)对错误信息的显示有很重要的影响,debug模式下,错误的输出是最详细的。而程序一旦投入运行,则应将YII_DEBUG修改为false。
无论是否处于debug模式,Yii程序产生错误时均会将相关错误信息进行记录(错误级别为error, 分类默认为application)。不同之处是debug模式时会直接在web页上显示详细信息。

CApplication:: handleError($code,$message,$file,$line)

上面的方法实现了相关逻。特别注意restore_error_handler,restore_exception_handler两个函数,如果没有这两个函数的调用,那么在后续的错误处理过程中,当再次产生异常或是错误时,又会调用CApplication:: handleError ,从而可能造成死循环,故Yii在此处临时禁止了使用CApplication:: handleError 接管后续的错误和异常(使用php默认的错误处理机制),这就保证了不会因之产生循环调用。
PHP错误的处理当产生错误时,PHP会在日志中记录哪些信息?错误代码(即PHP的E_ERROR E_WARNING  E_STRICT E_DEPRECATED)消息内容(如 Undefined vaiable $input)产生错误的文件路径产生错误的行号额外的跟踪回溯信息(这是通过debug_backtrace实现的)当前URL
除了记录相应日志之外,Yii还会对错误进行后续处理(如中断运行、显示错误页等),默认情况下错误的处理会交给CErrorHandler组件处理(但可以通过给CApplicaton绑定onError事件处理器而实现错误处理的二次接管,此处的设计很灵活!)。
此时将产生一个CErrorEvent(并包含$code,$message,$file,$line几项关键参数),传递给CErrorHandler组件进行处理。具体是交给CErrorHandler::handleError处理之。这个流程主要是将错误相关信息进行整理,并以合适的方式进行显示。
是否为debug模式(YII_DEBUG==true),对错误信息的显示结果有极大影响。调试模式下我们希望能显示详细的错误跟踪信息,而在生产模式下,我们希望给用户显示友好的页面。所以,此处的错误显示有所不同,下面区分说明之。
当处于调试模式时,将直接渲染exception视图展示错误。将按以下路径搜索:

  • protected/views/system/exception.php
  • YII_PATH/views/exception.php

显然,默认情况下并没有在应用程序中定义views/system目录,故会使用系统框架自带的视图文件。最终包含的文件将是Yii框架中的views/exception.php。

从以上分析中可以得知,在调试模式下如果我们要使用自定义异常页面(一般这么做的意义可能不大),则需要配置文件protected/views/system/exception.php, 可使用的变量即$data。
当处于非调试模式下时,会作如下处理:

配置文件中若为errorHandler组件定义了errorAction路由信息,则直接运行之,否则执行第2步流程。
尝试加载error视图,按以下路径搜索(第一个搜索到的文件将被使用)

  • protected/views/system/zh_cn/error500.php
  • protected/views/system/error500.php
  • protected/views/system/zh_cn/error.php
  • protected/views/system/error.php
  • YII_PATH/views/zh_cn/error500.php
  • YII_PATH/views/error500.php
  • YII_PATH/views/zh_cn/error.php
  • Y II_PATH/views/error.php

异常的处理根据前面的分析,异常的处理机制与错误处理机制类似,也会记录日志,级别是error, 分类为"exception.$EXCEPTIONCLASS", 若是CHttpException类异常,分类名称则为exception.CHttpException.$STATUS_CODE。如数据的异常分类称为exception.CDbException。

接下来将错误事件CExceptionEvent交由errorHandler处理,所有错误信息都由CExceptionEvent对象传递而来。处理方法如下:

如果是调试模式,则按以下顺序搜索视图文件,第一个搜索到的文件将被使用

  • protected/views/system/exception.php
  • YII_PATH/views/exception.php

如果是非调试模式,并在配置文件中为errorHandler组件定义了errorAction属性路由,则运行之,否则进入第3步。
按以下顺序尝试加载视图文件,第一个搜索到的文件将被使用
protected/views/system/zh_cn/error500.phpprotected/views/system/error500.phpprotected/views/system/zh_cn/error.phpprotected/views/system/error.phpYII_PATH/views/zh_cn/error500.phpYII_PATH/views/error500.phpYII_PATH/views/zh_cn/error.phpY II_PATH/views/error.php

使用流程图描述,会更清楚一些:搜寻视图文件流程比较重要,因为它关系到我们如何自定义错误页面的细节问题,后续的流程图详细描述其过程。

实例讲解如何在PHP的Yii框架中进行错误和异常处理

从图中可以看出,最容易的方式还是给errorHandler组件设置errorAction属性指定错误发生的路由

实例讲解如何在PHP的Yii框架中进行错误和异常处理

一般而言,我们最关心的是生产模式下错误页面的显示问题,经过以上分析,有两种方法可用:

配置文件中为errorHandler组件定义errorAction路由属性(应该优先使用这个方式,以达到灵活配置目的)
定义以下文件中的任意一个,实现自定义错误页(不推荐)

  • Protected/views/system/zh_cn/error500.php
  • protected/views/system/error500.php
  • protected/views/system/zh_cn/error.php
  • protected/views/system/error.php

第1种方式灵活可控,可以在控制器中指定视图文件,灵活可控。

使用错误处理器示例

yii\web\ErrorHandler 注册成一个名称为errorHandler应用组件, 可以在应用配置中配置它类似如下:

return [
  'components' => [
    'errorHandler' => [
      'maxSourceLines' => 20,
    ],
  ],
];

使用如上代码,异常页面最多显示20条源代码。

如前所述,错误处理器将所有非致命PHP错误转换成可获取异常,也就是说可以使用如下代码处理PHP错误:

use Yii;
use yii\base\ErrorException;

try {
  10/0;
} catch (ErrorException $e) {
  Yii::warning("Division by zero.");
}

// execution continues...

如果你想显示一个错误页面告诉用户请求是无效的或无法处理的,可简单地抛出一个 yii\web\HttpException异常, 如 yii\web\NotFoundHttpException。错误处理器会正确地设置响应的HTTP状态码并使用合适的错误视图页面来显示错误信息。

use yii\web\NotFoundHttpException;

throw new NotFoundHttpException();

自定义错误显示

yii\web\ErrorHandler错误处理器根据常量YII_DEBUG的值来调整错误显示, 当YII_DEBUG 为 true (表示在调试模式),错误处理器会显示异常以及详细的函数调用栈和源代码行数来帮助调试, 当YII_DEBUG 为 false,只有错误信息会被显示以防止应用的敏感信息泄漏。

补充: 如果异常是继承 yii\base\UserException,不管YII_DEBUG为何值,函数调用栈信息都不会显示, 这是因为这种错误会被认为是用户产生的错误,开发人员不需要去修正。
yii\web\ErrorHandler 错误处理器默认使用两个视图显示错误:

  • @yii/views/errorHandler/error.php: 显示不包含函数调用栈信息的错误信息是使用, 当YII_DEBUG 为 false时,所有错误都使用该视图。
  • @yii/views/errorHandler/exception.php: 显示包含函数调用栈信息的错误信息时使用。

可以配置错误处理器的 yii\web\ErrorHandler::errorView 和 yii\web\ErrorHandler::exceptionView 属性 使用自定义的错误显示视图。

使用错误操作

使用指定的错误操作 来自定义错误显示更方便, 为此,首先配置errorHandler组件的 yii\web\ErrorHandler::errorAction 属性,类似如下:

return [
  'components' => [
    'errorHandler' => [
      'errorAction' => 'site/error',
    ],
  ]
];

yii\web\ErrorHandler::errorAction 属性使用路由到一个操作, 上述配置表示不用显示函数调用栈信息的错误会通过执行site/error操作来显示。

可以创建site/error 操作如下所示:

namespace app\controllers;

use Yii;
use yii\web\Controller;

class SiteController extends Controller
{
  public function actions()
  {
    return [
      'error' => [
        'class' => 'yii\web\ErrorAction',
      ],
    ];
  }
}

上述代码定义error 操作使用yii\web\ErrorAction 类,该类渲染名为error视图来显示错误。

除了使用yii\web\ErrorAction, 可定义error 操作使用类似如下的操作方法:

public function actionError()
{
  $exception = Yii::$app->errorHandler->exception;
  if ($exception !== null) {
    return $this->render('error', ['exception' => $exception]);
  }
}

现在应创建一个视图文件为views/site/error.php,在该视图文件中,如果错误操作定义为yii\web\ErrorAction, 可以访问该操作中定义的如下变量:

  • name: 错误名称
  • message: 错误信息
  • exception: 更多详细信息的异常对象,如HTTP 状态码,错误码,错误调用栈等。

补充: 如果你使用 基础应用模板 或 高级应用模板, 错误操作和错误视图已经定义好了。

自定义错误格式

错误处理器根据响应设置的格式来显示错误, 如果yii\web\Response::format 响应格式为html, 会使用错误或异常视图来显示错误信息,如上一小节所述。 对于其他的响应格式,错误处理器会错误信息作为数组赋值给yii\web\Response::data属性,然后转换到对应的格式, 例如,如果响应格式为json,可以看到如下响应信息:

HTTP/1.1 404 Not Found
Date: Sun, 02 Mar 2014 05:31:43 GMT
Server: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y
Transfer-Encoding: chunked
Content-Type: application/json; charset=UTF-8

{
  "name": "Not Found Exception",
  "message": "The requested resource was not found.",
  "code": 0,
  "status": 404
}

可在应用配置中响应response组件的beforeSend事件来自定义错误响应格式。

return [
  // ...
  'components' => [
    'response' => [
      'class' => 'yii\web\Response',
      'on beforeSend' => function ($event) {
        $response = $event->sender;
        if ($response->data !== null) {
          $response->data = [
            'success' => $response->isSuccessful,
            'data' => $response->data,
          ];
          $response->statusCode = 200;
        }
      },
    ],
  ],
];

上述代码会重新格式化错误响应,类似如下:

HTTP/1.1 200 OK
Date: Sun, 02 Mar 2014 05:31:43 GMT
Server: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y
Transfer-Encoding: chunked
Content-Type: application/json; charset=UTF-8

{
  "success": false,
  "data": {
    "name": "Not Found Exception",
    "message": "The requested resource was not found.",
    "code": 0,
    "status": 404
  }
}
PHP 相关文章推荐
PHP 中执行系统外部命令
Oct 09 PHP
PHP实现网上点歌(二)
Oct 09 PHP
功能齐全的PHP发送邮件类代码附详细说明
Jul 10 PHP
MySQL时间字段究竟使用INT还是DateTime的说明
Feb 27 PHP
使用php判断网页是否gzip压缩
Jun 25 PHP
php格式输出文件var_export函数实例
Nov 15 PHP
使用php转义输出HTML到JavaScript
Mar 27 PHP
joomla数据库操作示例代码
Jan 06 PHP
Thinkphp自定义代码生成工具及用法说明(附下载地址)
May 27 PHP
ThinkPHP实现简单登陆功能
Apr 28 PHP
PHP将数据导出Excel表中的实例(投机型)
Jul 31 PHP
php成功操作redis cluster集群的实例教程
Jan 13 PHP
解析PHP的Yii框架中cookie和session功能的相关操作
Mar 17 #PHP
简要剖析PHP的Yii框架的组件化机制的基本知识
Mar 17 #PHP
PHP的Yii框架中YiiBase入口类的扩展写法示例
Mar 17 #PHP
Symfony控制层深入详解
Mar 17 #PHP
详解PHP的Yii框架的运行机制及其路由功能
Mar 17 #PHP
深入解析PHP的Yii框架中的event事件机制
Mar 17 #PHP
全面解读PHP的Yii框架中的日志功能
Mar 17 #PHP
You might like
laravel框架查询数据集转为数组的两种方法
2019/10/10 PHP
jquery设置text的值示例(设置文本框 DIV 表单值)
2014/01/06 Javascript
javascript圆盘抽奖程序实现原理和完整代码例子
2014/06/03 Javascript
js不能获取隐藏的div的宽度只能先显示后获取
2014/09/04 Javascript
js实现a标签超链接提交form表单的方法
2015/06/24 Javascript
Bootstrap实现弹性搜索框
2016/07/11 Javascript
js修改onclick动作的四种方法(推荐)
2016/08/18 Javascript
javascript 利用arguments实现可变长参数
2016/11/21 Javascript
详解Node.js:events事件模块
2016/11/24 Javascript
浅析JavaScript中作用域和作用域链
2016/12/06 Javascript
jQuery实现简单漂亮的Nav导航菜单效果
2017/03/29 jQuery
浅谈vue单一组件下动态修改数据时的全部重渲染
2018/03/01 Javascript
JS中利用FileReader实现上传图片前本地预览功能
2018/03/02 Javascript
深入理解JavaScript的值传递和引用传递
2018/10/24 Javascript
浅谈Node框架接入ELK实践总结
2019/02/22 Javascript
Jquery的autocomplete插件用法及参数讲解
2019/03/12 jQuery
node.js中事件触发器events的使用方法实例分析
2019/11/23 Javascript
vue项目在webpack2实现移动端字体自适配功能
2020/06/02 Javascript
Element-ui upload上传文件限制的解决方法
2021/01/22 Javascript
python获取一组数据里最大值max函数用法实例
2015/05/26 Python
python 列表删除所有指定元素的方法
2018/04/19 Python
Pandas读取MySQL数据到DataFrame的方法
2018/07/25 Python
Python中的 sort 和 sorted的用法与区别
2019/08/10 Python
python 多进程并行编程 ProcessPoolExecutor的实现
2019/10/11 Python
Python3.7在anaconda里面使用IDLE编译器的步骤详解
2020/04/29 Python
Scrapy中如何向Spider传入参数的方法实现
2020/09/28 Python
python利用xlsxwriter模块 操作 Excel
2020/10/14 Python
Python命令行参数定义及需要注意的地方
2020/11/30 Python
基于HTML5代码实现折叠菜单附源码下载
2015/11/27 HTML / CSS
俄罗斯马克西多姆家居用品网上商店:Максидом
2020/02/06 全球购物
一组SQL面试题
2016/02/15 面试题
司机岗位职责
2013/11/15 职场文书
试用期员工工作自我评价
2014/09/10 职场文书
2016新年感言
2015/08/03 职场文书
自考生自我评价
2019/06/21 职场文书
纯CSS如何禁止用户复制网页的内容
2021/11/01 HTML / CSS