CodeIgniter 4 中的生产环境使用
快速总览
标题为“快速总览”的章节生产环境中的控制器依赖具体的 NextPDF 服务。它们会明确处理已记录的异常层级,并发出可观测性信号。耗时的 PDF 工作会通过 CodeIgniter 4 Queue 移出请求流程之外执行。
概念总览
标题为“概念总览”的章节CodeIgniter 4 会通过自身的定位器(locator)来 resolve(解析)该软件包的服务。服务定位器(service locator)模式会把容器(container)交给某个对象,让该对象自行取出所需依赖。此模式并不建议使用(PSR-11 §1.3,modal SHOULD NOT)。若要遵循该指引,请在控制器边界逐一解析每个 NextPDF 服务,再把具体对象向内传递。请勿把 Services 类或容器传入你的领域代码。
每个 PHP 示例都会单独一行声明 declare(strict_types=1);(PSR-12 §x1.x3.p34)。
API 接口
标题为“API 接口”的章节| 生产环境考量 | 已验证接口 |
|---|---|
| 解析服务 | Services::pdf(false)、Services::pdfDocument(false)、Services::documentFactory() |
| 构建响应 | PdfResponse::download() / inline() → DownloadResponse |
| 捕获失败 | NextPDF\Exception\NextPdfException(生态系统基类型) |
| 异步生成 | GeneratePdfJob:已注册于 Config\Queue::$jobHandlers |
| 路径 / 可调用项防护 | GeneratePdfJob 会抛出 InvalidArgumentException |
生产环境控制器 —— 错误处理与可观测性
标题为“生产环境控制器 —— 错误处理与可观测性”的章节核心引擎抛出的异常全部继承自 NextPDF\Exception\NextPdfException。只捕获这一个类型,就能涵盖核心和扩展功能的失败。这里的 catch 块会带上下文记录日志,并返回一个明确定义的错误响应,绝不留下空的 catch。
<?php
declare(strict_types=1);
namespace App\Controllers;
use CodeIgniter\HTTP\DownloadResponse;use CodeIgniter\HTTP\ResponseInterface;use NextPDF\CodeIgniter\Config\Services;use NextPDF\Exception\NextPdfException;use Psr\Log\LoggerInterface;
final class InvoiceController extends BaseController{ public function download(int $id): DownloadResponse|ResponseInterface { /** @var LoggerInterface $logger */ $logger = \service('logger');
$start = \hrtime(true);
try { $pdf = Services::pdf(false); $pdf->document()->addPage(); $pdf->document()->cell(0, 10, "Invoice #{$id}");
$response = $pdf->download("invoice-{$id}.pdf");
$logger->info('pdf.invoice.generated', [ 'invoice_id' => $id, 'elapsed_ms' => (\hrtime(true) - $start) / 1_000_000, ]);
return $response; } catch (NextPdfException $e) { $logger->error('pdf.invoice.failed', [ 'invoice_id' => $id, 'exception' => $e::class, 'message' => $e->getMessage(), ]);
return $this->response ->setStatusCode(ResponseInterface::HTTP_INTERNAL_SERVER_ERROR) ->setJSON(['error' => 'pdf_generation_failed', 'invoice_id' => $id]); } }}Services::pdf(false) 每次调用都会返回全新的库实例和全新的底层文档。因此,并发请求之间绝不会共用文档状态。该软件包的功能测试会验证这一行为。
worker 安全的服务生命周期
标题为“worker 安全的服务生命周期”的章节字体与图像注册表(registry)按进程生命周期单例(singleton)设计。字体注册表只会预热并锁定一次。图像注册表是一个有界的最近最少使用(LRU)缓存。在长驻 worker(CodeIgniter spark 服务器、RoadRunner 风格执行器,或队列 worker)中,这正是预期行为:成本较高的注册表会持续保留,而每份文档都是全新的。请勿在请求或工作代码中索取共用文档(Services::pdfDocument(true));它仅供测试重置使用,否则会在多个请求之间共用内容。
以 CodeIgniter Queue 进行异步生成
标题为“以 CodeIgniter Queue 进行异步生成”的章节GeneratePdfJob 会通过 codeigniter4/queue 把 PDF 生成工作移出请求流程之外执行。队列运行时会强制以下两项事实成立,而这两项你都必须正确配置。
1. 以名称注册工作处理器
标题为“1. 以名称注册工作处理器”的章节队列按**名称键(name key)**解析工作,而不是按类字符串解析。队列处理器会将推入的工作名称与 Config\Queue::$jobHandlers 的键进行比对并验证。若遇到未知名称,它会以 CodeIgniter\Queue\Exceptions\QueueException 拒绝。请在 app/Config/Queue.php 中注册工作:
<?php
declare(strict_types=1);
namespace Config;
use CodeIgniter\Queue\Config\Queue as BaseQueue;use NextPDF\CodeIgniter\Jobs\GeneratePdfJob;
final class Queue extends BaseQueue{ /** @var array<string, class-string> */ public array $jobHandlers = [ 'generate-pdf' => GeneratePdfJob::class, ];}2. 以注册的名称派发
标题为“2. 以注册的名称派发”的章节将注册名称作为第二个参数推入工作。第一个参数是队列名称,第三个参数是工作数据数组。
<?php
declare(strict_types=1);
namespace App\Controllers;
use CodeIgniter\HTTP\ResponseInterface;
final class InvoiceController extends BaseController{ public function queueInvoice(int $id): ResponseInterface { \service('queue')->push('pdf-queue', 'generate-pdf', [ 'builder' => 'App\\PdfBuilders\\InvoiceBuilder::build', 'outputPath' => WRITEPATH . 'pdfs/invoice-' . $id . '.pdf', 'context' => ['invoice_id' => $id], ]);
return $this->response ->setStatusCode(ResponseInterface::HTTP_ACCEPTED) ->setJSON(['status' => 'queued', 'invoice_id' => $id]); }}3. 在 App\PdfBuilders 之下实现构建器
标题为“3. 在 App\PdfBuilders 之下实现构建器”的章节该工作会将构建器(builder)可调用项限制在 App\PdfBuilders 命名空间(namespace)下,并将输出路径限定在 WRITEPATH/pdfs/ 内。构建器是一个静态方法,会接收一份全新的 Document 和上下文数组,并返回该文档。
<?php
declare(strict_types=1);
namespace App\PdfBuilders;
use NextPDF\Core\Document;
final class InvoiceBuilder{ /** @param array<string, mixed> $context */ public static function build(Document $document, array $context): Document { $invoiceId = (int) ($context['invoice_id'] ?? 0);
$document->addPage(); $document->cell(0, 10, "Invoice #{$invoiceId}");
return $document; }}运行 worker
标题为“运行 worker”的章节php spark queue:work pdf-queue每次工作运行都会通过 Services::pdfDocument() 从全新文档开始。它会应用构建器,然后保存到已验证的路径。该软件包的测试会验证连续两次工作运行不会共用文档状态。
边界情况与陷阱
标题为“边界情况与陷阱”的章节- 队列在推入时会拒绝以
GeneratePdfJob::class作为工作名称,因为它不是已注册的键'generate-pdf'。请务必推入jobHandlers的键。 - 构建器字符串必须完全匹配
App\PdfBuilders\<Class>::<method>。函数、其他命名空间,或带有 prefixed/suffixed 形式的载荷,都会在任何代码运行之前就引发InvalidArgumentException。 - 输出路径必须解析到
WRITEPATH/pdfs/内部,并以.pdf结尾(不区分大小写)。路径穿越以及同层前缀路径都会被拒绝。 codeigniter4/queue是该软件包仅供开发使用的依赖。请在实际运行 worker 的应用程序中安装它。
注册表在每个 worker 进程中只会创建一次。文档构建成本随内容变化,与适配器(adapter)无关。对于大型批处理工作,请优先采用队列路径,让请求 worker 保持响应能力。任何有可衡量目标的 recipe(示例)都请设置 performance_budget。
安全注意事项
标题为“安全注意事项”的章节队列工作是风险最高的接口。当 broker(消息中介)可被触及时,队列载荷会受到攻击者影响。其可调用项允许列表与路径限定,以及已验证的拒绝案例,记录在 /integrations/codeigniter/security-and-operations/ 一节。
符合性
标题为“符合性”的章节- 控制器收到的是具体服务,而非容器,这与 PSR-11 §1.3 的服务定位器指引一致。
商业情境
标题为“商业情境”的章节NextPDF 核心采用 Apache-2.0 许可。在队列工作中生成签名和 PDF/A 输出,需要在 worker 环境安装 NextPDF Pro 或 Enterprise。CodeIgniter 软件包会公开对应的服务方法。安装对应的 Premium 软件包之前,这些方法都会返回 null。请参阅 </get-license/?intent=codeigniter-async-signing>。
另请参阅
标题为“另请参阅”的章节- /integrations/codeigniter/quickstart/ —— 这些控制器的精简版本。
- /integrations/codeigniter/configuration/ —— 签名、TSA 和路径配置。
- /integrations/codeigniter/security-and-operations/ —— 队列威胁模型和加固。
- /integrations/codeigniter/troubleshooting/ —— 队列与发现的失败模式。
- /integrations/codeigniter/integration/ —— 接线参考和冒烟测试。