今天看書《PHP核心技術與最佳實踐》,裏面有使用到一個函數,register_shutdown_function,由於之前沒有用過該函數,就去查了一下資料,就覺得是個很實用的函數,所以這裏寫一下這個函數的用法。
1. 函數說明
定義:該函數是來註冊一個會在PHP中止時執行的函數
參數說明:
void register_shutdown_function ( callable $callback [, mixed $parameter [, mixed $... ]] )
註冊一個 callback ,它會在腳本執行完成或者 exit() 後被調用。
callback:待註冊的中止回調
parameter:可以通過傳入額外的參數來將參數傳給中止函數
2. PHP中止的情況
PHP中止的情況有三種:
- 執行完成
- exit/die導致的中止
- 發生致命錯誤中止
a. 第一種情況,執行完成
<?php
function test()
{
echo '這個是中止方法test的輸出';
}
register_shutdown_function('test');
echo 'before' . PHP_EOL;
運行:
before
這個是中止方法test的輸出
注意輸出的順序,等執行完成了之後纔會去執行register_shutdown_function的中止方法test
b. 第二種情況,exit/die導致的中止
<?php
function test()
{
echo '這個是中止方法test的輸出';
}
register_shutdown_function('test');
echo 'before' . PHP_EOL;
exit();
echo 'after' . PHP_EOL;
運行:
before
這個是中止方法test的輸出
後面的after並沒有輸出,即exit或者是die方法導致提前中止。
c. 第三種情況,發送致命錯誤中止
<?php
function test()
{
echo '這個是中止方法test的輸出';
}
register_shutdown_function('test');
echo 'before' . PHP_EOL;
// 這裏會發生致命錯誤
$a = new a();
echo 'after' . PHP_EOL;
運行:
before
Fatal error: Uncaught Error: Class 'a' not found in D:\laragon\www\php_book\test.php on line 12
Error: Class 'a' not found in D:\laragon\www\php_book\test.php on line 12
Call Stack:
0.0020 360760 1. {main}() D:\laragon\www\php_book\test.php:0
這個是中止方法test的輸出
後面的after也是沒有輸出,致命錯誤導致提前中止了。
3. 參數
第一個參數支持以數組的形式來調用類中的方法,第二個以及後面的參數都是可以當做額外的參數傳給中止方法。
<?php
class Shutdown
{
public function stop()
{
echo "這個是stop方法的輸出";
}
}
// 當PHP終止的時候(執行完成或者是遇到致命錯誤中止的時候)會調用new Shutdown的stop方法
register_shutdown_function([new Shutdown(), 'stop']);
// 將因爲致命錯誤而中止
$a = new a();
// 這一句並沒有執行,也沒有輸出
echo '必須終止';
也可以在類中執行:
<?php
class TestDemo {
public function __construct()
{
register_shutdown_function([$this, "f"], "hello");
}
public function f($str)
{
echo "class TestDemo->f():" . $str;
}
}
$demo = new TestDemo();
echo 'before' . PHP_EOL;
/**
運行:
before
class TestDemo->f():hello
*/
4. 同時調用多個
可以多次調用 register_shutdown_function,這些被註冊的回調會按照他們註冊時的順序被依次調用。
不過注意的是,如果在第一個註冊的中止方法裏面調用exit方法或者是die方法的話,那麼其他註冊的中止回調也不會被調用。代碼:
<?php
/**
* 可以多次調用 register_shutdown_function,這些被註冊的回調會按照他們註冊時的順序被依次調用。
* 注意:如果你在f方法(第一個註冊的方法)裏面調用exit方法或者是die方法的話,那麼其他註冊的中止回調也不會被調用
*/
/**
* @param $str
*/
function f($str) {
echo $str . PHP_EOL;
// 如果下面調用exit方法或者是die方法的話,其他註冊的中止回調不會被調用
// exit();
}
// 註冊第一個中止回調f方法
register_shutdown_function("f", "hello");
class TestDemo {
public function __construct()
{
register_shutdown_function([$this, "f"], "hello");
}
public function f($str)
{
echo "class TestDemo->f():" . $str;
}
}
$demo = new TestDemo();
echo 'before' . PHP_EOL;
/**
運行:
before
hello
class TestDemo->f():hello
注意:如果f方法裏面調用了exit或者是die的話,那麼最後的class TestDemo->f():hello不會輸出
*/
5. 用處
該函數的作用:
- 析構函數:在PHP4的時候,由於類不支持析構函數,所以這個函數經常用來模擬實現析構函數
- 致命錯誤的處理:使用該函數可以用來捕獲致命錯誤並且在發生致命錯誤後恢復流程處理
代碼如下:
<?php
/**
* register_shutdown_function,註冊一個會在php中止時執行的函數,中止的情況包括髮生致命錯誤、die之後、exit之後、執行完成之後都會調用register_shutdown_function裏面的函數
* Created by PhpStorm.
* User: Administrator
* Date: 2017/7/15
* Time: 17:41
*/
class Shutdown
{
public function stop()
{
echo 'Begin.' . PHP_EOL;
// 如果有發生錯誤(所有的錯誤,包括致命和非致命)的話,獲取最後發生的錯誤
if (error_get_last()) {
print_r(error_get_last());
}
// ToDo:發生致命錯誤後恢復流程處理
// 中止後面的所有處理
die('Stop.');
}
}
// 當PHP終止的時候(執行完成或者是遇到致命錯誤中止的時候)會調用new Shutdown的stop方法
register_shutdown_function([new Shutdown(), 'stop']);
// 將因爲致命錯誤而中止
$a = new a();
// 這一句並沒有執行,也沒有輸出
echo '必須終止';
運行:
Fatal error: Uncaught Error: Class 'a' not found in D:\laragon\www\php_book\1_23_register_shutdown.php on line 31
Error: Class 'a' not found in D:\laragon\www\php_book\1_23_register_shutdown.php on line 31
Call Stack:
0.0060 362712 1. {main}() D:\laragon\www\php_book\1_23_register_shutdown.php:0
Begin.
Array
(
[type] => 1
[message] => Uncaught Error: Class 'a' not found in D:\laragon\www\php_book\1_23_register_shutdown.php:31
Stack trace:
#0 {main}
thrown
[file] => D:\laragon\www\php_book\1_23_register_shutdown.php
[line] => 31
)
Stop.
注意:PHP7中新增了Throwable異常類,這個類可以捕獲致命錯誤,即可以使用try...catch(Throwable $e)來捕獲致命錯誤,代碼如下:
<?php
try {
// 將因爲致命錯誤而中止
$a = new a();
// 這一句並沒有執行,也沒有輸出
echo 'end';
} catch (Throwable $e) {
print_r($e);
echo $e->getMessage();
}
運行:
Error Object
(
[message:protected] => Class 'a' not found
[string:Error:private] =>
[code:protected] => 0
[file:protected] => C:\laragon\www\php_book\throwable.php
[line:protected] => 5
[trace:Error:private] => Array
(
)
[previous:Error:private] =>
[xdebug_message] =>
Error: Class 'a' not found in C:\laragon\www\php_book\throwable.php on line 5
Call Stack:
0.0000 349856 1. {main}() C:\laragon\www\php_book\throwable.php:0
)
Class 'a' not found
這樣的話,PHP7中使用Throwable來捕獲的話比使用register_shutdown_function這個函數來得更方便,也更推薦Throwable。
注意:Error類也是可以捕獲到致命錯誤,不過Error只能捕獲致命錯誤,不能捕獲異常Exception,而Throwable是可以捕獲到錯誤和異常的,所以更推薦。
總結:register_shutdown_function這個函數主要是用在處理致命錯誤的後續處理上(PHP7更推薦使用Throwable來處理致命錯誤),不過缺點也很明顯,只能處理致命錯誤Fatal error,其他的錯誤包括最高錯誤Parse error也是沒辦法處理的。