CakePHP 应用程序为您设置了错误和异常处理。PHP 错误会被捕获并显示或记录。未捕获的异常会自动渲染成错误页面。
错误配置是在应用程序的 config/app.php 文件中完成的。默认情况下,CakePHP 使用 Cake\Error\ErrorTrap
和 Cake\Error\ExceptionTrap
分别处理 PHP 错误和异常。错误配置允许您为应用程序自定义错误处理。支持以下选项
errorLevel
- int - 您感兴趣的捕获错误级别。使用内置的 PHP 错误常量和位掩码来选择您感兴趣的错误级别。请参阅 弃用警告 以禁用弃用警告。
trace
- bool - 在日志文件中包含错误的堆栈跟踪。堆栈跟踪将在每次错误之后包含在日志中。这有助于查找错误发生的位置和时间。
exceptionRenderer
- string - 用于渲染未捕获异常的类。如果您选择自定义类,则应将该类的文件放置在 src/Error 中。此类需要实现一个 render()
方法。
log
- bool - 当 true
时,异常及其堆栈跟踪将被记录到 Cake\Log\Log
。
skipLog
- array - 不应记录的异常类名的数组。这对于删除 NotFoundExceptions 或其他常见但无趣的日志消息很有用。
extraFatalErrorMemory
- int - 设置为遇到致命错误时增加的内存限制的兆字节数。这允许在完成日志记录或错误处理时提供空间。
logger
(在 4.4.0 之前使用 errorLogger
) - Cake\Error\ErrorLoggerInterface
- 负责记录错误和未处理异常的类。默认设置为 Cake\Error\ErrorLogger
。
errorRenderer
- Cake\Error\ErrorRendererInterface
- 负责渲染错误的类。默认值根据 PHP SAPI 选择。
ignoredDeprecationPaths
- array - 一个 glob 兼容路径列表,这些路径上的弃用错误应该被忽略。在 4.2.0 中添加。
默认情况下,当 debug
为 true
时,PHP 错误会被显示,而当 debug 为 false
时,PHP 错误会被记录。致命错误处理程序将独立于 debug
级别或 errorLevel
配置被调用,但结果将根据 debug
级别而不同。致命错误的默认行为是显示一个内部服务器错误页面 (debug
禁用) 或一个包含消息、文件和行号的页面 (debug
启用)。
注意
如果您使用自定义错误处理程序,支持的选项将取决于您的处理程序。
CakePHP 使用弃用警告来指示功能何时被弃用。我们还建议在您的插件和应用程序代码中有用时使用此系统。您可以使用 deprecationWarning()
触发弃用警告。
deprecationWarning('5.0', 'The example() method is deprecated. Use getExample() instead.');
升级 CakePHP 或插件时,您可能会遇到新的弃用警告。您可以通过以下几种方法暂时禁用弃用警告。
将 Error.errorLevel
设置设置为 E_ALL ^ E_USER_DEPRECATED
以忽略所有弃用警告。
使用 Error.ignoredDeprecationPaths
配置选项来忽略具有 glob 兼容表达式的弃用。例如:
'Error' => [
'ignoredDeprecationPaths' => [
'vendors/company/contacts/*',
'src/Models/*',
],
],
将忽略来自应用程序 Models
目录和 Contacts
插件的所有弃用。
CakePHP 中的异常处理提供了多种方法来定制异常的处理方式。每种方法都为您提供了不同程度的异常处理过程控制权。
监听事件 这使您能够通过 CakePHP 事件在处理错误和异常时收到通知。
自定义模板 这使您可以更改渲染的视图模板,就像您在应用程序中更改任何其他模板一样。
自定义控制器 这使您可以控制异常页面的渲染方式。
自定义异常渲染器 这使您可以控制异常页面和日志记录的执行方式。
创建和注册您自己的陷阱 这使您能够完全控制错误和异常的处理、记录和渲染方式。在实现您的陷阱时,请参考 Cake\Error\ExceptionTrap
和 Cake\Error\ErrorTrap
。
ErrorTrap
和 ExceptionTrap
处理程序将在它们处理错误时触发 CakePHP 事件。您可以监听 Error.beforeRender
事件以收到 PHP 错误的通知。当处理异常时,会分派 Exception.beforeRender
事件。
$errorTrap = new ErrorTrap(Configure::read('Error'));
$errorTrap->getEventManager()->on(
'Error.beforeRender',
function (EventInterface $event, PhpError $error) {
// do your thing
}
);
在 Error.beforeRender
处理程序中,您有几个选项:
停止事件以阻止渲染。
返回一个字符串以跳过渲染并使用提供的字符串代替。
在 Exception.beforeRender
处理程序中,您有几个选项:
停止事件以阻止渲染。
使用 setData('exception', $err)
设置 exception
数据属性以替换正在渲染的异常。
从事件监听器返回一个响应以跳过渲染并使用提供的响应代替。
默认异常陷阱使用 Cake\Error\Renderer\WebExceptionRenderer
和您的应用程序 ErrorController
来呈现应用程序引发的所有未捕获异常。
错误页面视图位于 **templates/Error/**。所有 4xx 错误使用 **error400.php** 模板,而 5xx 错误使用 **error500.php**。您的错误模板将可以使用以下变量
message
异常消息。
code
异常代码。
url
请求 URL。
error
异常对象。
在调试模式下,如果您的错误扩展了 Cake\Core\Exception\CakeException
,则由 getAttributes()
返回的数据也将作为视图变量公开。
注意
您需要将 debug
设置为 false,才能看到您的 **error404** 和 **error500** 模板。在调试模式下,您将看到 CakePHP 的开发错误页面。
默认情况下,错误模板使用 **templates/layout/error.php** 作为布局。您可以使用 layout
属性选择不同的布局
// inside templates/Error/error400.php
$this->layout = 'my_error';
以上将使用 **templates/layout/my_error.php** 作为错误页面的布局。
CakePHP 引发的许多异常将在调试模式下呈现特定的视图模板。在调试关闭的情况下,CakePHP 引发的所有异常将根据其状态代码使用 **error400.php** 或 **error500.php**。
App\Controller\ErrorController
类由 CakePHP 的异常呈现使用,用于呈现错误页面视图并接收所有标准请求生命周期事件。通过修改此类,您可以控制使用哪些组件以及呈现哪些模板。
如果您的应用程序使用 前缀路由,您可以为每个路由前缀创建自定义错误控制器。例如,如果您有一个 Admin
前缀。您可以创建以下类
namespace App\Controller\Admin;
use App\Controller\AppController;
use Cake\Event\EventInterface;
class ErrorController extends AppController
{
/**
* beforeRender callback.
*
* @param \Cake\Event\EventInterface $event Event.
* @return void
*/
public function beforeRender(EventInterface $event)
{
$this->viewBuilder()->setTemplatePath('Error');
}
}
此控制器仅在带前缀的控制器中遇到错误时使用,并允许您根据需要定义特定于前缀的逻辑/模板。
如果要控制整个异常呈现和日志记录过程,可以使用 **config/app.php** 中的 Error.exceptionRenderer
选项来选择将呈现异常页面的类。更改异常呈现器在您想要更改用于创建错误控制器的逻辑、选择模板或控制总体呈现过程时很有用。
您的自定义异常呈现器类应放置在 **src/Error** 中。假设我们的应用程序使用 App\Exception\MissingWidgetException
来指示缺少小部件。我们可以创建一个异常呈现器,在处理此错误时呈现特定的错误页面
// In src/Error/AppExceptionRenderer.php
namespace App\Error;
use Cake\Error\Renderer\WebExceptionRenderer;
class AppExceptionRenderer extends WebExceptionRenderer
{
public function missingWidget($error)
{
$response = $this->controller->getResponse();
return $response->withStringBody('Oops that widget is missing.');
}
}
// In config/app.php
'Error' => [
'exceptionRenderer' => 'App\Error\AppExceptionRenderer',
// ...
],
// ...
以上将处理我们的 MissingWidgetException
,并允许我们为这些应用程序异常提供自定义显示/处理逻辑。
异常呈现方法将处理的异常作为参数接收,并应返回一个 Response
对象。您还可以实现方法,以便在处理 CakePHP 错误时添加其他逻辑
// In src/Error/AppExceptionRenderer.php
namespace App\Error;
use Cake\Error\Renderer\WebExceptionRenderer;
class AppExceptionRenderer extends WebExceptionRenderer
{
public function notFound($error)
{
// Do something with NotFoundException objects.
}
}
异常呈现器决定使用哪个控制器进行异常呈现。如果要更改用于呈现异常的控制器,请在您的异常呈现器中覆盖 _getController()
方法
// in src/Error/AppExceptionRenderer
namespace App\Error;
use App\Controller\SuperCustomErrorController;
use Cake\Controller\Controller;
use Cake\Error\Renderer\WebExceptionRenderer;
class AppExceptionRenderer extends WebExceptionRenderer
{
protected function _getController(): Controller
{
return new SuperCustomErrorController();
}
}
// in config/app.php
'Error' => [
'exceptionRenderer' => 'App\Error\AppExceptionRenderer',
// ...
],
// ...
您可以使用任何内置的 SPL 异常、Exception
本身或 Cake\Core\Exception\Exception
来创建您自己的应用程序异常。如果您的应用程序包含以下异常
use Cake\Core\Exception\CakeException;
class MissingWidgetException extends CakeException
{
}
您可以通过创建 **templates/Error/missing_widget.php** 来提供良好的开发错误。在生产模式下,上面的错误将被视为 500 错误并使用 **error500** 模板。
扩展 Cake\Http\Exception\HttpException
的异常,如果错误代码介于 400
和 506
之间,它们的错误代码将用作 HTTP 状态代码。
Cake\Core\Exception\CakeException
的构造函数允许您传入额外的數據。这些附加数据会插入到 _messageTemplate
中。这允许您创建数据丰富的异常,这些异常提供更多关于错误的上下文
use Cake\Core\Exception\CakeException;
class MissingWidgetException extends Exception
{
// Context data is interpolated into this format string.
protected $_messageTemplate = 'Seems that %s is missing.';
// You can set a default exception code as well.
protected $_defaultCode = 404;
}
throw new MissingWidgetException(['widget' => 'Pointy']);
呈现时,您的视图模板将设置一个 $widget
变量。如果将异常转换为字符串或使用其 getMessage()
方法,您将获得 Seems that Pointy is missing.
。
注意
在 CakePHP 4.2.0 之前,请使用类 Cake\Core\Exception\Exception
而不是 Cake\Core\Exception\CakeException
使用内置的异常处理,您可以通过在 **config/app.php** 中将 log
选项设置为 true
来记录 ErrorTrap 处理的所有异常。启用此功能会将每个异常记录到 Cake\Log\Log
和配置的记录器中。
注意
如果您使用的是自定义异常处理程序,则此设置将无效。除非您在实现中引用它。
CakePHP 中有几个内置异常,除了内部框架异常之外,还有几个用于 HTTP 方法的异常
用于执行 400 错误请求错误。
用于执行 401 未授权错误。
用于执行 403 禁止错误。
用于执行由无效 CSRF 令牌引起的 403 错误。
用于执行 404 未找到错误。
用于执行 405 方法不允许错误。
用于执行 406 不可接受错误。
用于执行 409 冲突错误。
用于执行 410 已消失错误。
有关 HTTP 4xx 错误状态代码的更多详细信息,请参阅 RFC 2616 第 10.4 节。
用于执行 500 内部服务器错误。
用于执行 501 未实现错误。
用于执行 503 服务不可用错误。
有关 HTTP 5xx 错误状态代码的更多详细信息,请参阅 RFC 2616 第 10.5 节。
您可以从控制器中抛出这些异常来指示失败状态或 HTTP 错误。 HTTP 异常的一个示例用法可能是为未找到的项目渲染 404 页面。
use Cake\Http\Exception\NotFoundException;
public function view($id = null)
{
$article = $this->Articles->findById($id)->first();
if (empty($article)) {
throw new NotFoundException(__('Article not found'));
}
$this->set('article', $article);
$this->viewBuilder()->setOption('serialize', ['article']);
}
通过使用异常来处理 HTTP 错误,您可以保持代码清洁,并为客户端应用程序和用户提供 RESTful 响应。
您可以从控制器操作中抛出任何与 HTTP 相关的异常来指示失败状态。 例如
use Cake\Network\Exception\NotFoundException;
public function view($id = null)
{
$article = $this->Articles->findById($id)->first();
if (empty($article)) {
throw new NotFoundException(__('Article not found'));
}
$this->set('article', 'article');
$this->viewBuilder()->setOption('serialize', ['article']);
}
上面的操作将导致配置的异常处理程序捕获并处理 NotFoundException
。 默认情况下,这将创建一个错误页面并记录异常。
此外,CakePHP 使用以下异常
找不到选择的视图类。
找不到选择的模板文件。
找不到选择的布局。
找不到选择的辅助程序。
找不到选择的元素文件。
找不到选择的单元格类。
找不到选择的单元格视图文件。
找不到配置的组件。
找不到请求的控制器操作。
访问私有/受保护的/_ 前缀操作。
控制台库类遇到错误。
模型的连接丢失。
找不到数据库驱动程序。
数据库驱动程序缺少 PHP 扩展。
找不到模型的表格。
找不到模型的实体。
找不到模型的行为。
在使用
Cake\ORM\Table::saveOrFail()
或Cake\ORM\Table::deleteOrFail()
时,实体无法保存/删除。
找不到请求的记录。 这也会将 HTTP 响应头设置为 404。
找不到请求的控制器。
请求的 URL 无法反向路由或无法解析。
CakePHP 中的基类异常。 CakePHP 抛出的所有框架层异常都将扩展此类。
这些异常类都扩展了 Exception
。 通过扩展 Exception,您可以创建自己的“框架”错误。
参见
Cake\Network\Request::header()
所有 Http 和 Cake 异常都扩展了 Exception 类,该类有一个方法可以将头添加到响应中。 例如,当抛出 405 MethodNotAllowedException 时,rfc2616 指出
"The response MUST include an Allow header containing a list of valid
methods for the requested resource."
默认情况下,PHP 错误会渲染到控制台或 HTML 输出中,还会记录下来。 如果需要,您可以用自己的代码替换 CakePHP 的错误处理逻辑。
错误处理程序使用 Cake\Error\ErrorLoggingInterface
的实例来创建日志消息并将它们记录到适当的位置。 您可以使用 Error.errorLogger
配置值替换错误记录器。 一个示例错误记录器
namespace App\Error;
use Cake\Error\ErrorLoggerInterface;
use Cake\Error\PhpError;
use Psr\Http\Message\ServerRequestInterface;
use Throwable;
/**
* Log errors and unhandled exceptions to `Cake\Log\Log`
*/
class ErrorLogger implements ErrorLoggerInterface
{
/**
* @inheritDoc
*/
public function logError(
PhpError $error,
?ServerRequestInterface $request,
bool $includeTrace = false
): void {
// Log PHP Errors
}
/**
* @inheritDoc
*/
public function logException(
?ServerRequestInterface $request,
bool $includeTrace = false
): void {
// Log exceptions.
}
}
CakePHP 包含用于 Web 和控制台环境的错误渲染器。 但是,如果您想替换渲染错误的逻辑,您可以创建一个类
// src/Error/CustomErrorRenderer.php
namespace App\Error;
use Cake\Error\ErrorRendererInterface;
use Cake\Error\PhpError;
class CustomErrorRenderer implements ErrorRendererInterface
{
public function write(string $out): void
{
// output the rendered error to the appropriate output stream
}
public function render(PhpError $error, bool $debug): string
{
// Convert the error into the output string.
}
}
您的渲染器构造函数将传递一个包含所有 Error 配置的数组。 您可以通过 Error.errorRenderer
配置值将自定义错误渲染器连接到 CakePHP。 替换错误处理时,您需要同时考虑 Web 和命令行环境。