如何在Laravel中记录或展示PHP致命错误?
处理Laravel 9中PHP内存耗尽致命错误的方案
针对你遇到的超大请求触发内存耗尽、无法返回自定义响应和日志的问题,可以通过以下两种方式解决:
一、捕获致命错误并返回自定义响应/记录日志
PHP的致命错误(如内存耗尽)会直接终止脚本执行,Laravel默认的Exception Handler无法捕获这类错误,需要通过register_shutdown_function在进程关闭前处理:
修改app/Exceptions/Handler.php的register方法:
public function register() { $this->reportable(function (Throwable $e) { // 保留原有常规异常报告逻辑 }); // 注册致命错误回调 register_shutdown_function(function () { $lastError = error_get_last(); // 筛选出内存耗尽类的致命错误 $fatalErrorTypes = [E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR]; if ($lastError && in_array($lastError['type'], $fatalErrorTypes) && str_contains($lastError['message'], 'Allowed memory size')) { // 1. 记录自定义日志(示例用Laravel日志系统,也可替换为自定义SQL写入) \Illuminate\Support\Facades\Log::error('内存耗尽错误', [ 'message' => $lastError['message'], 'file' => $lastError['file'], 'line' => $lastError['line'], 'request_uri' => $_SERVER['REQUEST_URI'] ?? '', 'timestamp' => now()->toDateTimeString() ]); // 2. 返回自定义JSON响应 header('Content-Type: application/json'); http_response_code(413); // 413表示请求实体过大,语义更准确 echo json_encode([ 'status' => 'failed', 'message' => '请求数据量过大,请使用分页功能或减少单次请求的数据规模' ]); exit; } }); }
注意事项:
- 若要直接写入SQL日志,需确保数据库连接在致命错误发生时仍可用;优先推荐使用Laravel日志系统,稳定性更高。
- 需手动设置HTTP响应头和状态码,因为此时Laravel的响应生命周期已终止。
二、提前拦截超大请求(从源头避免错误)
在请求进入业务逻辑前,通过中间件检查请求大小,直接拦截超出阈值的请求:
- 创建中间件
CheckRequestSize:
<?php namespace App\Http\Middleware; use Closure; use Illuminate\Http\Request; class CheckRequestSize { public function handle(Request $request, Closure $next) { // 设定允许的最大请求大小(示例为10MB) $maxAllowedSize = 10 * 1024 * 1024; // 检查请求头部的Content-Length if ($request->hasHeader('Content-Length')) { $requestSize = (int)$request->header('Content-Length'); if ($requestSize > $maxAllowedSize) { return response()->json([ 'status' => 'failed', 'message' => '请求实体过大,请使用分页功能' ], 413); } } return $next($request); } }
- 在
app/Http/Kernel.php中注册中间件,并添加到API中间件组:
protected $routeMiddleware = [ // 其他中间件... 'check.request.size' => \App\Http\Middleware\CheckRequestSize::class, ]; protected $middlewareGroups = [ 'api' => [ // 其他中间件... 'check.request.size', ], ];
这种方式能在请求处理初期就拦截风险,避免触发内存耗尽错误,体验更优。
内容的提问来源于stack exchange,提问作者user1829002
相关产品推荐
相关产品推荐

