跳到內容

契約:41 個公開介面(SPI)

NextPDF\Contracts 是公開的 service-provider interface(SPI,服務提供者介面):src/Contracts/ 之下共有 41 個介面與列舉,每個都帶有明確的 @stability 標記與向後相容承諾。擴充套件、Framework(框架)橋接器,以及 Pro 與 Enterprise 版本都針對這些型別撰寫程式,絕不直接依賴具體類別。

Terminal window
composer require nextpdf/core:^3

引擎將兩個介面層分開。src/Core/src/Html/src/Writer/ 之下的具體類別不承諾相容性,可能在各個次要版本之間自由變動。Contracts 命名空間則相反:它是一組精選型別,其簽章會依各自宣告的穩定度層級凍結。引擎以外的一切都只依賴這個命名空間,不再往下探入具體實作。這包含 Laravel、Symfony 與 CodeIgniter 橋接器、compat-tcpdf shim(相容層)、NextPDF Server,以及 Pro 與 Enterprise 版本。

每個契約都會在其 PHPDoc 中宣告四個層級之一。stable 契約在次要或修補版本中不接受任何破壞性變更。新方法只會連同預設實作一起加入。experimental 契約可能在次要版本中變更,但會附上棄用通知。deprecated 契約會指明其替代型別。少數型別僅作為契約存在,例如 StreamingWriterInterfaceCursorInterface。這類型別已發布並凍結,但尚未隨附任何正式環境實作。

權威的層級清單是 docs/extension-points.json(資訊清單版本 3.0.0,橫跨 ContractsEvent 共 67 個已發布的擴充點)。可由機器驗證的測試 tests/Unit/Contracts/StabilityContractTest.php 會讀取該資訊清單,並在五種情況下讓建置失敗。第一種是清單列出的型別缺漏。第二種是反射取得的類型與資訊清單不一致。第三種是 @stability PHPDoc 標記與資訊清單發生偏移。第四種是 src/Contracts/ 之下有契約,卻未出現在資訊清單中。第五種是 @internal 型別洩漏進其中。契約介面層一旦發生偏移,就會被偵測出來。

契約分為九個領域。每個領域都有專屬頁面:文件建構、簽章、條碼編碼、字體排印、安全政策、擷取、可觀測性與串流。這樣的切分對應到整合者採用引擎的方式。你會依賴文件契約來產生 PDF,依賴簽章契約來加入簽章,也會依賴安全政策契約來約束不受信任的 HTML。

在整個引擎中,resolve(解析)選用實作都遵循同一種模式。Core 會以 class_exists() 檢查具體類別是否存在,再將它轉型為對應契約。LtvManagerInterfacePdfAManagerInterface 就是以這種方式解析其 Pro 實作。因此 Core 維持 Apache-2.0 授權,且不硬性依賴商業程式碼。

契約類型穩定度起始版本領域
PdfDocumentInterface介面穩定1.0.0文件
DocumentFactoryInterface介面穩定1.7.0文件
ResettableService介面穩定1.7.0文件
OutputDestination列舉穩定1.0.0文件
Orientation列舉穩定1.0.0文件
Alignment列舉穩定1.0.0文件
SignerInterface介面穩定1.0.0簽章
HsmSignerInterface介面穩定1.0.0簽章
DeferredSignerInterface介面實驗性3.0.0簽章
TimestampProviderInterface介面實驗性3.0.0簽章
LtvManagerInterface介面穩定1.10.0簽章
CryptoPolicyInterface介面穩定1.9.0簽章
Barcode1DEncoderInterface介面穩定1.0.0條碼
Barcode2DEncoderInterface介面穩定1.0.0條碼
BarcodeEncoderInterface介面穩定3.0.0條碼
Gs1DataParserInterface介面穩定1.0.0條碼
FontRegistryInterface介面穩定1.7.0字體排印
TextPreprocessorInterface介面穩定1.9.0字體排印
HtmlSecurityPolicyInterface介面穩定3.1.0安全政策
ExternalResourcePolicyInterface介面穩定4.0.0安全政策
InspectorInterface介面實驗性2.2.0擷取
EmbeddingServiceInterface介面實驗性2.1.0擷取
VectorIndexInterface介面實驗性2.1.0擷取
JobNotificationInterface介面實驗性2.2.0可觀測性
SpectrumInterface介面實驗性2.1.0可觀測性
StreamingWriterInterface介面實驗性3.1.0串流
CursorInterface介面實驗性3.1.0串流

此表格列出主要契約。其餘型別——value-object DTO(TextSegmentTextPreprocessResult)、EInvoice 子命名空間、行為列舉(DegradationPolicyUnderlineStyle),以及匯入契約(ImportedFormObjectInterfaceEmbeddedPdfObjectInterfaceChromeRenderResultInterface)——都記錄在 另見所列的領域頁面中。完整的機器可讀清單是 docs/extension-points.json,並鏡像同步到 .ai/contracts-map.md

examples/01-hello-world.php
<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();
$doc->setTitle('Hello World');
$doc->addPage();
$doc->setFont('helvetica', '', 24);
$doc->cell(0, 15, 'Hello, NextPDF!', newLine: true);
$doc->save(__DIR__ . '/output/01-hello-world.pdf');

Document::createStandalone() 會回傳一個具體的 Document,它滿足 PdfDocumentInterface。請在你自己的服務中以介面作為型別提示,讓引擎內部維持可替換。

examples/14-worker-factory.php
<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Core\DocumentFactory;
use NextPDF\Core\PdfFactory;
use NextPDF\Graphics\ImageRegistry;
use NextPDF\Typography\FontRegistry;
// Created once at process boot in a RoadRunner/Swoole/Octane worker.
$fontRegistry = new FontRegistry();
$imageRegistry = new ImageRegistry(maxCacheBytes: 50 * 1024 * 1024);
$documentFactory = new DocumentFactory($fontRegistry, $imageRegistry);
$factory = PdfFactory::new()
->withCompress(true)
->withDocumentFactory($documentFactory);
for ($request = 1; $request <= 3; $request++) {
$doc = $factory->create();
$doc->setTitle("Worker Request #{$request}");
$doc->addPage();
$doc->setFont('helvetica', 'B', 16);
$doc->cell(0, 12, "Worker Request #{$request}", newLine: true);
$doc->save(__DIR__ . "/output/14-worker-request-{$request}.pdf");
}

DocumentFactory 實作 DocumentFactoryInterface。它會持有行程生命週期等級的 FontRegistryInterfaceImageRegistryInterface 單例,並把它們注入每個可丟棄的 Document,因此一個 worker 在數千個請求中只需剖析每個字體一次。

  • 僅契約型別可以編譯,但沒有任何執行期支援。new 無法用於 StreamingWriterInterfaceCursorInterface,因為目前還沒有任何類別實作它們。請把它們視為預先宣告的 API。
  • 單一型別的真實來源是 @stability PHPDoc 標記。docs/extension-points.json 是整組型別的真實來源。當兩者不一致時,StabilityContractTest 就會失敗——別只改其中一邊來掩蓋這個不一致。
  • experimental 在實務上不代表不穩定,而是代表相容性承諾較弱。在固定依賴某個契約之前,請先閱讀其 bc_promise 欄位(位於 .ai/contracts-map.md)。
  • 標示為 @internal 的類別永遠不是契約,即使其他套件在技術上能夠參照它。穩定度測試會拒絕任何出現在資訊清單中的 @internal 型別。
  • stable 介面新增方法,對實作者而言是破壞性變更,除非該方法隨附預設實作。引擎透過新增介面來擴充能力,而不是擴張既有介面。

針對 Contracts 撰寫程式不會增加任何可量測的執行期成本:介面型別提示在連結時解析,而不是每次呼叫時解析。本頁 worker 範例的 performance_budget 為三份文件合計 1500 ms 牆鐘時間與 64 MB 峰值記憶體。第一個請求的字體剖析會主導這份預算。後續請求會重用 registry 快取,使契約相關的工作降到個位數毫秒。成本模型在每次契約派送上為 O(1);真正的工作在具體實作中,記錄於各領域頁面。

SPI 同時也是一道安全邊界。HtmlSecurityPolicyInterfaceExternalResourcePolicyInterface 是預設拒絕的契約,會在不受信任的 HTML 抵達 renderer(渲染器)之前約束它能做的事。CryptoPolicyInterface 會把關簽章與加密所用的演算法與金鑰強度選擇。因為這些是契約,整合者可以提供更嚴格的政策,而無須分叉引擎。任何與安全相關的政策都請固定依賴 stable 層級。實驗性政策契約可能在各個次要版本之間改變形態。簽章與安全政策領域頁面會載明完整的威脅模型與規範性參考。

本總覽不作任何直接的規範性主張;每個領域頁面會建立自己的 citations 區塊。簽章契約對應到 ISO 32000-2 §12.8(數位簽章)與 ETSI EN 319 142(PAdES 基準)。PDF/A 管理器對應到 ISO 19005-4。關於條款層級的符合性表格,請參閱簽章、安全政策與擷取頁面。

Pro 與 Enterprise 版本實作了數個 Core 契約背後的正式環境程式碼:LtvManagerInterface(長期驗證)、PdfAManagerInterface(PDF/A 強制)、Hardware Security Module(HSM,硬體安全模組)與延遲簽署者、條碼編碼器,以及嵌入與向量 Index(索引)契約。Core 發布並凍結介面;Premium 套件則隨附實作。這讓開源引擎維持 Apache-2.0 授權,同時讓商業部署享有可直接替換、且不需變更 API 的升級。