跳到內容

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
  • 一個 Pdf 函式庫NextPDF\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/ —— 接線參考與冒煙測試。