错误与异常
错误 和异常 在PHP 中不一样的, 它们都表明代码出现问题, 且都能提供错误信息.
Points:
错误 出现的时机比异常早错误 可以委托给全局错误处理器处理, 有些错误是无法恢复的, 会导致脚本停止异常 要先实例化(Exception类), 然后抛出, 可以被捕获(try...catch)异常 捕获后可以就地处理, 无需停止脚本(任何未被捕获的异常都会导致脚本停止)
异常的使用:
主动出击: 在遇到无法修复的状况时(当前上下文不知道如何处理)主动抛出, 交由使用者处理
eg. 数据库连接超时, 传入参数类型不符合条件等.
eg. 组件和框架的作者尤其无法确定如何处理异常状况, 通常会抛出异常, 交由具体使用者去处理.
被动防守: 预测潜在的问题, 减轻影响(将可能抛出异常的代码放在 try/catch 块中)
PHP 7 注意 :
PHP 7中, 大多数错误被作为 Error异常 抛出, 能够被捕获.
若未被捕获且未注册异常处理函数(通过 set_exception_handler() 注册), 则会按照传统方式处理(指PHP7之前版本): 被报告为一个致命错误(Fatal Error), 可被 set_error_handler() 处理.
异常类
PHP 内置异常类:
SPL 扩充的异常类(均继承自 Exception 类):
错误类(PHP >= 7)
Throwable
Error
ArithmeticError
AssertionError ParseError TypeError Exception
注意: PHP 7 中, Error 和 Exception 都继承自 Throwable, 因此在捕获(try...catch)时可通过捕获 Throwable 来同时捕获异常和错误
try {
} catch (\Throwable $e) {
}
错误
php 能触发不同类型的错误:
致命错误 运行时错误 编译时错误 启动错误 用户触发错误(少见)
错误报告级别
error_reporting(int $level);
PHP 5.3 及以上, 默认的错误报告级别是 E_ALL & ~E_NOTICE & ~E_STRICT & ~E_DEPRECATED
(不会显示 E_NOTICE 、 E_STRICT 、E_DEPRECATED )
PHP Manual 所有的错误级别
部分错误级别解释:
E_NOTICE 运行时通知。表示脚本遇到可能会表现为错误的情况,但是在可以正常运行的脚本里面也可能会有类似的通知。 开发期间使用, 会对代码中可能出现的bug进行警告, 节省调试时间 E_STRICT 启用 PHP 对代码的修改建议,以确保代码具有最佳的互操作性和向前兼容性。 E_ALL 不包含 E_STRICT, 因此默认不激活 开发期间使用 E_DEPRECATED 运行时通知。启用后将会对在未来版本中可能无法正常工作的代码给出警告。 开启
错误报告设置
错误报告遵循原则
要报告 错误 开发环境要显示 错误 生产环境要不能显示 错误(安全考虑) 开发环境和生产环境都要记录错误
注意分清 报告错误 和 显示错误 这两个概念的区别.
php.ini
开发环境 推荐错误报告方式
display_startup_errors = On
display_errors = On
error_reporting = -1
log_errors = On
生产环境 推荐错误报告方式
display_startup_errors = Off
display_errors = Off
error_reporting = E_ALL & ~E_NOTICE
log_errors = On
部分参数解释
display_errors 设置是否将错误信息作为输出的一部分显示到屏幕,或者对用户隐藏而不显示 开发环境打开 生产环境务必关闭 display_startup_errors 即使 display_errors 设置为开启, PHP 启动过程中的错误信息也不会被显示。强烈建议除了调试目的以外,将 display_startup_errors 设置为关闭。 开发环境打开 生产环境务必关闭 log_errors 设置是否将脚本运行的错误信息记录到服务器错误日志或者error_log 之中 打开
范例代码
<?php
error_reporting(0 );
error_reporting(E_ERROR | E_WARNING | E_PARSE);
error_reporting(E_ERROR | E_WARNING | E_PARSE | E_NOTICE);
error_reporting(E_ALL ^ E_NOTICE);
error_reporting(E_ALL);
error_reporting(-1 );
ini_set('error_reporting' , E_ALL);
?>
全局异常处理程序
捕获所有未被捕获的异常: 通过 set_exception_handler
注册全局异常处理程序.
函数
set_exception_handler ( callable $exception_handler ) : callable
set_exception_handler('handleException' );
handleException(Exception $ex)
{
echo "Uncaught exception: " , $ex->getMessage(), "\n" ;
}
handleException(Throwable $ex)
{
}
在用户自定义异常处理函数内部, 可根据情况做一下处理:
日志记录错误 web 渲染错误页面 console 渲染错误提示
全局错误处理函数
通过设置全局错误处理程序, 使用自己的自定义方式拦截并处理PHP错误, 包括但不限于:
记录详细错误日志 对数据/文件做清理回收 转换成 ErrorException 对象, 再由处理异常的流程来处理错误.
注册全局错误处理程序
set_error_handler( callable $error_handler [, int $error_types = E_ALL | E_STRICT ] ) : mixed
$error_types
指定的错误类型会被该错误处理函数拦截 ( 除非该函数返回了 false
),不受 error_report()
影响.
处理程序
function error_handler (int $errno, string $errstr, string $errfile, int $errline, array $errcontext) {
}
带 @ 前缀的语句发生错误时, $errno 值为 0
无法捕获的错误类型
以下级别的错误不能由用户定义的错误处理函数来捕获:
E_ERROR 致命的运行错误, 一般不可恢复(eg. 内存分配导致的问题) E_PARSE 编译时语法解析错误, 由分析器产生 E_CORE_ERROR PHP初始化启动过程中发生的致命错误, 由php引擎核心产生 E_CORE_WARNING PHP初始化启动过程中发生的警告(非致命错误), 由php引擎核心产生 E_COMPILE_ERROR 致命编译时错误。类似E_ERROR , 但是是由Zend脚本引擎产生的。 E_COMPILE_WARNING 编译时警告 (非致命错误)。类似 E_WARNING ,但是是由Zend脚本引擎产生的。
以及在 调用 set_error_handler() 函数所在文件中产生的大多数 E_STRICT 。
这些无法捕获的错误, 可在 register_shutdown_function()
中处理( 但脚本仍会结束 )
范例代码
function handleError ($errno, $errstr, $errfile = '' , $errline = 0 )
{
if (!(error_reporting() & $errno)) {
return false ;
}
throw new ErrorException($errstr, 0 , $errno, $errfile, $errline);
}
将 错误 转换为 异常
开发/生产环境处理错误和异常
开发环境
php 默认的错误信息很糟糕, 为了更高的帮助调试程序, 可以使用 filp/whoops 扩展包.
安装
composer require filp/whoops
使用 (web)
$whoops = new \Whoops\Run;
$whoops->pushHandler(new \Whoops\Handler\PrettyPageHandler);
$whoops->register();
如果php脚本触发PHP错误, 或应用没有捕获异常, 则开发人员就可以看到 Whoops 的图形化诊断页面.
可用的处理器
生产环境
记录错误信息通常使用 error_log()
函数以将错误信息记录到文件系统或syslog.
error_log ( string $message [, int $message_type = 0 [, string $destination [, string $extra_headers ]]] ) : bool
message_type 参数, 设置错误应该发送到何处。可能的信息类型有以下几个:
1 message
发送到参数 destination
设置的邮件地址。 第四个参数 extra_headers
只有在这个类型里才会被用到。2 不再是一个选项。 3 message
被发送到位置为 destination
的文件里。 字符 message
不会默认被当做新的一行。4 message
直接发送到 SAPI 的日志处理程序中。
一个更好的选择是使用 monolog/monolog 扩展包
<?php
require "vendor/autoload.php" ;
use Monolog \Logger ;
use Monolog \Handler \StreamHandler ;
$log = new Logger('name' );
$log->pushHandler(new StreamHandler('path/to/your.log' , Logger::WARNING));
$log->warning('Foo' );
$log->error('Bar' );
示例: 在生产环境中使用 Monolog 记录日志, 严重错误使用邮件通知
依赖: swiftmailer/swiftmailer
require "vendor/autoload.php" ;
use Monolog \Logger ;
use Monolog \Handler \StreamHandler ;
use Monolog \Handler \SwiftMailerHandler ;
date_default_timezone_set('Asia/Shanghai' );
$logger = new Logger('my-app-name' );
$logger->pushHandler(new StreamHandler('path/to/your.log' , Logger::WARNING));
$transport = (new Swift_SmtpTransport('smtp.example.org' , 25 ))
->setUsername('your username' )
->setPassword('your password' );
$mailer = new Swift_Mailer($transport);
$message = (new Swift_Message('Wonderful Subject' ))
->setFrom(['john@doe.com' => 'John Doe' ])
->setTo(['receiver@domain.org' , 'other@domain.org' => 'A name' ]);
$logger->pushHandler(new SwiftMailerHandler($mailer, $message, Logger::CRITICAL));
$logger->critical('The server is on fire!' );
php中止时的回调函数
register_shutdown_function
register_shutdown_function(function () {
}, $para1, $param2, ...)
注册一个 callback
,它会在脚本执行完成或者 exit() 后被调用。
Note:
可注册多个回调函数(不会互相覆盖, 依照注册顺序依次调用), 在php脚本中止时会被调用到.
如果在注册的方法内部调用 exit() , 那么所有处理会被中止,并且其他注册的中止回调也不会再被调用。
由于部分错误无法被 set_error_handler
捕获, 因此需配合 register_shutdown_function
, 判断脚本退出的原因, 若是因为未被捕获的致命错误, 则需要处理(日志记录等)
register_shutdown_function('handleShutdown' );
function handleShutdown ()
{
if (! is_null($error = error_get_last()) && isFatal($error['type' ])) {
handleException(new \ErrorException(
$error['message' ], $error['type' ], 0 , $error['file' ], $error['line' ],
));
}
}
function isFatal ($type)
{
return in_array($type, [E_COMPILE_ERROR, E_CORE_ERROR, E_ERROR, E_PARSE]);
}
Note:
进程被信号 SIGTERM 或 SIGKILL 杀死时中止函数不会被调用. 可通过 pcntl_signal
捕获信号, 再在其中调用 exit()
来进行正常中止.
文章转载自 https://www.cnblogs.com/youjiaxing/p/10310658.html