跳转到内容

NextPDF:适用于 CodeIgniter 4

nextpdf/codeigniter 通过框架自身的 Services 层,将 NextPDF PDF 引擎连接到 CodeIgniter 4 应用程序。你可以在控制器、工作(job)或命令中构建 PDF 文档,然后以 CodeIgniter 原生 HTTP 响应的形式返回它们。

Terminal window
composer require nextpdf/codeigniter

此包的 composer.json 要求 php >=8.4 <9.0nextpdf/core ^3.0 || ^5.2codeigniter4/framework ^4.6。它还建议安装 nextpdf/artisannextpdf/premiumcodeigniter4/queue。完整的需求表、可选包以及验证步骤,请参阅 /integrations/codeigniter/install/ 一节。

NextPDF 是一套 PHP 8.4 PDF 2.0 引擎。核心引擎(nextpdf/core)不依赖任何框架;它不了解 HTTP、路由或依赖注入。nextpdf/codeigniter 是将引擎连接到 CodeIgniter 4 应用程序的适配层。有了这个适配层,你就不必自行接入注册表(registry)、工厂或响应处理。

此包为 CodeIgniter 4 应用程序添加四项能力:

  • 一个 Services 类NextPDF\CodeIgniter\Config\Services),由 CodeIgniter 自动发现,并对外公开具名服务:fontRegistryimageRegistrydocumentFactorypdfDocumentpdftsaClientpdfSigner
  • 一个 PdfNextPDF\CodeIgniter\Libraries\Pdf)——一套高层控制器 API。它包装单个一次性文档,并在一次调用中将其转换为响应。
  • 一个 PdfResponse 辅助类NextPDF\CodeIgniter\Http\PdfResponse),它会生成 CodeIgniter 的 DownloadResponse,用于内嵌预览或下载,并附加一组固定的响应加固标头。
  • 两个全局辅助函数pdf()pdf_document()。它们通过 Composer 的 files autoload 条目以及包的 Registrar 注册。

此包还会在构建文档时检测可选的 NextPDF 扩展功能。当 nextpdf/artisan 已安装且设置了 Chrome 可执行文件时,文档就会取得 Chrome renderer(渲染器)。当 NextPDF Pro 已安装时,便能通过同一个 Services 接口使用 PDF/A 输出与数字签名。这项检测是有条件且静默执行的;此包绝不会要求一个并不存在的扩展功能。

CodeIgniter 4 并未内置 PSR-11 依赖注入容器,而是使用 Services 定位器。Services 定位器是一个带静态工厂方法的类,由框架发现;每个方法返回共享实例或全新实例。PSR-11 明确不建议使用服务定位器模式,也就是把容器传入对象、让对象自行获取依赖(PSR-11 §1.3,modal SHOULD NOT)。此包遵循 CodeIgniter 的定位器惯例,同时让定位器接口保持精简明确:每个服务都是一个具名工厂方法,带有 bool $getShared 参数,调用方拿到的是具体对象,而不是容器句柄。

这项设计让 CodeIgniter 集成与 Laravel、Symfony 集成保持一致。这些集成都通过各自框架的惯用语法,对外公开同一组逻辑服务。

进入点类型回传生命周期
Services::fontRegistry()服务FontRegistryInterface共享(先预热,再锁定)
Services::imageRegistry()服务ImageRegistry共享(有界 LRU 缓存)
Services::documentFactory()服务DocumentFactoryInterface共享(无状态)
Services::pdfDocument(false)服务NextPDF\Core\Document每次调用均为全新实例
Services::pdf(false)服务NextPDF\CodeIgniter\Libraries\Pdf每次调用均为全新实例
Services::tsaClient()服务?TsaClient共享;null(无 TSA URL 时)
Services::pdfSigner(false)服务?SignerInterface全新;null(停用签名时)
pdf()辅助函数Pdf每次调用均为全新实例
pdf_document()辅助函数Document每次调用均为全新实例
PdfResponse::inline() / download()静态DownloadResponse每次调用
GeneratePdfJob队列任务每次派发一个

控制器只需三行即可返回一份 PDF。Services::pdf() 会创建一个全新的 Pdf 库,并包装一份全新文档。随后 download() 会生成 CodeIgniter 的 DownloadResponse

<?php
declare(strict_types=1);
namespace App\Controllers;
use CodeIgniter\HTTP\DownloadResponse;
use NextPDF\CodeIgniter\Config\Services;
final class InvoiceController extends BaseController
{
public function download(int $id): DownloadResponse
{
$pdf = Services::pdf();
$pdf->document()->addPage();
$pdf->document()->cell(0, 10, "Invoice #{$id}");
return $pdf->download("invoice-{$id}.pdf");
}
}

完整可运行的分步教程见 /integrations/codeigniter/quickstart/ 一节。它涵盖路由、内嵌预览,以及 pdf()pdf_document() 辅助函数的各种变体。

在正式环境中,请使用 Services::pdf(false) 获取一个非共享实例。请捕获单一基类异常 NextPDF\Exception\NextPdfException;所有核心与扩展功能的失败都继承自它。请连同上下文一起记录这次失败,不要吞掉错误。

try {
$pdf = Services::pdf(false);
$pdf->document()->addPage();
$pdf->document()->cell(0, 10, "Invoice #{$id}");
return $pdf->download("invoice-{$id}.pdf");
} 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]);
}

完整的正式环境控制器见 /integrations/codeigniter/production-usage/ 一节。它加入了可观测性计时、worker 安全的生命周期,以及异步生成。

  • 字体与图像注册表都是进程生命周期内的单例。文档绝不会被共享。pdfDocumentpdf 每次调用都会返回一个全新实例,因此一个请求不会把内容泄漏到另一个请求。Services::pdf(false)pdf() 都会返回一个全新的库,并包装一份全新文档。
  • 此包需要 mbstringzlib 这两个 PHP 扩展。字体注册表会在每个进程中验证一次。如果任一扩展不存在,字体注册表会抛出运行时错误,并指出缺少的扩展。
  • 可选扩展功能的行为取决于同一个应用程序中安装了什么。若只有 nextpdf/core 存在,签名与 PDF/A 路径会返回 null 或被跳过;它们绝不会因此显式报错。

除引擎本身之外,此集成不会带来任何可测量的额外开销。字体注册表只解析一次,之后便锁定。图像注册表是一个 LRU 缓存,受 imageCacheMb 配置值限制(默认 50 MB)。PDF 构建成本由核心引擎与文档内容决定,而不是由适配层决定。这套文档中每页预算为 1500 ms wall / 128 MB peak。实际示例会在 front-matter 中设置自己的预算。

PdfResponse 会为它发送的每一份 PDF 附加一组固定的响应标头:X-Content-Type-Options: nosniffX-Frame-Options: DENYContent-Security-Policy: default-src 'none'X-Robots-Tag: noindex, nofollowReferrer-Policy: no-referrer。文件名会经过净化处理,非 ASCII 名称则以 RFC 5987 扩展参数的形式发送。队列任务会把构建器 callable 限制在 App\PdfBuilders 命名空间,并把输出路径限定在 WRITEPATH/pdfs/。完整的威胁模型请见 /integrations/codeigniter/security-and-operations/ 一节。

  • 模块发现依赖 Composer 的 PSR-4 自动加载。命名空间前缀映射到一个基目录,完整限定类名则映射到一个文件路径(PSR-4 §x1.x3)。
  • Services 的设计遵循 PSR-11 §1.3 中讨论的定位器指引。

NextPDF 核心采用 Apache-2.0。数字签名、PDF/A 归档以及 Factur-X 电子发票嵌入,由 NextPDF Pro 与 NextPDF Enterprise 提供。CodeIgniter 包会对外公开对应的服务方法。在同一个应用程序中安装对应的 Premium 包之前,这些方法都会返回 null

  • /integrations/codeigniter/install/ —— 安装并验证包。
  • /integrations/codeigniter/quickstart/ —— 在控制器中产生第一份 PDF。
  • /integrations/codeigniter/configuration/ —— 每个配置键。
  • /integrations/codeigniter/boot-and-discovery/ —— CodeIgniter 如何找到 Services 类。
  • /integrations/codeigniter/integration/ —— 接入参考与冒烟测试。