Rumah >pembangunan bahagian belakang >PHP7 >Mari kita bincangkan tentang cara Ralat dikendalikan dalam PHP7
Beberapa masa lalu, saya menghadapi situasi pelik dalam projek: apabila menggunakan GuzzleHttp untuk menghantar permintaan curl, respons API telah tamat masa, menyebabkan pengecualian dilemparkan. Tetapi catch(Exception) tidak menangkap pengecualian, menyebabkan kod berhenti berjalan tanpa diduga. Kemudian, saya menyemak maklumat dan mendapati bahawa dalam PHP 7, pengecualian yang dilemparkan oleh GuzzleHttp permintaan tamat masa mewarisi Ralat, dan Ralat tidak mewarisi Pengecualian, jadi catch(Exception) tidak boleh menangkap dan mengendalikan pengecualian.
Pengendalian Ralat dalam PHP 7
Dalam PHP 5, apabila ralat maut berlaku dalam atur cara, skrip akan berhenti berjalan serta-merta. Selain itu, pengendali ralat yang ditetapkan melalui set_error_handler tidak akan dipanggil dalam kes ini.
[Pembelajaran yang disyorkan: Tutorial PHP7]
⒈ Set_error_handler pengendali ralat tersuai
set_error_handler, menerima dua Parameter_handler yang pertama ialah fungsi pengendalian ralat tersuai, dan parameter kedua menentukan tahap ralat yang mencetuskan fungsi pengendalian ralat tersuai. Tetapi harus diingat bahawa hanya satu pengendali ralat tersuai boleh aktif pada bila-bila masa.
function func_notice($num, $str, $file, $line) { print "Encountered notice $num in $file, line $line: $str\n"; } function func_error($num, $str, $file, $line) { print "Encountered error $num in $file, line $line: $str\n"; } set_error_handler("func_notice", E_NOTICE); set_error_handler("func_error", E_ERROR); echo $foo;
Selepas kod di atas dilaksanakan, Notis PHP: Pembolehubah tidak ditentukan: foo akan dikeluarkan. Selepas set_error_handler kedua dilaksanakan, fungsi pengendalian ralat tersuai menjadi func_error, dan pada masa yang sama, tahap ralat yang mencetuskan fungsi pengendalian ralat tersuai menjadi E_ERROR. Dalam PHP, pembolehubah tidak ditentukan hanya akan mencetuskan ralat tahap E_NOTICE, jadi fungsi pengendalian ralat tersuai tidak akan dicetuskan.
Perlu ditegaskan bahawa fungsi pengendalian ralat tersuai tidak berfungsi pada tahap ralat berikut:
E_ERROR、E_PARSE、E_CORE_ERROR、E_CORE_WARNING、E_COMPILE_ERROR、E_COMPILE_WARNING、E_STRICT
Jenis ralat yang disebutkan di atas yang tidak dapat dikendalikan oleh pengendali ralat tersuai , apa-apa yang berakhir dengan ERROR adalah ralat yang membawa maut. Walaupun beberapa jenis ralat yang lain tidak membawa maut,
E_PARSE ialah ralat yang dijana semasa menghuraikan kod PHP Pada masa ini, kod PHP belum mula berjalan dan ralat tersuai pengendali secara semula jadi tidak dapat menangani ralat itu pengendali ralat
E_COMPILE_WARNING dijana semasa fasa penyusunan kod PHP, jadi ia tidak boleh dikendalikan oleh pengendali ralat tersuai
dan E_STRICT dicipta oleh PHP untuk memastikan kebolehoperasian terbaik dan keserasian hadapan kod Cadangan pengubahsuaian kod yang dicadangkan secara semula jadi tidak akan diproses oleh fungsi pengendalian ralat tersuai
function func_error($num, $str, $file, $line) { print "Encountered error $num in $file, line $line: $str\n"; } set_error_handler('func_error', E_NOTICE); $obj = 'foo'; $obj->method(); Walaupun pengendali ralat tersuai ditetapkan, Tetapi ia tidak berfungsi apabila ralat maut berlaku. Untuk ralat maut yang tidak boleh dikendalikan oleh pengendali ralat tersuai ini, dalam PHP 5 anda boleh mendaftarkan panggilan balik penutupan (shutdown_function) untuk merekod maklumat ralat tertentu, tetapi ia terhad kepada merekod maklumat ralat apabila ia berlaku. kod masih akan berhenti berjalan pada ralat maut.
PHP Fatal error: Call to a member function method() on stringPelaksanaan kod di atas akan mengeluarkan
$shutdownHandler = function(){ print PHP_EOL; print "============================" . PHP_EOL; print "Running the shutdown handler" . PHP_EOL; $error = error_get_last(); if (!empty($error)) { print "Looks like there was an error: " . print_r($error, true) . PHP_EOL; // 可以添加记录日志的逻辑 } else { // 程序正常运行结束 print "Running a normal shutdown without error." . PHP_EOL; } }; register_shutdown_function($shutdownHandler); $obj = 'foo'; $obj->method();
⒉ Buat asal pengendali ralat tersuai
PHP Fatal error: Call to a member function method() on string in /home/chenyan/test.php on line 24 ============================ Running the shutdown handler Looks like there was an error: Array ( [type] => 1 [message] => Call to a member function method() on string [file] => /home/chenyan/test.php [line] => 24 )
Apabila berbilang ralat tersuai ditetapkan pada masa yang sama pengendali, walaupun hanya set pengendali ralat tersuai terakhir yang berfungsi. Walau bagaimanapun, semua pengendali ralat tersuai yang ditetapkan akan disimpan dalam tindanan (FILO). Gunakan restore_error_handler untuk membuat asal pengendali ralat tersuai yang paling baru ditetapkan; jika set_error_handler dipanggil berbilang kali pada masa yang sama, setiap kali restore_error_handler dipanggil, pengendali ralat di bahagian atas tindanan akan dibuat asal.
Apabila kod di atas dijalankan, ia akan mengeluarkan:function func_notice($num, $str, $file, $line) { print "Encountered notice : $str\n"; } set_error_handler("func_notice", E_NOTICE); set_error_handler("func_notice", E_NOTICE); set_error_handler("func_notice", E_NOTICE); echo $foo; set_error_handler("func_notice", E_NOTICE); echo $foo; restore_error_handler(); echo $foo; restore_error_handler(); echo $foo; restore_error_handler(); echo $foo; restore_error_handler(); echo $foo;
⒊ Ralat pengendalian dalam PHP 7
Encountered notice : Undefined variable: foo Encountered notice : Undefined variable: foo Encountered notice : Undefined variable: foo Encountered notice : Undefined variable: foo Encountered notice : Undefined variable: foo PHP Notice: Undefined variable: foo
Dalam PHP 7, apabila ralat maut atau ralat jenis E_RECOVERABLE_ERROR berlaku, Ralat biasanya dilemparkan dan atur cara tidak ditamatkan.
try { $obj = 'foo'; $obj->method(); } catch (\Error $e) { echo $e->getMessage(); }E_RECOVERABLE_ERROR ialah ralat maut yang boleh terperangkap. Kejadian ralat ini tidak akan menjadikan enjin Zend berada dalam keadaan tidak stabil ditangkap dan ditangani. Jika tidak dikendalikan, ralat ini akhirnya akan bertukar menjadi ralat jenis E_ERROR, akhirnya menyebabkan kod PHP berhenti berjalan. Dalam PHP 7, tidak semua ralat maut akan membuang Ralat Fatal (Out Of Memory) yang berlaku dalam keadaan tertentu masih akan menyebabkan kod berhenti berjalan. Selain itu, jika Ralat yang dilemparkan tidak ditangkap dan dikendalikan, kod itu masih akan berhenti berjalan.
Call to a member function method() on stringRalat dalam PHP 7 tidak mewarisi Exception Ini dilakukan untuk menghalang kod yang menangkap dan mengendalikan Exception dalam PHP 5 daripada menangkap Ralat ini. Kerana dalam PHP 5, ralat maut ini akan menyebabkan kod berhenti berjalan. Ralat dan Pengecualian kedua-duanya diwarisi daripada Throwable. Dalam PHP 7, Throwable ialah antara muka, dan semua objek yang boleh dilemparkan melalui kata kunci lontaran melaksanakan antara muka ini.
// bak.sql 的大小为 377 M // PHP 配置的 memory_limit = 128M try { $file = './bak.sql'; file_get_contents($file); } catch (\Error $e) { echo $e->getMessage(); } // 执行以上代码,仍然会产生致命错误 PHP Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 395191240 bytes) // 抛出的 Error 没有被捕获并处理,代码依然会停止运行 $obj = 'foo'; $obj->method(); // 执行以上代码,由于并没有用 try/catch 捕获并处理抛出的 Error,程序仍然会停止运行 PHP Fatal error: Uncaught Error: Call to a member function method() on string
interface Throwable { public function getMessage(): string; public function getCode(): int; public function getFile(): string; public function getLine(): int; public function getTrace(): array; public function getTraceAsString(): string; public function getPrevious(): Throwable; public function __toString(): string; }
需要指出的是,Throwable 是 PHP 底层的 interface,PHP 代码中不能直接实现 Throwable 。之所以作出这个限制,是因为通常只有 Error 和 Exception 可以被抛出,并且这些抛出的 Error 和 Exception 中还存储了它们被抛出的堆栈跟踪信息,而 PHP 代码中开发者自定义的 class 无法实现这些。
要在 PHP 代码中实现 Throwable 必须通过继承 Exception 来实现。
interface CustomThrowable extends Throwable {} class CustomException extends Exception implements CustomThrowable {} throw new CustomException();
PHP 7 中 Error 和 Exception 的继承关系
interface Throwable |- Exception implements Throwable |- Other Exception classes |- Error implements Throwable |- TypeError extends Error |- ParseError extends Error |- AssertionError extends Error |- ArithmeticError extends Error |- DivizionByZeroError extends ArithmeticError
TypeError
当函数的传参或返回值的数据类型与申明的数据类型不一致时,会抛出 TypeError
function add(int $left, int $right) { return $left + $right; } try { $value = add('left', 'right'); } catch (TypeError $e) { echo $e->getMessage(); } // 运行以上代码,会输出: Argument 1 passed to add() must be of the type int, string given
当开启严格模式时,如果 PHP 内建函数的传参个数与要求的参数不一致,也会抛出 TypeError
declare(strict_types = 1); try { substr('abc'); } catch (TypeError $e) { echo $e->getMessage(); } // 运行以上代码,会输出: substr() expects at least 2 parameters, 1 given
默认情况下,PHP 7 处于弱模式。在弱模式下,PHP 7 会尽可能的将传参的数据类型转换为期望的数据类型。例如,如果函数期望的参数类型为 string,而实际传参的数据类型的 int,那么 PHP 会把 int 转换为 string。
// declare(strict_types = 1); function add(string $left, string $right) { return $left + $right; } try { $value = add(11, 22); echo $value; } catch (TypeError $e) { echo $e->getMessage(); } // 以上代码运行,会正常输出 33,PHP 会对传参的数据类型做转换(int→string→int) // 但如将 PHP 改为严格模式,则运行是会抛出 TypeError Argument 1 passed to add() must be of the type string, int given
ParseError
当在 include 或 require 包含的文件中存在语法错误,或 eval() 函数中的代码中存在语法错误时,会抛出 ParseError
// a.php $a = 1 $b = 2 // test.php try { require 'a.php'; } catch (ParseError $e) { echo $e->getMessage(); } // 以上代码运行会输出: syntax error, unexpected '$b' (T_VARIABLE) // eval 函数中的代码存在语法错误 try { eval("$a = 1"); } catch (ParseError $e) { echo $e->getMessage(); } // 以上代码运行会输出: syntax error, unexpected end of file
AssertionError
当断言失败时,会抛出 AssertionError(此时要求 PHP 配置中 zend.assertions = 1,assert.exception = 1,这两个配置可以在 php.ini 文件中配置,也可以通过 ini_set() 在 PHP 代码中配置)。
ini_set('zend_assertions', 1); ini_set('assert.exception', 1); try { $test = 1; assert($test === 0); } catch (AssertionError $e) { echo $e->getMessage(); } // 运行以上代码会输出: assert($test === 0)
ArithmeticError
在 PHP 7 中,目前有两种情况会抛出 ArithmeticError:按位移动操作,第二个参数为负数;使用 intdiv() 函数计算 PHP_INT_MIN 和 -1 的商(如果使用 / 计算 PHP_INT_MIN 和 -1 的商,结果会自动转换为 float 类型)。
try { $value = 1 << -1; } catch (ArithmeticError $e) { echo $e->getMessage(); } // 运行以上代码,会输出: Bit shift by negative number try { $value = intdiv(PHP_INT_MIN, -1); } catch (ArithmeticError $e) { echo $e->getMessage(); } // 运行以上代码,会输出: Division of PHP_INT_MIN by -1 is not an integer
DivisionByZeroError
抛出 DivisionByZeorError 的情况目前也有两种:在进行取模(%)运算时,第二个操作数为 0;使用 intdiv() 计算两个数的商时,除数为 0。如果使用 / 计算两个数的商时除数为 0,PHP 只会产生一个 Warning。并且,如果被除数非 0,则结果为 INF,如果被除数也是 0,则结果为 NaN。
try { $value = 1 % 0; echo $value; } catch (DivisionByZeroError $e) { echo $e->getMessage(), "\n"; } // 运行以上代码,会输出: Modulo by zero try { $value = intdiv(0, 0); echo $value; } catch (DivisionByZeroError $e) { echo $e->getMessage(), "\n"; } // 运行以上代码,会输出: Division by zero
通常在实际的业务中,捕获并处理抛出的 Error 并不常见,因为一旦抛出 Error 说明代码存在严重的 BUG,需要修复。所以,在实际的业务中,Error 更多的只是被用来捕获并记录具体的错误日志,然后通知开发者进行 BUG 修复。
Atas ialah kandungan terperinci Mari kita bincangkan tentang cara Ralat dikendalikan dalam PHP7. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!