在PHP 7之前,**Exception**未实现[Throwable](https://www.php.net/manual/en/class.throwable.php)接口。
```
Exception {
/* 属性 */
protected string $message ;//异常消息内容
protected int $code ;//异常代码
protected string $file ;//抛出异常的文件名
protected int $line ;//抛出异常在该文件中的行号
/* 方法 */
public __construct ([ string $message = "" [, int $code = 0 [, Throwable $previous = NULL ]]] )
final public getMessage ( void ) : string//获取异常消息内容
final public getPrevious ( void ) : Throwable//返回异常链中的前一个异常
final public getCode ( void ) : int//获取异常代码
final public getFile ( void ) : string// 创建异常时的程序文件名称
final public getLine ( void ) : int//获取创建的异常所在文件中的行号
final public getTrace ( void ) : array//获取异常追踪信息
final public getTraceAsString ( void ) : string// 获取字符串类型的异常追踪信息
public __toString ( void ) : string//将异常对象转换为字符串
final private __clone ( void ) : void//异常克隆
}
```
php7+
```
Exception implements Throwable{
/* 属性 */
protected string $message ;//异常消息内容
protected int $code ;//异常代码
protected string $file ;//抛出异常的文件名
protected int $line ;//抛出异常在该文件中的行号
/* 方法 */
public __construct ([ string $message = "" [, int $code = 0 [, Throwable $previous = NULL ]]] )
final public getMessage ( void ) : string//获取异常消息内容
final public getPrevious ( void ) : Throwable//返回异常链中的前一个异常
final public getCode ( void ) : int//获取异常代码
final public getFile ( void ) : string// 创建异常时的程序文件名称
final public getLine ( void ) : int//获取创建的异常所在文件中的行号
final public getTrace ( void ) : array//获取异常追踪信息
final public getTraceAsString ( void ) : string// 获取字符串类型的异常追踪信息
public __toString ( void ) : string//将异常对象转换为字符串
final private __clone ( void ) : void//异常克隆
}
```
所以在php7+中,如果既想捕获异常有需要捕获错误$e是Error和Exception的基类Throwable
~~~
try{
...
}catch ( Throwable $e){
echo $e->getCode().'<br/>';
echo $e->getMessage().'<br/>';
echo $e->getLine().'<br/>';
echo $e->getFile().'<br/>';
}
~~~
除了Exception之外还有Error类,[这个网站](https://3v4l.org/sDMsv)列出了各个版本的错误类
*****
## 设置顶层异常处理器 (Top Level Exception Handler)
set_exception_handler() 函数可设置处理所有未捕获异常的用户定义函数。(即没有用 try/catch 块来捕获的异常或者try/catch没有捕获到的)
~~~
function myException($exception)
{
echo "<b>Exception:</b> " , $exception->getMessage();
}
set_exception_handler('myException');
//使用:
throw new Exception('Uncaught Exception occurred');
~~~
## 设置顶层错误处理器set_error_handler
设置用户自定义的错误处理函数
E_ERROR、E_PARSE、E_CORE_ERROR、E_CORE_WARNING、 E_COMPILE_ERROR、E_COMPILE_WARNING是不会被这个句柄处理的,也就是会用最原始的方式显示出来。不过出现这些错误都是编 译或PHP内核出错,在通常情况下不会发生。(也就是说如果在脚本执行前发生错误,由于在那时自定义程序还没有注册,因此就不会用到这个自定义错误处理程序)
```
//自定义的错误处理方法
function _error_handler($errno, $errstr ,$errfile, $errline)
{
echo "错误编号errno: $errno<br>";
echo "错误信息errstr: $errstr<br>";
echo "出错文件errfile: $errfile<br>";
echo "出错行号errline: $errline<br><br>";
}
set_error_handler('_error_handler', E_ALL | E_STRICT); // 注册错误处理方法来处理所有错误
echo $foo['bar']; // 由于数组未定义,会产生一个notice级别的错误
//人为触发错误
trigger_error('人为触发一个错误', E_USER_ERROR);
trigger_error('人为触发一个错误', E_ERROR);
```
这两个错误都会被自定义的函数捕获
```
错误编号errno: 8
错误信息errstr: Undefined variable: foo
出错文件errfile: D:\\phpstudy\_pro\\WWW\\www.test.com\\error.php
出错行号errline: 13
错误编号errno: 256
错误信息errstr: 人为触发一个错误
出错文件errfile: D:\\phpstudy\_pro\\WWW\\www.test.com\\error.php
出错行号errline: 16
错误编号errno: 256
错误信息errstr: 人为触发一个错误
出错文件errfile: D:\\phpstudy\_pro\\WWW\\www.test.com\\error.php
出错行号errline: 17
```
7之前一大弊端,某些错误是处理不了的
如果错误发生在脚本执行之前(比如编译、文件上传时),将不会 调用自定义的错误处理程序因为它尚未在那时注册。
```
//自定义的错误处理方法
function _error_handler($errno, $errstr ,$errfile, $errline)
{
echo "错误编号errno: $errno<br>";
echo "错误信息errstr: $errstr<br>";
echo "出错文件errfile: $errfile<br>";
echo "出错行号errline: $errline<br><br>";
}
set_error_handler('_error_handler', E_ALL | E_STRICT); // 注册错误处理方法来处理所有错误
foobar(3, 5); //调用未定义的方法将会产生一个Error级别的错误
```
结果未被自定义的函数捕获
```
Fatal error: Call to undefined function foobar() in D:\phpstudy_pro\WWW\www.test.com\error.php on line 19
```
使用set_error_handler()后,error_reporting ()将会失效。也就是所有的错误(除上述的错误)都会交给自定义的函数处理。
例子:三种用法
```
// 三种方法如下:
1:
set_error_handler('NonClassFunction'); // 直接转到一个普通的函数 NonClassFunction
2:
set_error_handler(array('CallbackClass', 'StaticFunction')); // 转到 CallbackClass 类下的静方法 StaticFunction
3:
$o =new CallbackClass();
set_error_handler(array($o, 'CallbackFunction')); // 转到类的构造函数,
//可以这样操作
class CallbackClass {
function CallbackClass() {
set_error_handler(array(&$this, 'CallbackFunction')); // the & is important
}
function CallbackFunction() {
// refers to $this
}
}
new CallbackClass()-> CallbackClass();
```
例子2:通过触发错误并以用户自定义的程序来进行内部异常的处理
```
`// error handler function
function myErrorHandler($errno, $errstr, $errfile, $errline)
{
if (!(error_reporting() & $errno)) {
// This error code is not included in error_reporting, so let it fall
//除非回调函数返回了FALSE,error_reporting()设置将不会起到作用
//返回false时,转到标准PHP错误处理程序
return false;
}
switch ($errno) {
case E_USER_ERROR:
echo "<b>My ERROR</b> [$errno] $errstr<br />\n";
echo " Fatal error on line $errline in file $errfile";
echo ", PHP " . PHP_VERSION . " (" . PHP_OS . ")<br />\n";
echo "Aborting...<br />\n";
exit(1);
break;
case E_USER_WARNING:
echo "<b>My WARNING</b> [$errno] $errstr<br />\n";
break;
case E_USER_NOTICE:
echo "<b>My NOTICE</b> [$errno] $errstr<br />\n";
break;
default:
echo "Unknown error type: [$errno] $errstr<br />\n";
break;
}
/* Don't execute PHP internal error handler */
return true;
}
// 测试错误处理的函数
function scale_by_log($vect, $scale)
{
if (!is_numeric($scale) || $scale <= 0) {
trigger_error("log(x) for x <= 0 is undefined, you used: scale = $scale", E_USER_ERROR);
}
if (!is_array($vect)) {
trigger_error("Incorrect input vector, array of values expected", E_USER_WARNING);
return null;
}
$temp = array();
foreach($vect as $pos => $value) {
if (!is_numeric($value)) {
trigger_error("Value at position $pos is not a number, using 0 (zero)", E_USER_NOTICE);
$value = 0;
}
$temp[$pos] = log($scale) * $value;
}
return $temp;
}
// set to the user defined error handler
$old_error_handler = set_error_handler("myErrorHandler");
// trigger some errors, first define a mixed array with a non-numeric item
echo "vector a\n";
$a = array(2, 3, "foo", 5.5, 43.3, 21.11);
print_r($a);
// now generate second array
echo "----\nvector b - a notice (b = log(PI) * a)\n";
/* Value at position $pos is not a number, using 0 (zero) */
$b = scale_by_log($a, M_PI);
print_r($b);
// this is trouble, we pass a string instead of an array
echo "----\nvector c - a warning\n";
/* Incorrect input vector, array of values expected */
$c = scale_by_log("not array", 2.3);
var_dump($c); // NULL
// this is a critical error, log of zero or negative number is undefined
echo "----\nvector d - fatal error\n";
/* log(x) for x <= 0 is undefined, you used: scale = $scale" */
$d = scale_by_log($a, -2.5);
var_dump($d); // Never reached
?>`
以上例程的输出类似于:
vector a
Array
(
[0] => 2
[1] => 3
[2] => foo
[3] => 5.5
[4] => 43.3
[5] => 21.11
)
----
vector b - a notice (b = log(PI) * a)
<b>My NOTICE</b> [1024] Value at position 2 is not a number, using 0 (zero)<br />
Array
(
[0] => 2.2894597716988
[1] => 3.4341896575482
[2] => 0
[3] => 6.2960143721717
[4] => 49.566804057279
[5] => 24.165247890281
)
----
vector c - a warning
<b>My WARNING</b> [512] Incorrect input vector, array of values expected<br />
NULL
----
vector d - fatal error
<b>My ERROR</b> [256] log(x) for x <= 0 is undefined, you used: scale = -2.5<br />
Fatal error on line 35 in file trigger_error.php, PHP 5.2.1 (FreeBSD)<br />
Aborting...<br />
```
`set_error_handler`用来捕捉`trigger_error`
`set_exception_handler`用来捕捉`throw new Exception` 这是设定一个全局的异常处理函数,在异常没有被 try catch 处理时系统会将异常交于此函数处理
异常抛出但不使用 try catch 处理,系统会产生一个致命错误导致脚本退出执行,set\_exception\_handler 只是将没有被 try catch 处理的异常进行捕捉,自定义一些友好的信息输出,并不能阻止致命错误的产生,脚本依然会退出执行。
设置默认的异常处理程序,用于没有用 try/catch 块来捕获的异常。 在`exception_handler`调用后异常会中止
set\_error\_handler 则会将用户在脚本中触发的错误拦截下来进行处理,而不再提交给系统,但它不会自动识别错误级别,我们需要手动判断是否为 ERROR 级的进行退出,还是 WARNING 或 NOTICE 级的进行提示后继续执行。
## **thinkphp5**
另外thinkphp继承值Exception
```
namespace think;
class Exception extends \Exception
{
/**
* @var array 保存异常页面显示的额外 Debug 数据
*/
protected $data = [];
/**
* 设置异常额外的 Debug 数据
* 数据将会显示为下面的格式
*
* Exception Data
* --------------------------------------------------
* Label 1
* key1 value1
* key2 value2
* Label 2
* key1 value1
* key2 value2
*
* @access protected
* @param string $label 数据分类,用于异常页面显示
* @param array $data 需要显示的数据,必须为关联数组
* @return void
*/
final protected function setData($label, array $data)
{
$this->data[$label] = $data;
}
/**
* 获取异常额外 Debug 数据
* 主要用于输出到异常页面便于调试
* @access public
* @return array
*/
final public function getData()
{
return $this->data;
}
}
```
**thinkphp5是怎么实现的呢**
在base.php文件中,用一句代码\think\Error::register();实现错误和异常处理机制的注册。
// 注册错误和异常处理机制
```
\think\Error::register();
```
打开library/think/Error.php,register函数如下:
```
/**
* 注册异常处理
* @access public
* @return void
*/
public static function register()
{
error_reporting(E_ALL); // 设置错误级别
set_error_handler([__CLASS__, 'appError']); // 设置错误处理方式__CLASS__:获取当前的类名,
set_exception_handler([__CLASS__, 'appException']); // 设置异常处理
register_shutdown_function([__CLASS__, 'appShutdown']); // 注册关闭函数
}
```
>[danger]注:set_error_handler("myError") 、set_exception_handler("myException")、register_shutdown_function("myShutdown")不仅可以接受函数,还可以接受类的方法(公开的静态方法及公开的非静态方法),但需要以数组形式传递,数组的第一个值为“类名”,第二个值为“方法名”。
**1、error_reporting()**
error_reporting() 函数设置当前脚本的错误报告级别,规定报告哪种错误。E_ALL为显示所有的错误和警告信息,类似的级别还有E_ERROR—运行时致命的错误、E_WARNING—运行时非致命的错误、E_NOTICE-—运行时的通知。
**2、set_error_handler()**
set_error_handler指定appError来处理系统错误,捕获错误后把错误以异常的形式抛出。当程序出现错误的时候自动调用appError函数,该函数实例化了一个PHP自带的错误异常类ErrorException,如果符合异常处理的,就将错误信息以异常的形式抛出来,否则将错误信息写入日志中。
**3、set_exception_handler()**
set_exception_handler指定appException来处理用户抛出的异常,如果不是异常,就实例化ThrowableError类,将异常包装起来并抛出。
**4、register_shutdown_function()**
register_shutdown_function指定appShutdown处理超时异常。
注册一个会在php中止时执行的函数 注册一个`callback`,它会在脚本执行完成或者`exit()`后被调用。
到此,框架的错误和异常处理机制就注册完了。
在注册错误和异常处理机制中,都是使用getExceptionHandler方法来获取异常处理的实例。
```
/**
* 获取异常处理的实例
* @access public
* @return Handle
*/
public static function getExceptionHandler()
{
static $handle;
if (!$handle) {
// 异常处理 handle
$class = Config::get('exception_handle');
if ($class && is_string($class) && class_exists($class) &&
is_subclass_of($class, "\\think\\exception\\Handle")
) {
$handle = new $class;
} else {
$handle = new Handle;
if ($class instanceof \Closure) {
$handle->setRender($class);
}
}
}
return $handle;
}
```
可以看到 `$class = Config::get('exception_handle');` 这句代码,也就是可以通过修改配置参数来指定新的异常处理对象。
如果想使用自定义的错误和异常处理机制,首先在application/config.php文件中找到exception_handle的配置:
```
// 异常处理handle类 留空则系统默认使用 \think\exception\Handle
'exception_handle' => '',
```
将其改为自己定义的 完整Exception类(包含命名空间):
```
'exception_handle' => 'app\lib\exception\ExceptionHandler',
```
再按照配置新建文件,为application/lib/exception/ExceptionHandler.php,重写这个Handle方法即可:
```
class ExceptionHandler extends Handle
{
public function render(Exception $e)
{
return json('这里是自定义的错误');
//return parent::render($e); // TODO: Change the autogenerated stub
}
}
```
这样就可以使用自定义的错误和异常处理机制了。
- 目录结构与基础
- 修改数据后页面无变化
- 防跨目录设置
- input
- 系统目录
- 自动生成的文件以及目录
- 类自动加载
- url生成
- 数据增删改查
- 增加数据
- 数据更新
- 数据删除
- 数据查询
- 架构
- 生命周期
- 入口文件
- URL访问规则
- 配置
- 默认惯例配置配置
- 初始应用配置
- 路由
- 域名路由
- URL生成
- 数据库操作
- 方法列表
- 连接数据库
- 分布式数据库
- 查询构造器
- 查询数据
- 添加数据
- 更新数据
- 删除数据
- 查询语法
- 聚合查询(统计)
- 时间查询
- 高级查询
- 视图查询
- 子查询
- 辅助查询之链式操作
- where
- table
- alias
- field
- order
- limit
- page
- group
- having
- join
- union
- distinct
- lock
- cache
- comment
- fetchSql
- force
- bind
- partition
- strict
- failException
- sequence(pgsql专用)
- 查询事件
- 事务操作
- 监听SQL
- 存储过程
- 数据集
- 控制器
- 跳转和重定向
- 空控制器和空操作
- 分层控制器
- Rest控制器
- 资源控制器
- 自动定位控制器
- tp3的增删改查
- 方法注入
- 模型
- 属性方法一览
- 类方法详解
- Model
- 调用model不存在的属性
- 调用model中不存在的方法
- 调用model中不存在的静态方法
- hasOne
- belongsTo
- hasMany {Relation}
- belongsToMany
- hasManyThrough
- morphMany
- morphOne
- morphTo
- ::hasWhere {Query}
- ::has
- relationCount
- data 【model】
- setInc {integer|true}
- setDec {integer|true}
- save {integer | false}
- saveAll {array}
- delete {integer}
- ::get 查询单条数据 {Model}
- ::all 查询多条数据{Model [ ]}
- ::create 新增单条数据 {Model}
- ::update 更新单条数据 {Model}
- ::destroy {integer}
- ::scope {Query}
- getAttr {mixed}
- xxx
- append
- appendRelationAttr
- hidden
- visible
- except
- readonly
- auto
- together
- allowField
- isUpdate
- validate
- toCollection
- toJson
- toArray
- 定义
- 新增
- 更新
- 查询
- 删除
- 聚合
- 获取器
- 修改器
- 时间戳
- 只读字段
- 软删除
- 类型转换
- 数据完成
- 查询范围
- 模型分层
- 数组访问和转换
- JSON序列化
- 事件
- 关联
- 一对一关联
- 主表一对一关联
- 从表一对一关联(相对关联)
- 一对多关联
- 主表定义一对多关联
- 从表定义一对多关联
- 远程一对多
- 多对多关联
- 多态关联
- 动态属性
- 关联预载入with()
- 关联统计
- N+1查询
- 聚合模型
- Model方法集合
- 表单验证
- 验证器
- 验证规则
- 错误信息
- 验证场景
- 控制器验证
- 模型验证
- 内置规则
- 静态调用
- 表单令牌
- Token身份令牌
- 视图
- 模版
- 变量输出
- 函数输出
- Request请求参数
- 模板注释及原样输出
- 三元运算
- 内置标签
- 模板继承
- 模板布局
- 日志
- 日志初始化
- 日志驱动
- 日志写入
- 独立日志
- 日志清空
- 写入授权
- 自定义日志
- 错误和调试
- 异常
- php系统异常及thinkphp5异常机制
- 异常处理
- 抛出异常
- 异常封装
- resful
- 404页面
- 调试模式
- Trace调试
- SQL调试
- 变量调试
- 性能调试
- 远程调试
- 安全
- 输入安全
- 数据库安全
- 上传安全
- 其它安全建议
- xss过滤
- 扩展
- 函数
- 类库
- 行为
- 驱动
- Composer包
- Time
- 数据库迁移工具
- Workerman
- MongoDb
- htmlpurifier XSS过滤
- 新浪SAE
- oauth2.0
- 命令行及生成文件
- 系统现成命令
- 创建类库文件
- 生成类库映射文件
- 生成路由缓存
- 清除缓存文件
- 生成配置缓存文件
- 生成数据表字段缓存
- 自定义命令行
- 开始
- 调用命令
- 杂项
- 助手函数
- URL重写
- 缓存
- 缓存总结
- Session
- Cookie
- 多语言
- 分页
- 上传
- 验证码
- 图像处理
- 文件处理
- 单元测试
- 自定义表单令牌