错误处理
CodeIgniter 通过异常机制为你的系统构建错误报告功能,包括 SPL 集合 以及框架提供的一些异常。
根据你的环境设置,
当抛出错误或异常时,默认操作是显示详细的错误报告,除非应用程序运行在 production 环境中。在 production 环境中,会显示更通用的消息,以保持用户的最佳体验。
使用异常
本节为新程序员或对使用异常没有经验的开发者提供一个快速概览。
什么是异常
异常只是在异常被”抛出”时发生的事件。这会停止脚本的当前流程, 然后将执行权发送给错误处理器,错误处理器会显示相应的错误页面:
<?php
throw new \Exception('Some message goes here');
捕获异常
如果你调用的方法可能会抛出异常,可以使用 try/catch 来捕获该异常:
<?php
try {
$user = $userModel->find($id);
} catch (\Exception $e) {
exit($e->getMessage());
}
如果 $userModel 抛出异常,它会被捕获,catch 块中的代码会被执行。在这个例子中,
脚本会终止,并输出 UserModel 定义的错误消息。
捕获特定异常
在上面的例子中,我们捕获任何类型的异常。如果我们只想监控特定类型的异常,
比如 DataException,可以在 catch 参数中指定。任何其他抛出的异常
如果不是被捕获异常的子类,将被传递给错误处理器:
<?php
use CodeIgniter\Database\Exceptions\DataException;
try {
$user = $userModel->find($id);
} catch (DataException $e) {
// do something here...
}
这对于自己处理错误或在脚本结束前执行清理操作很有用。如果你希望 错误处理器正常工作,可以在 catch 块中抛出新异常:
<?php
use CodeIgniter\Database\Exceptions\DataException;
try {
$user = $userModel->find($id);
} catch (DataException $e) {
// do something here...
throw new \RuntimeException($e->getMessage(), $e->getCode(), $e);
}
配置
错误报告
当 PHP ini 设置中的 display_errors 启用时,CodeIgniter 将显示
包含所有错误的详细错误报告
因此默认情况下,CodeIgniter 会在 development 和 testing 环境中
显示详细的错误报告,在 production 环境中不显示任何错误。
你可以通过设置 CI_ENVIRONMENT 变量来更改你的环境。
参见 设置环境。
重要
禁用错误报告并不会在有错误时停止日志的写入。
警告
请注意,.env 文件中的设置会被添加到 $_SERVER
和 $_ENV 中。作为副作用,这意味着如果显示了详细的错误报告,
你的安全凭据将被公开暴露。
异常日志记录
默认情况下,除了 “404 - Page Not Found” 异常之外的所有异常都会被记录。这可以通过
设置 app/Config/Exceptions.php 中的 $log 值来开启或关闭:
<?php
namespace Config;
use CodeIgniter\Config\BaseConfig;
class Exceptions extends BaseConfig
{
// ...
public bool $log = true;
// ...
}
要忽略对其他状态码的日志记录,可以在同一文件中设置要忽略的状态码:
<?php
namespace Config;
use CodeIgniter\Config\BaseConfig;
class Exceptions extends BaseConfig
{
// ...
public array $ignoreCodes = [404];
// ...
}
备注
如果你当前的 日志设置
没有设置为记录 critical 错误(所有异常都被记录为此级别),
异常的日志记录可能仍然不会发生。
记录弃用警告
在 4.3.0 版本加入.
在 v4.3.0 之前,所有由 error_reporting() 报告的错误都会被抛出为
ErrorException 对象。
但随着 PHP 8.1+ 的普及,许多用户可能会看到为 向非空内部函数参数传递 null 抛出的异常。
为了简化向 PHP 8.1 的迁移,从 v4.3.0 开始,CodeIgniter 提供了
只记录弃用错误(E_DEPRECATED 和 E_USER_DEPRECATED)
而不将其作为异常抛出的功能。
默认情况下,CodeIgniter 只在开发环境中记录弃用警告而不抛出异常。 在生产环境中,不会进行日志记录,也不会抛出异常。
配置
此功能的设置如下。
首先,确保你的 Config\Exceptions 副本已更新为包含两个新属性,
并按以下方式设置:
<?php
namespace Config;
use CodeIgniter\Config\BaseConfig;
use Psr\Log\LogLevel;
class Exceptions extends BaseConfig
{
// ...
public bool $logDeprecations = true; // If set to false, an exception will be thrown.
// ...
public string $deprecationLogLevel = LogLevel::WARNING; // This should be one of the log levels supported by PSR-3.
// ...
}
接下来,根据你在 Config\Exceptions::$deprecationLogLevel 中设置的日志级别,
检查 Config\Logger::$threshold 中定义的日志记录器阈值是否覆盖了弃用日志级别。
如果没有,相应地调整它。
<?php
namespace Config;
use CodeIgniter\Config\BaseConfig;
class Logger extends BaseConfig
{
// ...
// This must contain the log level (5 for LogLevel::WARNING) corresponding to $deprecationLogLevel.
public $threshold = (ENVIRONMENT === 'production') ? 4 : 9;
// ...
}
之后,后续的弃用警告将按配置记录而不会作为异常抛出。
此功能也适用于用户弃用:
<?php
@trigger_error('Do not use this class!', E_USER_DEPRECATED);
// Your logs should contain a record with a message like: "[DEPRECATED] Do not use this class!"
为了测试应用程序,你可能希望总是在弃用时抛出异常。可以通过
将环境变量 CODEIGNITER_SCREAM_DEPRECATIONS 设置为真值来配置此功能。
框架异常
异常设计
从 v4.6.0 开始,框架抛出的所有异常类:
实现
CodeIgniter\Exceptions\ExceptionInterface继承
CodeIgniter\Exceptions\LogicException或CodeIgniter\Exceptions\RuntimeException
备注
框架只抛出上述类型的异常类,但 PHP 或使用的其他库可能会抛出其他异常。
框架抛出的异常有两个基类:
LogicException
CodeIgniter\Exceptions\LogicException 继承 \LogicException。
此异常表示程序逻辑中的错误。这类异常应该直接导致修复你的代码。
RuntimeException
CodeIgniter\Exceptions\RuntimeException 继承 \RuntimeException。
如果发生只能在运行时发现的错误,会抛出此异常。
框架还提供以下异常:
PageNotFoundException
用于表示 404 Page Not Found 错误:
<?php
use CodeIgniter\Exceptions\PageNotFoundException;
$page = $pageModel->find($id);
if ($page === null) {
throw PageNotFoundException::forPageNotFound();
}
你可以向异常传递消息,该消息将显示在 404 页面上替换默认消息。
有关默认 404 视图文件位置,请参见 HTTP 状态码和错误视图。
如果在 app/Config/Routing.php 或 app/Config/Routes.php 中指定了 404 重写,则会调用它而不是标准的 404 页面。
ConfigException
当配置类的值无效,或配置类不是正确的类型等情况时,应使用此异常:
<?php
throw new \CodeIgniter\Exceptions\ConfigException();
该异常提供退出代码 3。
DatabaseException
为数据库错误抛出此异常,例如无法创建数据库连接, 或连接暂时丢失时:
<?php
throw new \CodeIgniter\Database\Exceptions\DatabaseException();
该异常提供退出代码 8。
RedirectException
备注
从 v4.4.0 开始,RedirectException 的命名空间已更改。
之前它是 CodeIgniter\Router\Exceptions\RedirectException。
之前的类在 v4.6.0 中已被移除。
此异常是一个特殊情况,允许覆盖所有其他响应路由并强制重定向到特定 URI:
<?php
throw new \CodeIgniter\HTTP\Exceptions\RedirectException($uri);
$uri 是相对于 baseURL 的 URI 路径。你也可以提供
重定向代码来代替默认值(302,”临时重定向”):
<?php
throw new \CodeIgniter\HTTP\Exceptions\RedirectException($uri, 301);
另外,从 v4.4.0 开始,实现 ResponseInterface 的类的对象可以用作第一个参数。 此解决方案适用于需要在响应中添加额外头部或 Cookie 的情况。
<?php
$response = service('response')
->redirect('https://example.com/path')
->setHeader('Some', 'header')
->setCookie('and', 'cookie');
throw new \CodeIgniter\HTTP\Exceptions\RedirectException($response);
在异常中指定 HTTP 状态码
在 4.3.0 版本加入.
从 v4.3.0 开始,你可以为你的异常类指定 HTTP 状态码,以实现
CodeIgniter\Exceptions\HTTPExceptionInterface。
当实现 HTTPExceptionInterface 的异常被 CodeIgniter 的异常处理器捕获时,
异常代码将成为 HTTP 状态码。
HTTP 状态码和错误视图
异常处理器显示与 HTTP 状态码对应的错误视图(如果存在)。
例如,PageNotFoundException 实现了 HTTPExceptionInterface,
所以其异常代码 404 将是 HTTP 状态码。因此如果它被抛出,
当是 Web 请求时,系统将显示 app/Views/errors/html 文件夹中的
error_404.php。如果是通过 CLI 调用,系统将显示
app/Views/errors/cli 文件夹中的 error_404.php。
如果没有与 HTTP 状态码对应的视图文件,将显示 production.php 或 error_exception.php。
备注
如果 PHP ini 设置中的 display_errors 开启,
会选择 error_exception.php 并显示详细的错误报告。
你应该为你的网站自定义 app/Views/errors/html 文件夹中的所有错误视图。
你也可以为特定的 HTTP 状态码创建错误视图。例如,如果你想为 “400 Bad Request” 创建错误视图,添加 error_400.php。
警告
如果存在具有相应 HTTP 状态码的错误视图文件, 异常处理器将显示该文件,而不管环境如何。 视图文件必须以在生产环境中不显示详细错误消息的方式自行实现。
在异常中指定退出代码
在 4.3.0 版本加入.
从 v4.3.0 开始,你可以为你的异常类指定退出代码,以实现
CodeIgniter\Exceptions\HasExitCodeInterface。
当实现 HasExitCodeInterface 的异常被 CodeIgniter 的异常处理器捕获时,
从 getExitCode() 方法返回的代码将成为退出代码。
自定义异常处理器
在 4.4.0 版本加入.
如果你需要更多控制异常的显示方式,现在可以定义自己的处理器并 指定它们何时应用。
定义新处理器
第一步是创建一个实现 CodeIgniter\Debug\ExceptionHandlerInterface 的新类。
你也可以继承 CodeIgniter\Debug\BaseExceptionHandler。
这个类包含许多默认异常处理器使用的实用方法。
新处理器必须实现一个方法:handle():
<?php
namespace App\Libraries;
use CodeIgniter\Debug\BaseExceptionHandler;
use CodeIgniter\Debug\ExceptionHandlerInterface;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
use Throwable;
class MyExceptionHandler extends BaseExceptionHandler implements ExceptionHandlerInterface
{
// You can override the view path.
protected ?string $viewPath = APPPATH . 'Views/exception/';
public function handle(
Throwable $exception,
RequestInterface $request,
ResponseInterface $response,
int $statusCode,
int $exitCode,
): void {
$this->render($exception, $statusCode, $this->viewPath . "error_{$statusCode}.php");
exit($exitCode);
}
}
这个例子定义了通常需要的最小代码量 - 显示视图并以适当的
退出代码退出。但是,BaseExceptionHandler 提供了许多其他辅助函数和对象。
配置新处理器
告诉 CodeIgniter 使用你的新异常处理器类是在 app/Config/Exceptions.php
配置文件的 handler() 方法中完成的:
<?php
namespace Config;
use CodeIgniter\Config\BaseConfig;
use CodeIgniter\Debug\ExceptionHandler;
use CodeIgniter\Debug\ExceptionHandlerInterface;
use Throwable;
class Exceptions extends BaseConfig
{
// ...
public function handler(int $statusCode, Throwable $exception): ExceptionHandlerInterface
{
return new ExceptionHandler($this);
}
}
你可以使用应用程序需要的任何逻辑来确定是否应该处理异常, 但最常见的是检查 HTTP 状态码或异常类型。如果你的类应该处理它, 则返回该类的新实例:
<?php
namespace Config;
use CodeIgniter\Config\BaseConfig;
use CodeIgniter\Debug\ExceptionHandlerInterface;
use CodeIgniter\Exceptions\PageNotFoundException;
use Throwable;
class Exceptions extends BaseConfig
{
// ...
public function handler(int $statusCode, Throwable $exception): ExceptionHandlerInterface
{
if (in_array($statusCode, [400, 404, 500], true)) {
return new \App\Libraries\MyExceptionHandler($this);
}
if ($exception instanceof PageNotFoundException) {
return new \App\Libraries\MyExceptionHandler($this);
}
return new \CodeIgniter\Debug\ExceptionHandler($this);
}
}