thinkphp3.2源码(错误和异常处理)
- 写在前面:tp3.2中每次载入入口文件时都会进行错误和异常的捕获,解读这一部分代码可以对以后的优化很有好处。
- 处理概览:
- 错误捕获与处理:
- 致命错误捕获:
- public static function start()
- {
- // 注册AUTOLOAD方法
- spl_autoload_register('Think\Think::autoload');
- // 设定错误和异常处理
- register_shutdown_function('Think\Think::fatalError');
- set_error_handler('Think\Think::appError');
- set_exception_handler('Think\Think::appException');
- .........................
tp使用了 register_shutdown_function()来注册一个在php中止时执行的函数,通过这个回调函数来捕获了致命异常:
- // 致命错误捕获
- public static function fatalError()
- {
- Log::save();
- if ($e = error_get_last()){
- switch ($e['type']) {
- case E_ERROR: //通常会显示出来,也会中断程序执行
- case E_PARSE: //语法解析错误
- case E_CORE_ERROR: //在PHP启动时发生的致命错误
- case E_COMPILE_ERROR: //编译时发生的致命错误,指出脚本的错误
- case E_USER_ERROR: //用户产生的错误信息
- ob_end_clean();
- self::halt($e);
- break;
- }
- }
- }
在这个方法中,tp使用error_get_last获得当前错误,并且使用ob_end_clean 丢掉缓冲区内容,阻止页面上错误信息的输出。
将会把缓冲区的内容输出,返回到浏览器。而中止回调是作为请求的一部分被执行的,因此可以在它们中进行输出或者读取输出缓冲区,我们此时用ob_end_clean丢掉缓冲区的内容,就阻止了页面的输出显示。(关于缓冲区:传送门)
- /**
- * 错误输出
- * @param mixed $error 错误
- * @return void
- */
- public static function halt($error)
- {
- $e = array();
- if (APP_DEBUG || IS_CLI) {
- //调试模式下输出错误信息
- if (!is_array($error)) {
- $trace = debug_backtrace();
- $e['message'] = $error;
- $e['file'] = $trace[0]['file'];
- $e['line'] = $trace[0]['line'];
- ob_start();
- debug_print_backtrace();
- $e['trace'] = ob_get_clean();
- } else {
- $e = $error;
- }
- if (IS_CLI) {
- exit((IS_WIN ? iconv('UTF-8', 'gbk', $e['message']) : $e['message']) . PHP_EOL . 'FILE: ' . $e['file'] . '(' . $e['line'] . ')' . PHP_EOL . $e['trace']);
- }
- } else {
- //否则定向到错误页面
- $error_page = C('ERROR_PAGE');
- if (!empty($error_page)) {
- redirect($error_page);
- } else {
- $message = is_array($error) ? $error['message'] : $error;
- $e['message'] = C('SHOW_ERROR_MSG') ? $message : C('ERROR_MESSAGE');
- }
- }
- // 包含异常页面模板
- $exceptionFile = C('TMPL_EXCEPTION_FILE', null, THINK_PATH . 'Tpl/think_exception.tpl');
- include $exceptionFile;
- exit;
- }
APP_DEBUG 可以在入口文件中修改,IS_CLI是 通过 php预定义常量 “PHP_SAPI”判断当前php的运行环境,在框架入口文件ThinkPHP.php中是这样配置的:
- /* 错误设置 */
- 'ERROR_MESSAGE' => '页面错误!请稍后再试~', //错误显示信息,非调试模式有效
- 'ERROR_PAGE' => '', // 错误定向页面
- 'SHOW_ERROR_MSG' => false, // 显示错误信息
2.自定义错误处理:
trigger_error ( "用户自定义错误信息提示" , E_USER_ERROR );
运行结果如下:
- /**
- * 自定义错误处理
- * @access public
- * @param int $errno 错误类型
- * @param string $errstr 错误信息
- * @param string $errfile 错误文件
- * @param int $errline 错误行数
- * @return void
- */
- public static function appError($errno, $errstr, $errfile, $errline)
- {
- switch ($errno) {
- case E_ERROR:
- case E_PARSE:
- case E_CORE_ERROR:
- case E_COMPILE_ERROR:
- case E_USER_ERROR:
- ob_end_clean();
- $errorStr = "$errstr " . $errfile . " 第 $errline 行.";
- if (C('LOG_RECORD')) {
- Log::write("[$errno] " . $errorStr, Log::ERR);
- }
- self::halt($errorStr);
- break;
- default:
- $errorStr = "[$errno] $errstr " . $errfile . " 第 $errline 行.";
- self::trace($errorStr, '', 'NOTIC');
- break;
- }
- }
首先我们要知道的是,当前的静态方法是set_error_handler()的回调方法,这个回调方法就包含了错误的error_handler(参数说明见代码)。这个方法和之前的fatalError相比,不同的地方主要有两个(记录日志会在以后的博客中说明):传给halt()的参数变成了一个字符串(之前down处理了是包含错误信息的数组);NOTICE不在是通过halt去显示了,而是调用了另外一个方法,trace();
- if (!is_array($error)) {
- $trace = debug_backtrace();
- $e['message'] = $error;
- $e['file'] = $trace[0]['file'];
- $e['line'] = $trace[0]['line'];
- ob_start();
- debug_print_backtrace();
- $e['trace'] = ob_get_clean();
- } else {
- $e = $error;
- }
如果传递过来的参数不是数组,通过处理后$e就多一个成员['trace'],而我们在错误模板中可以发现,这个成员就是用于显示我们的代码执行流程(追溯)的:
- <?php if(isset($e['trace'])) {?>
- <div class="info">
- <div class="title">
- <h3>TRACE</h3>
- </div>
- <div class="text">
- <p><?php echo nl2br($e['trace']);?></p>
- </div>
- </div>
- <?php }?>
- /**
- * 添加和获取页面Trace记录
- * @param string $value 变量
- * @param string $label 标签
- * @param string $level 日志级别(或者页面Trace的选项卡)
- * @param boolean $record 是否记录日志
- * @return void|array
- */
- public static function trace($value = '[think]', $label = '', $level = 'DEBUG', $record = false)
- {
- static $_trace = array();
- if ('[think]' === $value) {
- // 获取trace信息
- return $_trace;
- } else {
- $info = ($label ? $label . ':' : '') . print_r($value, true);
- $level = strtoupper($level);
- if ((defined('IS_AJAX') && IS_AJAX) || !C('SHOW_PAGE_TRACE') || $record) {
- Log::record($info, $level, $record);
- } else {
- if (!isset($_trace[$level]) || count($_trace[$level]) > C('TRACE_MAX_RECORD')) {
- $_trace[$level] = array();
- }
- $_trace[$level][] = $info;
- }
- }
- }
- 异常处理
throw new \Exception('抛出一个异常')
- public static function appException($e)
- {
- $error = array();
- $error['message'] = $e->getMessage();
- $trace = $e->getTrace();
- if ('E' == $trace[0]['function']) {
- $error['file'] = $trace[0]['file'];
- $error['line'] = $trace[0]['line'];
- } else {
- $error['file'] = $e->getFile();
- $error['line'] = $e->getLine();
- }
- $error['trace'] = $e->getTraceAsString();
- Log::record($error['message'], Log::ERR);
- // 发送404信息
- header('HTTP/1.1 404 Not Found');
- header('Status:404 Not Found');
- self::halt($error);
- }
首先我们要知道,$e就是当前的异常对象,$e可以调用该异常对象是方法,其中$e->getTrace()是追踪包含异常信息的数组,追踪信息中包含触发异常的函数,tp判断触发该异常的函数是不是tp自带的 E()函数,从而组装异常信息发送给halt显示,我们可以看到传递给halt是通过$e->getTraceAsString()获取的字符串,所以halt后面又会用debug_backtrace()追溯异常,最后在页面上生成TRACE信息。
thinkphp3.2源码(错误和异常处理)的更多相关文章
- SpringMVC源码分析-400异常处理流程及解决方法
本文涉及SpringMVC异常处理体系源码分析,SpringMVC异常处理相关类的设计模式,实际工作中异常处理的实践. 问题场景 假设我们的SpringMVC应用中有如下控制器: 代码示例-1 @Re ...
- Spring MVC源码(四) ----- 统一异常处理原理解析
SpringMVC除了对请求URL的路由处理特别方便外,还支持对异常的统一处理机制,可以对业务操作时抛出的异常,unchecked异常以及状态码的异常进行统一处理.SpringMVC既提供简单的配置类 ...
- Spring AMQP 源码分析 05 - 异常处理
### 准备 ## 目标 了解 Spring AMQP Message Listener 如何处理异常 ## 前置知识 <Spring AMQP 源码分析 04 - MessageListene ...
- Android studio应用导入源码错误This attribute must be localized
This attribute must be localized 产生原因: 多语言错误,源码中关于语言的显示不能直接赋值,而是需要通过xml来实现: 例如 <TextView android: ...
- jsoncpp-src-0.5.0.tar.gz 源码错误!!!!
近期在做毕设,使用到了JsonCpp0.5.0版本号的源码! 依照网上的安装配置教程,搭建好环境后就能够使用了! 在这里就不浪费空间去将怎样搭建开发环境了!请大家去google一下就好了! 在解析一个 ...
- Spring 源码学习系列
前言 Spring框架之于 JavaEE 程序员来说,犹如锄头之于农民.Java 程序员每天都要使用Spring框架,Spring框架也确实是个可手的工具. 最初使用Spring的时候,我们需要配置m ...
- 关于Eclipse部署openfire3.8.2源码的体会
因为公司要做人际银行的一个项目需要openfire(服务器)+asmack(客户端),所以需要对消息的推送及消息发送知识的积累.所以需要研究xmpp,以前不是很了解这个技术,现在需要学习.首先就得部署 ...
- Spring 源码分析-1-启动
Spring 源码分析-1-启动 在web项目中使用spring的时候,我们会在web.xml中加入如下配置: <listener> <listener-class>org.s ...
- win10+vs2008编译比特币1.0版源码总结
https://zhuanlan.zhihu.com/p/25074960 https://zhuanlan.zhihu.com/p/25095222 总体上是参考这两个链接,感谢大神的分享,但是中间 ...
随机推荐
- 复盘一篇浅谈KNN的文章
认识-什么是KNN KNN 即 K-nearest neighbors, 是一个hello world级别, 但被广泛使用的机器学习算法, 中文叫K近邻算法, 是一种基本的分类和回归方法. KNN既可 ...
- Flask入门很轻松 (二)
转载请在文章开头附上原文链接地址:https://www.cnblogs.com/Sunzz/p/10959454.html 请求钩子 在客户端和服务器交互的过程中,有些准备工作或扫尾工作需要处理,比 ...
- Linux 磁盘配额(XFS & EXT4)
若是在Linux中搭建了FTP服务器,为了安全性,就要考虑磁盘配额,以防服务器磁盘空间被恶意占满. 磁盘配额概述 1.作用范围:只在指定的分区有效. 2.限制对象:主要针对用户.组进行限制,对组账号限 ...
- 使用Cloudera Manager添加Sentry服务
使用Cloudera Manager添加Sentry服务 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.通过CM添加Sentry服务 1>.点击添加服务进入CM服务安装向 ...
- TCN时间卷积网络——解决LSTM的并发问题
TCN是指时间卷积网络,一种新型的可以用来解决时间序列预测的算法.在这一两年中已有多篇论文提出,但是普遍认为下篇论文是TCN的开端. 论文名称: An Empirical Evaluation of ...
- python使用 pdb 进行调试--- python -m pdb xxx.py 即可 和gdb使用一样
使用 pdb 进行调试 pdb 是 python 自带的一个包,为 python 程序提供了一种交互的源代码调试功能,主要特性包括设置断点.单步调试.进入函数调试.查看当前代码.查看栈片段.动态改变变 ...
- Java调用Kotlin事项及Kotlin反射初步
继续来研究Java调用Kotlin的一些东东. @Throws注解: 我们知道在Kotlin中是不存在checked exception的,而在Java中是存在的,那..如果从Java来调用Kotli ...
- qt事件机制(转)
学习了一段时间的Qt之后,发现Qt的事件机制和其他语言的机制有些不同.Qt除了能够通过信号和槽机制来实现一些Action动作之外,还可以用对象所带的事件,或者用户自定义的事件来实现对象的一些行为处理. ...
- discuz x3.4 开启tags聚合标签及伪静态配置方法
因为SEO的需要,要做tags聚合到一个页面,做到伪静态. 例如: misc.php?mod=tag >>> /tag/ misc.php?mod=tag&id=47 > ...
- C语言 define实现的宏函数汇总
最大值,最小值 #define MAX( x, y ) ( (x) > (y) ? (x) : (y) )#define MIN( x, y ) ( (x) < (y) ? (x) : ( ...