通過十幾天的忙碌,張小五手上的項目終於如期上線,雖然很累,但心裏無比的充實與喜悅。喝了杯熱咖啡,小五在椅子上慵懶地躺着,享受着這份靜謐的時光。php
"嗨,小五,這幾天累壞了吧?"
"哈哈,是有點累,不過還好。"數組
"週末好好休息下吧,我先跟你討論個事兒啊。"
"好的,Z哥。"閉包
"我們線上運行的代碼,出於各類各樣的狀況,可能會有好多Fatal Error、Exception。有沒有辦法,在出現Fatal Error、Exception的時候,我們能自動捕獲,並寫到Log文件裏?"
"嗯...這個嘛,出現Fatal Error的時候,腳本就終止了,很差捕獲啊。"框架
"對,是很差捕獲。可是對於出現的Fatal Error、Exception咱們不知道的話,不能提早發現問題,就像身邊有個隱形的刺客同樣,讓人心裏特別虛啊..."
"這樣啊,Z哥,那我這幾天試一下吧!"函數
"好的,小五,這個挺重要的,相信你!"
"哈哈,Z哥你仍是不要抱太大但願,我努力試一下就是了。"測試
對於碼農來講,從Google到Stackoverflow是解決問題的通途,固然張小五也不例外。ui
哈!不搜不知道,一搜嚇一跳,PHP還真有捕獲Error和Exception的函數。spa
//設置一個用戶的函數來處理腳本中出現的錯誤。 set_error_handler($callback) //設置一個用戶的函數來處理腳本中出現的異常。 set_exception_handler($callback)
張小五不自覺的笑了笑:「哈哈,不愧是世界上最好的語言!」code
說幹就幹,看看這兩個函數的威力怎樣,不一會,小五就寫出了測試代碼。隊列
<?php //設置異常捕獲函數 set_exception_handler("my_exception"); function my_exception($exception){ echo 'Exception Catched:'.$exception->getMessage(); } //拋出異常 throw new Exception("I am Exception");
Yes,拋出的一個Exception真的被捕獲了!
"接下來再測下set_error_handler(),你可不能讓我失望啊!"小五心想。
<?php set_error_handler("error_handler"); function error_handler($errno,$errstr,$errfile,$errline){ $str=<<<EOF "errno":$errno "errstr":$errstr "errfile":$errfile "errline":$errline EOF; //獲取到錯誤能夠本身處理,好比記Log、報警等等 echo $str; } echo $test;//$test未定義,會報一個notice級別的錯誤
不錯,Notice級別的錯誤也捕獲到了!
接下來再測一下Fatal Error,若是Fatal Error也能捕獲到,這個需求就實現了!
抑制住激動的心情,小五很快寫完了測試代碼。
<?php set_error_handler("error_handler"); function error_handler($errno,$errstr,$errfile,$errline){ $str=<<<EOF "errno":$errno "errstr":$errstr "errfile":$errfile "errline":$errline EOF; //獲取到錯誤能夠本身處理,好比記Log、報警等等 echo $str; } //調用一個不存在的函數,會出現Fatal Error test();
小五屏住呼吸,等待着奇蹟的出現。"咣噹",手起指落,幾行報錯躍然屏上...
神馬?Fatal Error居然沒捕獲到?怎麼可能?
正在小五陷入沉思的時候,不經意間,小五瞥見了函數的說明:
如下級別的錯誤不能由用戶定義的函數來處理: E_ERROR、 E_PARSE、 E_CORE_ERROR、 E_CORE_WARNING、 E_COMPILE_ERROR、 E_COMPILE_WARNING,和在 調用 set_error_handler() 函數所在文件中產生的大多數 E_STRICT。
也就是:set_error_handler($callback)只能捕獲系統產生的一些Warning、Notice級別的Error。
嗚呼悲催,好不容易找到了解決辦法,沒想到這函數居然仍是個半吊子,不少級別的錯誤捕獲不到...?
王小五從不是輕言放棄的人,他又繼續搜索,尋找着解決辦法...
"嗯?哈哈,SO上還真有人遇到這問題!"
小五專一地看着答案,邊看邊敲了起來:
要實現這個需求,須要用到兩個函數:register_shutdown_function()
和error_get_last()
。
register_shutdown_function($callback)
register_shutdown_function(),就把你要註冊進去的function放進【僞裝是隊列吧】,等到腳本正常退出或顯式調用exit()時,再把註冊進去的function拉出來執行.
register_shutdown_function()調用的3種狀況:
腳本正常退出時;
在腳本運行(run-time not parse-time)出錯退出時;
用戶調用exit方法退出時。
error_get_last();//函數獲取最後發生的錯誤。
該函數以數組的形式返回最後發生的錯誤。
返回的數組包含 4 個鍵和值:
[type] - 錯誤類型
[message] - 錯誤消息
[file] - 發生錯誤所在的文件
[line] - 發生錯誤所在的行
在parse-time出錯的時候,是不會調用register_shutdown_function()函數的。只有在run-time出錯的時候,纔會調用register_shutdown_function()。
爲了更好的理解,下面咱們舉例說明:
<?php register_shutdown_function("error_handler"); function error_handler(){ echo "Yeah,it's worked!"; } function test(){} function test(){}
執行結果以下:
在執行error_handler.php的時候,因爲重複定義了兩個函數test(),在php的parse-time就出錯了(不是run-time),因此不能回調register_shutdown_function()中註冊的函數。
<?php register_shutdown_function("error_handler"); function error_handler(){ echo "Yeah,it's worked!"; } if(true){ function test(){} } function test(){}
執行結果以下:
咱們看到,上面回調了register_shutdown_function()中註冊的函數。
由於咱們加了一個if()判斷,if()裏面的test()方法,至關於一個閉包,與外面的test()名稱不衝突。
也就是,上面的代碼在parse-time沒有出錯,而是在run-time的時候出錯了,因此咱們可以獲取到fatal error。
<?php register_shutdown_function("error_handler"); function error_handler(){ echo "Yeah,it's worked!"; }
<?php include './error_handler.php'; function test(){} function test(){}
執行 test.php的結果以下
當咱們在運行test.php的時候,由於redeclare了兩個test()方法,因此php的語法解析器在parse-time的時候就出錯了。 因此不能回調register_shutdown_function()中的方法,不能catch住這個fatal error。
<?php register_shutdown_function("error_handler"); function error_handler(){ echo "Yeah,it's worked!"; }
<?php function test(){} function test(){}
<?php require './error_handler.php'; require './test.php';
執行 include_all.php的結果以下
上面咱們捕獲了fatal_error。
由於在運行include_all.php的時候,include_all.php自己語法並無出錯,也就是在parse-time的時候並無出錯,而是include的文件出錯了,也就是在run-time的時候出錯了,這個時候是能回調register_shutdown_function()中的函數的。
"哇塞,原來能夠這樣啊!"
王小五按答案中舉的例子認真的敲完代碼,瞬間明白瞭解決的辦法。
真可謂"衆裏尋他千百度,驀然回首,那人卻在燈火闌珊處。"小二不自覺的感嘆道!
"好了,我本身就寫一個error_handler腳本吧,確保每次都能獲取到想要的Fatal Error。"
<?php register_shutdown_function( "fatal_handler" ); set_error_handler("error_handler"); define('E_FATAL', E_ERROR | E_USER_ERROR | E_CORE_ERROR | E_COMPILE_ERROR | E_RECOVERABLE_ERROR| E_PARSE ); //獲取fatal error function fatal_handler() { $error = error_get_last(); if($error && ($error["type"]===($error["type"] & E_FATAL))) { $errno = $error["type"]; $errfile = $error["file"]; $errline = $error["line"]; $errstr = $error["message"]; error_handler($errno,$errstr,$errfile,$errline); } } //獲取全部的error function error_handler($errno,$errstr,$errfile,$errline){ $str=<<<EOF "errno":$errno "errstr":$errstr "errfile":$errfile "errline":$errline EOF; //獲取到錯誤能夠本身處理,好比記Log、報警等等 echo $str; }
有了這個腳本,我再按SO上說的第四種方法去執行,那這個需求就實現了!
王小五興沖沖的找到Z哥,詳細的說明了本身的研究成果。
次日,小五按照公司現有的框架規則,結合上面的解決辦法,不一會就實現了需求。
"不錯啊,小五,我就說你能夠吧!" Z哥高興的說到。
"哈哈,Z哥,這下全部的錯誤都在掌握之中了!"
轉載聲明:本文轉載自「聊聊代碼」,搜索「talkpoem」便可關注。
關注「聊聊代碼」,讓咱們一塊兒聊聊「左手代碼右手詩」的事兒。