0

0

聊聊在PHP7中对于Error的处理是怎样的

藏色散人

藏色散人

发布时间:2021-11-01 16:50:43

|

2484人浏览过

|

来源于juejin

转载

前段时间在项目中遇到一个当时觉得比较奇怪的情况:使用 GuzzleHttp 发送 curl 请求,API 响应超时导致抛出异常。但 catch(\Exception) 并没有捕获异常,导致代码意外停止运行。后来查资料发现,在 PHP 7 中,GuzzleHttp 请求超时抛出的异常继承的是 Error,而 Error 并没有继承 Exception,所以 catch(\Exception) 无法捕获并处理该异常。

php 7 中对 error 的处理

在 PHP 5 中,当程序中有致命错误发生时,脚本会立即停止运行。并且,通过 set_error_handler 设置的错误处理程序在这种情况下并不会被调用。

【推荐学习:PHP7教程

⒈ 自定义错误处理程序 set_error_handler

  set_error_handler 接受两个参数,第一个为自定义的错误处理函数,第二个参数指定触发该自定义错误处理函数的错误级别。但需要指出的是,在任何时候,只能有一个自定义的错误处理程序起作用。

立即学习PHP免费学习笔记(深入)”;

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;

  以上代码在执行以后,会输出 PHP Notice: Undefined variable: foo 。在第二个 set_error_handler 执行以后,自定义错误处理函数变成了 func_error ,同时,触发自定义错误处理函数的错误级别变成了 E_ERROR 。而在 PHP 中,变量未定义只会触发 E_NOTICE 级别的错误,所以自定义的错误处理函数并不会被触发。

需要指出的是,自定义的错误处理函数对以下几种错误级别并不起作用:

E_ERROR、E_PARSE、E_CORE_ERROR、E_CORE_WARNING、E_COMPILE_ERROR、E_COMPILE_WARNING、E_STRICT

  在上述几种自定义错误处理程序无法处理的错误中,凡是以 ERROR 结尾的都是致命错误。其他几种虽然不是致命错误,但

  • E_PARSE 是在解析 PHP 代码时产生的错误,此时 PHP 代码尚未开始运行,自定义错误处理程序自然无法处理该错误

  • E_CORE_WARNING 产生于 PHP 的初始化启动阶段,此时 PHP 代码仍然尚未运行,所以不能被自定义错误处理程序处理

  • E_COMPILE_WARNING 是在 PHP 代码的编译阶段产生,所以不能被自定义错误处理程序处理

而至于 E_STRICT 是 PHP 为了保证代码的最佳互操作性和向前兼容而提出的代码修改建议,自然也不会被自定义错误处理函数处理

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();

   以上代码运行输出结果:

PHP Fatal error:  Call to a member function method() on string

  虽然设置了自定义错误处理程序,但在致命错误发生时,并不起作用。

  对于这种自定义错误处理程序无法处理的致命错误,在 PHP 5 中可以通过注册一个终止回调(shutdown_function)来记录具体的错误信息,但也仅限于记录错误信息,当发生致命错误时代码仍然会停止运行。

$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();

  以上代码执行会输出

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
)

⒉ 撤销自定义错误处理程序

  当同时设置多个自定义错误处理程序时,虽然只有最后设置的自定义错误处理程序起作用。但所有设置的自定义错误处理程序会以栈的方式保存(FILO)。

  使用 restore_error_handler 可以撤销最近一次设置的自定义错误处理程序;如果同时调用了多次 set_error_handler ,则每调用一次 restore_error_handler,处于栈顶的错误处理程序就会被撤销。

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;

  以上代码运行,会输出:

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

⒊ PHP 7 中对错误的处理

  在 PHP 7 中,当有致命错误或 E_RECOVERABLE_ERROR 类型的错误发生时,通常会抛出一个 Error,程序并不会终止。

Copy Leaks
Copy Leaks

AI内容检测和分级,帮助创建和保护原创内容

下载
try {
    $obj = 'foo';
    $obj->method();
} catch (\Error $e) {
    echo $e->getMessage();
}

  运行以上代码会输出

Call to a member function method() on string

E_RECOVERABLE_ERROR 是一种可捕获的致命错误,这种错误的出现并不会使得 Zend 引擎处于不稳定的状态,但必须被捕获并且处理。如果不处理,那么这种错误最终会变成 E_ERROR 类型的错误,最终导致 PHP 代码停止运行。

  php 7 中,并不是所有的致命错误都会抛出 Error,一些特定情况下出现的致命错误( Out Of Memory)仍然会导致代码停止运行。另外,如果抛出的 Error 没有被捕获并处理,则代码仍然会停止运行。

// 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 395191241240 bytes)
// 抛出的 Error 没有被捕获并处理,代码依然会停止运行
$obj = 'foo';
$obj->method();
// 执行以上代码,由于并没有用 try/catch 捕获并处理抛出的 Error,程序仍然会停止运行
PHP Fatal error:  Uncaught Error: Call to a member function method() on string

  PHP 7 中的 Error 并没有继承 Exception,之所以这样做是为了防止 PHP 5 中捕获并处理 Exception 的代码捕获这些 Error。因为在 PHP 5 中,这些致命错误是会导致代码停止运行的。

  Error 和 Exception 都继承自 Throwable 。在 PHP 7 中,Throwable 是一个 interface,所有能通过 throw 关键字抛出的对象都实现了这个 interface。

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 修复。

相关文章

PHP速学教程(入门到精通)
PHP速学教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
Golang gRPC 服务开发与Protobuf实战
Golang gRPC 服务开发与Protobuf实战

本专题系统讲解 Golang 在 gRPC 服务开发中的完整实践,涵盖 Protobuf 定义与代码生成、gRPC 服务端与客户端实现、流式 RPC(Unary/Server/Client/Bidirectional)、错误处理、拦截器、中间件以及与 HTTP/REST 的对接方案。通过实际案例,帮助学习者掌握 使用 Go 构建高性能、强类型、可扩展的 RPC 服务体系,适用于微服务与内部系统通信场景。

8

2026.01.15

公务员递补名单公布时间 公务员递补要求
公务员递补名单公布时间 公务员递补要求

公务员递补名单公布时间不固定,通常在面试前,由招录单位(如国家知识产权局、海关等)发布,依据是原入围考生放弃资格,会按笔试成绩从高到低递补,递补考生需按公告要求限时确认并提交材料,及时参加面试/体检等后续环节。要求核心是按招录单位公告及时响应、提交材料(确认书、资格复审材料)并准时参加面试。

44

2026.01.15

公务员调剂条件 2026调剂公告时间
公务员调剂条件 2026调剂公告时间

(一)符合拟调剂职位所要求的资格条件。 (二)公共科目笔试成绩同时达到拟调剂职位和原报考职位的合格分数线,且考试类别相同。 拟调剂职位设置了专业科目笔试条件的,专业科目笔试成绩还须同时达到合格分数线,且考试类别相同。 (三)未进入原报考职位面试人员名单。

58

2026.01.15

国考成绩查询入口 国考分数公布时间2026
国考成绩查询入口 国考分数公布时间2026

笔试成绩查询入口已开通,考生可登录国家公务员局中央机关及其直属机构2026年度考试录用公务员专题网站http://bm.scs.gov.cn/pp/gkweb/core/web/ui/business/examResult/written_result.html,查询笔试成绩和合格分数线,点击“笔试成绩查询”按钮,凭借身份证及准考证进行查询。

11

2026.01.15

Java 桌面应用开发(JavaFX 实战)
Java 桌面应用开发(JavaFX 实战)

本专题系统讲解 Java 在桌面应用开发领域的实战应用,重点围绕 JavaFX 框架,涵盖界面布局、控件使用、事件处理、FXML、样式美化(CSS)、多线程与UI响应优化,以及桌面应用的打包与发布。通过完整示例项目,帮助学习者掌握 使用 Java 构建现代化、跨平台桌面应用程序的核心能力。

65

2026.01.14

php与html混编教程大全
php与html混编教程大全

本专题整合了php和html混编相关教程,阅读专题下面的文章了解更多详细内容。

36

2026.01.13

PHP 高性能
PHP 高性能

本专题整合了PHP高性能相关教程大全,阅读专题下面的文章了解更多详细内容。

75

2026.01.13

MySQL数据库报错常见问题及解决方法大全
MySQL数据库报错常见问题及解决方法大全

本专题整合了MySQL数据库报错常见问题及解决方法,阅读专题下面的文章了解更多详细内容。

21

2026.01.13

PHP 文件上传
PHP 文件上传

本专题整合了PHP实现文件上传相关教程,阅读专题下面的文章了解更多详细内容。

35

2026.01.13

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
PHP课程
PHP课程

共137课时 | 8.7万人学习

JavaScript ES5基础线上课程教学
JavaScript ES5基础线上课程教学

共6课时 | 7万人学习

PHP新手语法线上课程教学
PHP新手语法线上课程教学

共13课时 | 0.9万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号