From 87750b20c40547d3b103f540095ff20ee358d27b Mon Sep 17 00:00:00 2001 From: nxxy335 Date: Thu, 10 Sep 2026 15:45:39 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E6=8B=89=E5=8F=96?= =?UTF-8?q?=E5=A4=A7=E9=87=8F=E7=AD=94=E5=8D=B7=E6=97=B6=E5=86=85=E5=AD=98?= =?UTF-8?q?=E8=80=97=E5=B0=BD(128M)=E5=AF=BC=E8=87=B4=E5=90=8E=E5=8F=B0?= =?UTF-8?q?=E5=88=B7=E6=96=B0=E5=A4=B1=E8=B4=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - SurveyKingClient::listAnswers/listProjects 单页上限夹在 20~200, 无论配置 page_size 多大,单次 json_decode 体量都被限制,避免超大响应撑爆 128M - raw() 不再随返回值常驻完整 HTTP 正文(仅留前 2000 字符用于诊断), 降低 json_decode 之外的额外内存占用;每页处理后及时释放引用 - bootstrap.php 在未被 php_admin_value 硬锁时临时将 memory_limit 放宽到 512M 作为安全兜底(触顶场景:/api.php?action=data 拉取卷王全量答卷) 根因:error.log 中 PHP Fatal error Allowed memory size of 134217728 bytes exhausted 位于 SurveyKingClient.php:180 json_decode(),由上传名单后答卷量增大、 单页响应整体解码超出内存上限引发。 --- bootstrap.php | 24 ++++++++++++++++++++++++ lib/SurveyKingClient.php | 17 +++++++++++++---- 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/bootstrap.php b/bootstrap.php index e7b9db5..1982e9c 100644 --- a/bootstrap.php +++ b/bootstrap.php @@ -36,6 +36,30 @@ if ($missing !== []) { date_default_timezone_set('Asia/Shanghai'); mb_internal_encoding('UTF-8'); +// ---------------- 内存上限(数据接口可能处理大量答卷)---------------- +// 拉取卷王全量答卷做 json_decode 时极易触顶 128M;在未被禁用 / 未被 php_admin_value 硬锁的前提下 +// 临时放宽到 512M。若主机用 php_admin_value 锁定了 memory_limit,下面的 ini_set 会静默失效, +// 需要改主机 php.ini(或 fpm 池配置)才能生效。 +(function (): void { + $cur = (string) ini_get('memory_limit'); + if ($cur === '' || $cur === '-1') { + return; // 未设置 / 已无限制,无需处理 + } + $curBytes = (int) $cur; + $unit = strtoupper(substr($cur, -1)); + if ($unit === 'G') { + $curBytes *= 1024 * 1024 * 1024; + } elseif ($unit === 'M') { + $curBytes *= 1024 * 1024; + } elseif ($unit === 'K') { + $curBytes *= 1024; + } + $want = 512 * 1024 * 1024; + if ($curBytes > 0 && $curBytes < $want) { + @ini_set('memory_limit', '512M'); + } +})(); + // ---------------- 会话 ---------------- function ny_session_start(): void { diff --git a/lib/SurveyKingClient.php b/lib/SurveyKingClient.php index feed323..e96fb13 100644 --- a/lib/SurveyKingClient.php +++ b/lib/SurveyKingClient.php @@ -187,7 +187,10 @@ class SurveyKingClient $msg = (string)($decoded['msg'] ?? $decoded['message'] ?? ''); } - return ['status' => $status, 'headers' => $h, 'data' => $data, 'raw' => $rawBody, 'code' => $code, 'msg' => $msg]; + // 仅保留前若干字符用于错误诊断;完整正文不再随返回值常驻内存, + // 避免大答卷响应在 json_decode 之外再额外占用一倍内存。 + $rawDiag = mb_strcut($rawBody, 0, 2000); + return ['status' => $status, 'headers' => $h, 'data' => $data, 'raw' => $rawDiag, 'code' => $code, 'msg' => $msg]; } /** @@ -448,7 +451,7 @@ class SurveyKingClient /** 问卷 / 考试列表 */ public function listProjects(): array { - $size = max(20, (int)($this->cfg['page_size'] ?? 200)); + $size = min(200, max(20, (int)($this->cfg['page_size'] ?? 100))); $all = []; for ($page = 1; $page <= 50; $page++) { $r = $this->request('/project/list', ['current' => $page, 'pageSize' => $size]); @@ -483,7 +486,9 @@ class SurveyKingClient /** 答卷列表(自动翻页取全量) */ public function listAnswers(string $projectId): array { - $size = max(20, (int)($this->cfg['page_size'] ?? 200)); + // 单页上限夹在 20~200:无论配置 page_size 多大,单次 json_decode 的体量都被限制住, + // 避免一份超大响应直接撑爆 128M。100 页 × 200 = 最多 2 万份,足以覆盖学校级考试。 + $size = min(200, max(20, (int)($this->cfg['page_size'] ?? 100))); $all = []; for ($page = 1; $page <= 100; $page++) { $r = $this->request('/answer/list', [ @@ -502,10 +507,14 @@ class SurveyKingClient } } $total = is_array($data) ? (int)($data['total'] ?? 0) : 0; + // 及时释放本页引用,降低峰值内存占用 + $r = null; + $data = null; + $list = null; if ($total > 0 && count($all) >= $total) { break; } - if (count($list) < $size) { + if (count($all) < $size) { break; } }