Ga naar inhoud

Contracts: 41 publieke interfaces (SPI)

NextPDF\Contracts is de publieke service-provider-interface (SPI): 41 interfaces en enums onder src/Contracts/ met een expliciete @stability-tag en een belofte van achterwaartse compatibiliteit. Uitbreidingspakketten, framework-bridges en de Pro- en Enterprise-edities bouwen op deze types, nooit op concrete klassen.

Terminal window
composer require nextpdf/core:^3

De engine biedt twee API-oppervlakken. Concrete klassen onder src/Core/, src/Html/ en src/Writer/ hebben geen compatibiliteitsbelofte en kunnen tussen minor-versies wijzigen. De Contracts-namespace werkt anders. Dit is een zorgvuldig samengestelde verzameling types met signatures die bevroren zijn voor de stabiliteitslaag die ze declareren. Alles buiten de engine is afhankelijk van deze namespace en niet van diepere onderdelen. Daartoe behoren de Laravel-, Symfony- en CodeIgniter-bridges, de compat-tcpdf-shim, NextPDF Server en de Pro- en Enterprise-edities.

Elk contract declareert in zijn PHPDoc een van vier lagen. Een stable-contract staat geen breaking change toe in een minor- of patch-release. Nieuwe methoden komen er alleen bij met een standaardimplementatie. Een experimental-contract kan in een minor-release wijzigen, met een deprecationmelding. Een deprecated-contract noemt zijn vervanger. Een klein aantal types is uitsluitend als contract beschikbaar, zoals StreamingWriterInterface en CursorInterface. Het type is gepubliceerd en bevroren, maar er wordt nog geen productie-implementatie meegeleverd.

De gezaghebbende lagenlijst is docs/extension-points.json (manifest-versie 3.0.0, 67 gepubliceerde punten verdeeld over Contracts en Event). Een machinaal verifieerbare test, tests/Unit/Contracts/StabilityContractTest.php, leest dat manifest en laat de build onder vijf voorwaarden falen. Ten eerste: een vermeld type ontbreekt. Ten tweede: het via reflectie bepaalde soort komt niet overeen met het manifest. Ten derde: een @stability-PHPDoc-tag wijkt af van het manifest. Ten vierde: een contract onder src/Contracts/ ontbreekt in het manifest. Ten vijfde: een @internal-type lekt erin door. Het contractoppervlak kan niet onopgemerkt afwijken.

Contracts vallen uiteen in negen domeinen, elk behandeld op een eigen pagina: documentopbouw, ondertekening, barcodecodering, typografie, beveiligingsbeleid, extractie, observeerbaarheid en streaming. De opdeling weerspiegelt hoe je de engine gebruikt. Voor het genereren van Portable Document Format (PDF)-bestanden ben je afhankelijk van het documentcontract. Voor het toevoegen van een handtekening ben je afhankelijk van de ondertekeningscontracten. Voor het beperken van niet-vertrouwde HTML ben je afhankelijk van de beveiligingsbeleidscontracten.

Het oplossen van optionele implementaties volgt één patroon in de hele engine. Core controleert met class_exists() of er een concrete klasse bestaat en cast die naar het contract. LtvManagerInterface en PdfAManagerInterface lossen hun Pro-implementaties op deze manier op. Core blijft Apache-2.0 zonder harde afhankelijkheid van commerciële code.

ContractSoortStabiliteitSindsDomein
PdfDocumentInterfaceinterfacestable1.0.0document
DocumentFactoryInterfaceinterfacestable1.7.0document
ResettableServiceinterfacestable1.7.0document
OutputDestinationenumstable1.0.0document
Orientationenumstable1.0.0document
Alignmentenumstable1.0.0document
SignerInterfaceinterfacestable1.0.0signing
HsmSignerInterfaceinterfacestable1.0.0signing
DeferredSignerInterfaceinterfaceexperimental3.0.0signing
TimestampProviderInterfaceinterfaceexperimental3.0.0signing
LtvManagerInterfaceinterfacestable1.10.0signing
CryptoPolicyInterfaceinterfacestable1.9.0signing
Barcode1DEncoderInterfaceinterfacestable1.0.0barcode
Barcode2DEncoderInterfaceinterfacestable1.0.0barcode
BarcodeEncoderInterfaceinterfacestable3.0.0barcode
Gs1DataParserInterfaceinterfacestable1.0.0barcode
FontRegistryInterfaceinterfacestable1.7.0typography
TextPreprocessorInterfaceinterfacestable1.9.0typography
HtmlSecurityPolicyInterfaceinterfacestable3.1.0security-policy
ExternalResourcePolicyInterfaceinterfacestable4.0.0security-policy
InspectorInterfaceinterfaceexperimental2.2.0extraction
EmbeddingServiceInterfaceinterfaceexperimental2.1.0extraction
VectorIndexInterfaceinterfaceexperimental2.1.0extraction
JobNotificationInterfaceinterfaceexperimental2.2.0observability
SpectrumInterfaceinterfaceexperimental2.1.0observability
StreamingWriterInterfaceinterfaceexperimental3.1.0streaming
CursorInterfaceinterfaceexperimental3.1.0streaming

De tabel toont de primaire contracten. De overige types, waaronder de value-object data transfer objects (DTO’s) (TextSegment, TextPreprocessResult), de EInvoice-subnamespace, gedragsenums (DegradationPolicy, UnderlineStyle) en import-contracten (ImportedFormObjectInterface, EmbeddedPdfObjectInterface, ChromeRenderResultInterface), worden gedocumenteerd op de domeinpagina’s onder Zie ook. De volledige machineleesbare lijst is docs/extension-points.json, gespiegeld naar .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() retourneert een concreet Document dat aan PdfDocumentInterface voldoet. Gebruik de interface als typehint in je services, zodat de interne onderdelen van de engine uitwisselbaar blijven.

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 implementeert DocumentFactoryInterface. Het houdt singletons van FontRegistryInterface en ImageRegistryInterface aan voor de hele levensduur van het proces en injecteert ze in elk wegwerpbaar Document, zodat een worker elk lettertype eenmaal parseert en dat over duizenden requests heen hergebruikt.

  • Een type dat uitsluitend een contract is, compileert wel maar heeft geen runtimeondersteuning. Een new-expressie op StreamingWriterInterface of CursorInterface kan niet slagen, omdat nog geen enkele klasse ze implementeert. Behandel ze als een vooraf gedeclareerde application programming interface (API).
  • De @stability-PHPDoc-tag is de bron van waarheid voor één type. docs/extension-points.json is de bron van waarheid voor de verzameling. Wanneer ze van elkaar verschillen, faalt StabilityContractTest. Verberg het verschil niet door één kant aan te passen.
  • experimental betekent in de praktijk niet instabiel. Het betekent dat de compatibiliteitsbelofte zwakker is. Lees het bc_promise-veld van elk contract in .ai/contracts-map.md voordat je je eraan vastlegt.
  • Een @internal-klasse is nooit een contract, ook al kunnen andere pakketten er technisch gezien naar verwijzen. De stabiliteitstest weigert elk @internal-type dat in het manifest voorkomt.
  • Het toevoegen van een methode aan een stable-interface is een breaking change voor implementeerders, tenzij de methode met een standaardimplementatie wordt meegeleverd. De engine voegt functionaliteit toe via nieuwe interfaces, niet door bestaande te verbreden.

Programmeren tegen Contracts brengt geen meetbare runtimekosten met zich mee: een interface-typehint wordt opgelost tijdens het linken, niet per aanroep. Het performance_budget voor het workervoorbeeld op deze pagina bedraagt 1500 ms wandkloktijd en 64 MB piekgeheugen over drie documenten. Het parsen van lettertypen bij het eerste request domineert dat budget. Latere requests hergebruiken de registry-cache, waardoor er nog maar enkele milliseconden werk overblijven dat aan contracten toe te schrijven is. Het kostenmodel is O(1) per contractdispatch; het werk zit in de concrete implementatie, gedocumenteerd op elke domeinpagina.

De SPI is ook een beveiligingsgrens. HtmlSecurityPolicyInterface en ExternalResourcePolicyInterface zijn deny-by-default-contracten die beperken wat niet-vertrouwde HTML kan doen voordat die een renderer bereikt. CryptoPolicyInterface reguleert de keuze van algoritme en sleutelsterkte voor ondertekening en versleuteling. Omdat dit contracten zijn, kun je een striktere policy leveren zonder de engine te forken. Pin elke beveiligingsrelevante policy vast op de stable-laag. Experimentele policy-contracten kunnen tussen minor-releases van vorm veranderen. De domeinpagina’s voor ondertekening en beveiligingsbeleid bevatten het volledige dreigingsmodel en de normatieve referenties.

Dit overzicht doet geen rechtstreekse normatieve uitspraak; elke domeinpagina rendert zijn eigen citations-blok. De ondertekeningscontracten zijn gekoppeld aan ISO 32000-2 §12.8 (digitale handtekeningen) en ETSI EN 319 142 (PAdES-baselines). De PDF/A-manager is gekoppeld aan ISO 19005-4. Raadpleeg de pagina’s voor ondertekening, beveiligingsbeleid en extractie voor conformiteitstabellen op clausuleniveau.

De Pro- en Enterprise-edities leveren de productiecode achter diverse core-contracten: LtvManagerInterface (langetermijnvalidatie), PdfAManagerInterface (PDF/A-handhaving), de Hardware Security Module (HSM)- en deferred signers, de barcode-encoders en de embedding- en vector-index-contracten. Core publiceert en bevriest de interface; het Premium-pakket levert de implementatie. Dit houdt de opensource-engine Apache-2.0 en geeft commerciële deployments een drop-in-upgrade zonder API-wijziging.