在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
- 多語言
- 分頁
- 上傳
- 驗證碼
- 圖像處理
- 文件處理
- 單元測試
- 自定義表單令牌