Salta ai contenuti

Contratti: 41 interfacce pubbliche (SPI)

NextPDF\Contracts è l’interfaccia pubblica service-provider (SPI): 41 interfacce ed enum sotto src/Contracts/, ciascuno con un tag @stability esplicito e una promessa di compatibilità all’indietro. I pacchetti di estensione, i bridge per i framework e le edizioni Pro ed Enterprise programmano contro questi tipi, mai contro classi concrete.

Terminal window
composer require nextpdf/core:^3

Il motore separa due superfici. Le classi concrete sotto src/Core/, src/Html/ e src/Writer/ non offrono alcuna promessa di compatibilità e possono cambiare liberamente tra le versioni minori. Lo spazio dei nomi Contracts è l’opposto: un insieme curato di tipi le cui firme sono congelate in base al livello di stabilità dichiarato. Tutto ciò che vive all’esterno del motore dipende da questo spazio dei nomi e da nulla di più interno. Questo include i bridge per Laravel, Symfony e CodeIgniter, lo shim compat-tcpdf, NextPDF Server e le edizioni Pro ed Enterprise.

Ogni contratto dichiara nel proprio PHPDoc uno di quattro livelli. Un contratto stable non ammette modifiche incompatibili in una release minore o patch. I nuovi metodi vengono introdotti solo con implementazioni predefinite. Un contratto experimental può cambiare in una release minore con un avviso di deprecazione. Un contratto deprecated indica il proprio sostituto. Un numero limitato di tipi, come StreamingWriterInterface e CursorInterface, esiste solo come contratto. Il tipo è pubblicato e congelato, ma non viene ancora distribuita alcuna implementazione di produzione.

L’elenco autorevole dei livelli è docs/extension-points.json (manifest versione 3.0.0, 67 punti pubblicati tra Contracts ed Event). Un test verificabile automaticamente, tests/Unit/Contracts/StabilityContractTest.php, legge quel manifest. Fa fallire la build in cinque condizioni. La prima è un tipo elencato che risulta mancante. La seconda è un tipo letto tramite reflection che non è allineato al manifest. La terza è un tag PHPDoc @stability che diverge dal manifest. La quarta è un contratto sotto src/Contracts/ che è assente dal manifest. La quinta è un tipo @internal che finisce nel manifest. La superficie dei contratti non può divergere senza essere rilevata.

I contratti rientrano in nove domini. Ciascuno ha una pagina dedicata: costruzione del documento, firma, codifica dei codici a barre, tipografia, criteri di sicurezza, estrazione, osservabilità e streaming. La suddivisione rispecchia il modo in cui un integratore adotta il motore. Per generare i PDF si dipende dal contratto del documento. Per aggiungere una firma si dipende dai contratti di firma. Per vincolare l’HTML non attendibile si dipende dai contratti dei criteri di sicurezza.

La risoluzione di un’implementazione opzionale segue un unico schema in tutto il motore. Il core verifica la presenza di una classe concreta con class_exists() e la usa tramite il contratto. LtvManagerInterface e PdfAManagerInterface risolvono in questo modo le loro implementazioni Pro. Il core rimane pertanto Apache-2.0 senza alcuna dipendenza rigida da codice commerciale.

ContrattoTipoStabilitàDaDominio
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

La tabella elenca i contratti principali. I tipi rimanenti — i DTO value-object (TextSegment, TextPreprocessResult), il sottospazio dei nomi EInvoice, gli enum di comportamento (DegradationPolicy, UnderlineStyle) e i contratti di importazione (ImportedFormObjectInterface, EmbeddedPdfObjectInterface, ChromeRenderResultInterface) — sono documentati nelle pagine di dominio indicate in Vedere anche. L’elenco completo leggibile dalla macchina è docs/extension-points.json, rispecchiato in .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() restituisce un Document concreto che soddisfa PdfDocumentInterface. Dichiarare l’interfaccia come tipo nei propri servizi, così che gli interni del motore restino sostituibili.

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 implementa DocumentFactoryInterface. Mantiene istanze singleton di FontRegistryInterface e ImageRegistryInterface per la durata del processo e le inietta in ogni Document a vita breve, così che un worker analizzi ogni font una sola volta su migliaia di richieste.

  • Un tipo disponibile solo come contratto compila ma non ha alcun supporto a runtime. new su StreamingWriterInterface o CursorInterface non può riuscire perché nessuna classe li implementa ancora. Considerarli come un’API dichiarata in anticipo.
  • Il tag PHPDoc @stability è la fonte autorevole per un singolo tipo. docs/extension-points.json è la fonte autorevole per l’insieme. Quando divergono, StabilityContractTest fallisce — non va nascosto il disaccordo modificando uno dei due lati.
  • experimental non significa instabile nella pratica; significa che la promessa di compatibilità è più debole. Leggere il campo bc_promise di ogni contratto in .ai/contracts-map.md prima di dipendervi.
  • Una classe @internal non è mai un contratto, anche se altri pacchetti possono tecnicamente farvi riferimento. Il test di stabilità rifiuta qualsiasi tipo @internal che compaia nel manifest.
  • Aggiungere un metodo a un’interfaccia stable è una modifica che rompe la compatibilità per gli implementatori, a meno che il metodo non venga distribuito con un’implementazione predefinita. Il motore introduce funzionalità attraverso nuove interfacce, non ampliando quelle esistenti.

Programmare contro Contracts non aggiunge alcun costo misurabile a runtime: un type-hint d’interfaccia si risolve al momento del collegamento, non a ogni chiamata. Il performance_budget per l’esempio worker di questa pagina è di 1500 ms di tempo reale e 64 MB di picco su tre documenti. L’analisi dei font alla prima richiesta domina tale budget. Le richieste successive riutilizzano la cache del registro e il lavoro attribuibile ai contratti scende a millisecondi a una cifra. Il modello di costo è O(1) per dispatch di contratto; il lavoro risiede nell’implementazione concreta, documentata in ciascuna pagina di dominio.

L’SPI è anche un confine di sicurezza. HtmlSecurityPolicyInterface ed ExternalResourcePolicyInterface sono contratti deny-by-default che vincolano ciò che l’HTML non attendibile può fare prima di raggiungere un renderer. CryptoPolicyInterface controlla la scelta dell’algoritmo e della robustezza della chiave per la firma e la cifratura. Poiché sono contratti, un integratore può fornire un criterio più rigoroso senza creare un fork del motore. Vincolarsi al livello stable per qualsiasi criterio rilevante per la sicurezza. I contratti di criteri sperimentali possono cambiare forma tra le release minori. Le pagine di dominio relative alla firma e ai criteri di sicurezza riportano il modello di minaccia completo e i riferimenti normativi.

Questa panoramica non formula alcuna pretesa normativa diretta; ogni pagina di dominio espone il proprio blocco citations. I contratti di firma corrispondono a ISO 32000-2 §12.8 (firme digitali) ed ETSI EN 319 142 (baseline PAdES). Il gestore PDF/A corrisponde a ISO 19005-4. Per le tabelle di conformità a livello di clausola, vedere le pagine relative alla firma, ai criteri di sicurezza e all’estrazione.

Le edizioni Pro ed Enterprise forniscono le implementazioni di produzione dietro diversi contratti core: LtvManagerInterface (convalida a lungo termine), PdfAManagerInterface (applicazione del PDF/A), i firmatari Hardware Security Module (HSM) e differiti, gli encoder dei codici a barre e i contratti di embedding e di indice vettoriale. Il core pubblica e congela l’interfaccia; il pacchetto Premium distribuisce l’implementazione. Questo mantiene il motore open source Apache-2.0 offrendo al contempo alle distribuzioni commerciali un aggiornamento drop-in senza alcuna modifica dell’API.