php7的错误与异常处理
错误
错误是写程序中不可避免的,如何去发现定位错误及改正更是一门学问。今天和大家聊聊php7之后的错误与异常。
错误的分类
一般错误分为语法错误、运行时错误、逻辑错误。语法以及运行时错误很容易去排查,但逻辑错误就不是那么简单的了。鄙人在判断的时候也有多次将比较运算==写成了赋值运算=,像我这种就很难去发现。一般随着经验的丰富(踩坑踩多了),会越来越容易来找到逻辑错误。
控制错误的输出
一般建议开发阶段开启错误输出方便及时发现错误,上线阶段为了安全性及美观建议关闭错误输出。
php提供了一个参数display_errors
用于控制错误向浏览器、cli的输出。有两种方式可以来修改它,通过修改php.ini文件或使用ini_set函数来设置。下面展示使用ini_set来设置屏蔽错误输出的代码。
<?php
ini_set('display_errors', 0);
echo $a;
echo 0;
date();
echo 1;
该程序会正常输出01,但不会输出错误信息。
错误报告级别
一般分为4大类
- parse语法错误
- error致命错误
- warning警告级别错误
- notice注意级别错误
值 | 常量 | 说明 | 备注 |
---|---|---|---|
1 | E_ERROR (integer) | 致命的运行时错误。这类错误一般是不可恢复的情况,例如内存分配导致的问题。后果是导致脚本终止不再继续运行。 | |
2 | E_WARNING (integer) | 运行时警告 (非致命错误)。仅给出提示信息,但是脚本不会终止运行。 | |
4 | E_PARSE (integer) | 编译时语法解析错误。解析错误仅仅由分析器产生。 | |
8 | E_NOTICE (integer) | 运行时通知。表示脚本遇到可能会表现为错误的情况,但是在可以正常运行的脚本里面也可能会有类似的通知。 | |
16 | E_CORE_ERROR (integer) | 在PHP初始化启动过程中发生的致命错误。该错误类似 E_ERROR ,但是是由PHP引擎核心产生的。 | since PHP 4 |
32 | E_CORE_WARNING (integer) | PHP初始化启动过程中发生的警告 (非致命错误) 。类似 E_WARNING ,但是是由PHP引擎核心产生的。 | since PHP 4 |
64 | E_COMPILE_ERROR (integer) | 致命编译时错误。类似E_ERROR , 但是是由Zend脚本引擎产生的。 | since PHP 4 |
128 | E_COMPILE_WARNING (integer) | 编译时警告 (非致命错误)。类似 E_WARNING ,但是是由Zend脚本引擎产生的。 | since PHP 4 |
256 | E_USER_ERROR (integer) | 用户产生的错误信息。类似 E_ERROR , 但是是由用户自己在代码中使用PHP函数 trigger_error()来产生的。 | since PHP 4 |
512 | E_USER_WARNING (integer) | 用户产生的警告信息。类似 E_WARNING , 但是是由用户自己在代码中使用PHP函数 trigger_error()来产生的。 | since PHP 4 |
1024 | E_USER_NOTICE (integer) | 用户产生的通知信息。类似 E_NOTICE , 但是是由用户自己在代码中使用PHP函数 trigger_error()来产生的。 | since PHP 4 |
2048 | E_STRICT (integer) | 启用 PHP 对代码的修改建议,以确保代码具有最佳的互操作性和向前兼容性。 | since PHP 5 |
4096 | E_RECOVERABLE_ERROR (integer) | 可被捕捉的致命错误。 它表示发生了一个可能非常危险的错误,但是还没有导致PHP引擎处于不稳定的状态。 如果该错误没有被用户自定义句柄捕获 (参见 set_error_handler()),将成为一个 E_ERROR 从而脚本会终止运行。 | since PHP 5.2.0 |
8192 | E_DEPRECATED (integer) | 运行时通知。启用后将会对在未来版本中可能无法正常工作的代码给出警告。 | since PHP 5.3.0 |
16384 | E_USER_DEPRECATED (integer) | 用户产生的警告信息。 类似 E_DEPRECATED , 但是是由用户自己在代码中使用PHP函数 trigger_error()来产生的。 | since PHP 5.3.0 |
30719 | E_ALL (integer) | E_STRICT 外的所有错误和警告信息。 | 30719 in PHP 5.3.x, 6143 in PHP 5.2.x, 2047 previously |
在php.ini配置文件中,有选项error_reporting,该选项用来控制输出何种级别的错误。常见的有E_ALL、E_WARNING、E_NOTICE。
可以通过函数error_reporting()来动态控制错误级别的输出。
# 输出所有级别错误
error_reporting(E_ALL);
# 输出所有级别除了E_NOTICE
error_reporting(E_ALL & ~E_NOTICE);
# 输出所有级别除了E_NOTICE和E_WARNING
error_reporting(E_ALL & ~(E_NOTICE | E_WARNING));
display_errors与error_reporting的区别
display_errors是用来控制错误是否输出,而error_reporting是用来控制输出何种级别的错误。通常他们会搭配使用
ini_set('display_errors', 0); // 关闭错误输出
error_reporting(E_ALL); // 输出所有级别的错误信息
上面的配置一般是上线阶段,屏蔽所有的错误输出,但是记录错误到php的错误日志里。错误日志的路径是由选项error_log 决定的。
如果设置error_reporting为0值,则不输出错误也不记录错误日志。
错误对于程序的影响
当程序有语法错误时,程序是不会执行的。当有error级别的错误时,程序就会停止向下执行。notice、及warning级别错误时不会影响程序的向下执行的。
error_reporting(E_ALL); // 输出所有级别的错误信息
echo $a;
echo 0;
date();
echo 1;
new a;
echo 2;
该程序会输出0和1,但不会输出2。
用户自定义错误
trigger_error()函数可以生产一个用户级别的错误。错误级别有E_USER_ERROR 、E_USER_WARNING、E_USER_NOTICE 等
<?php
trigger_error('这是notice级别错误', E_USER_NOTICE);
trigger_error('warning错误', E_USER_WARNING);
trigger_error('deprecated', E_USER_DEPRECATED);
trigger_error('error错误', E_USER_ERROR);
自定义错误处理
set_error_handler — 设置用户自定义的错误处理函数,该函数原型如下:
set_error_handler ( callable $error_handler [, int $error_types = E_ALL | E_STRICT ] ) : mixed
该函数首个参数是一个回调函数,原型如下:
handler ( int $errno , string $errstr [, string $errfile [, int $errline [, array $errcontext ]]] ) : bool
- error 错误级别
- errstr 错误信息
- errfile 发送错误的文件
- errline 错误出现的行号
以下级别的错误不能由用户定义的函数来处理: E_ERROR
、 E_PARSE
、 E_CORE_ERROR
、 E_CORE_WARNING
、 E_COMPILE_ERROR
、 E_COMPILE_WARNING
,和在 调用 set_error_handler() 函数所在文件中产生的大多数 E_STRICT
。
<?php
define('DEBUG', false);
set_error_handler('error_handler');
if (DEBUG) {
ini_set('display_errors', 'On');
} else {
ini_set('display_errors', 'Off');
}
function error_handler($errLevel, $errInfo, $errFile, $errLine)
{
echo "ErrorLevel:$errLevel: $errInfo In $errFile ON $errLine" . PHP_EOL;
}
echo $a;
异常
异常和错误时有区别的,错误一般是指我们能控制的问题,比如变量名写错了,或者判断条件写的不错,导致死循环。而异常通常指那些难以控制的、意料外的错误,比如mysql连接不上,文件句柄打开失败等情况。
php的异常也是经典的try catch finally,但和一般的异常处理不一样的是,绝大部分的异常需要自行抛出。抛出异常使用throw关键字完成。php也支持捕获多个异常。
<?php
class MyError extends Exception
{
public function printErr ()
{
echo '出错啦'.PHP_EOL;
}
}
class YourError extends Exception
{
public function printErr ()
{
echo 'errors'.PHP_EOL;
}
}
try {
if (mt_rand(0,1)) {
throw new MyError('错误');
} else {
throw new YourError('错误');
}
} catch (MyError $e) {
$e->printErr();
} catch (YourError $e) {
$e->printErr();
} finally {
echo '不管有没有异常,我都会被执行'.PHP_EOL;
}
php的异常如果没有捕获,则会报Fatal Error错误,程序不会继续向下执行。
PHP 7 错误处理
PHP 7 改变了大多数错误的报告方式。不同于传统(PHP 5)的错误报告机制,现在大多数错误被作为 Error 异常抛出。 Error 和 Exception 都实现了 Throwable 接口
异常处理
设置默认的异常处理程序,有try/catch捕获的话这个异常函数就不会执行,反之就会执行异常处理函数,而且执行的话,脚本将不会继续执行。
php使用set_exception_handler来设置用户自定义的异常处理函数 ,函数原型如下:
set_exception_handler ( callable $exception_handler ) : callable
回调函数的原型如下:
handler ( Throwable $ex ) : void
下面,我们来写一个异常处理函数
set_exception_handler('exceptionHandler');
function exceptionHandler ($err)
{
$error = [
'message' => $err->getMessage(),
'line' => $err->getLine(),
'file' => $err->getFile()
];
$log = "ERR:message:{$error['message']} on file:{$error['file']}({$error['line']})" . PHP_EOL;
echo $log;
}
统一处理错误与异常
学完了错误与异常,知道如何去使用自定义错误处理和异常处理,现在我们就可以统一处理错误与异常了。
<?php
class Errors
{
// 处理非致命错误
static function errorHandle($errLevel, $errInfo, $errFile, $errLine)
{
echo '错误:'.PHP_EOL;
print_r(['file' => $errFile, 'level' => $errLevel, 'line' => $errLine, 'info' => $errInfo]);
}
// 处理致命错误及异常
static function exceptionHandle(Throwable $ex)
{
echo '异常:'.PHP_EOL;
print_r(['file' => $ex->getFile(), 'level' => $ex->getCode(), 'line' => $ex->getLine(), 'info' => $ex->getMessage()]);
}
}
set_error_handler(['Errors', 'errorHandle']);
set_exception_handler(['Errors', 'exceptionHandle']);