在正式学习Swoole之前,我们需要先了解Swoole中是如何捕捉异常错误的,否则我们没办法进行错误调试,因为在之前的文章中我们已经说过,Swoole中是不允许直接exit/die的。

1、可捕获的异常 / 错误

在 PHP 大致有三种类型的可捕获的异常 / 错误

  1. ErrorPHP 内核抛出错误的专用类型,如类不存在,函数不存在,函数参数错误,都会抛出此类型的错误,PHP 代码中不应该使用 Error类来作为异常抛出
  2. Exception:应用开发者应该使用的异常基类
  3. ErrorException:此异常基类专门负责将 PHP Warning/Notice 等信息通过 set_error_handler 转换成异常,PHP 未来的规划必然是将所有的 Warning/Notice 转为异常,以便于 PHP 程序能够更好更可控地处理各种错误

以上所有类都实现了 Throwable 接口,也就是说,通过 try {} catch(Throwable $e) {} 即可捕获所有可抛出的异常 / 错误

  1. try {
  2. test();
  3. } catch(Throwable $e) {
  4. var_dump($e);
  5. }

示例2:

  1. try {
  2. test();
  3. }
  4. catch (Error $e) {
  5. var_dump($e);
  6. }
  7. catch(Exception $e) {
  8. var_dump($e);
  9. }

2、不可捕获的致命错误和异常

PHP 错误的一个重要级别,如异常 / 错误未捕获时、内存不足时或是一些编译期错误 (继承的类不存在),将会以 E_ERROR 级别抛出一个 Fatal Error,是在程序发生不可回溯的错误时才会触发的,PHP 程序无法捕获这样级别的一种错误,只能通过 register_shutdown_function 在后续进行一些处理操作。

3、在协程中捕获运行时异常 / 错误

Swoole4 协程编程中,某个协程的代码中抛出错误,会导致整个进程退出,进程所有协程终止执行。

在协程顶层空间可以先进行一次 try/catch 捕获异常 / 错误,仅终止出错的协程。

  1. Co\run(function () {
  2. go(function () {
  3. try {
  4. call_user_func($func);
  5. }
  6. catch (Error $e) {
  7. var_dump($e);
  8. }
  9. catch(Exception $e) {
  10. var_dump($e);
  11. }
  12. });
  13. //协程1的错误不影响协程2
  14. go(function () {
  15. Co::sleep(5);
  16. echo 2;
  17. });
  18. });

4、捕获 Server 运行期致命错误

Server 运行期一旦发生致命错误,那客户端连接将无法得到回应。

Web 服务器,如果有致命错误应当向客户端发送 Http 500 错误信息。

在 PHP 中可以通过 register_shutdown_function + error_get_last 2 个函数来捕获致命错误,并将错误信息发送给客户端连接。

具体代码示例如下:

  1. $http = new Swoole\Http\Server("127.0.0.1", 9501);
  2. $http->on('request', function ($request, $response) {
  3. register_shutdown_function(function () use ($response) {
  4. $error = error_get_last();
  5. var_dump($error);
  6. switch ($error['type'] ?? null) {
  7. case E_ERROR :
  8. case E_PARSE :
  9. case E_CORE_ERROR :
  10. case E_COMPILE_ERROR :
  11. // log or send:
  12. // error_log($message);
  13. // $server->send($fd, $error['message']);
  14. $response->status(500);
  15. $response->end($error['message']);
  16. break;
  17. }
  18. });
  19. exit(0);
  20. });
  21. $http->start();

5、错误码

在Swoole中,组件调用失败,正常是不会抛出异常的,所以我们可以通过Swoole内置的swoole_last_error()函数捕捉到错误码。

可使用 swoole_last_error() 获取当前的错误码;

可使用 swoole_strerror(int $errno, 9); 将 Swoole 底层错误码转换成文字错误信息;

例如:

  1. echo swoole_strerror(swoole_last_error(), 9) . PHP_EOL;
  2. echo swoole_strerror(SWOOLE_ERROR_MALLOC_FAIL, 9) . PHP_EOL;

示例2:

  1. swoole_timer_tick($msec, function($timer_id){
  2. $result = $process->write($message);
  3. if($result === false){
  4. $errno = swoole_last_error();//获取最近一次Swoole底层的错误码
  5. $errmsg = swoole_strerror($errno, 9);//将错误码转化为错误信息
  6. echo "[error] errno {$errno} errmsg:{$errmsg}".PHP_EOL;
  7. }else{
  8. echo "[write] success {$result} bytes".PHP_EOL;
  9. }
  10. });

官方的错误码太多了,还包含了Linux的错误码,具体可以参考官方文档:https://wiki.swoole.com/#/other/errno