Przejdź do głównej zawartości

Kontrakty: 41 publicznych interfejsów (SPI)

NextPDF\Contracts to publiczny interfejs dostawcy usług (SPI): obejmuje 41 interfejsów i typów wyliczeniowych w katalogu src/Contracts/, z jawnym znacznikiem @stability oraz obietnicą zgodności wstecznej. Pakiety rozszerzeń, mostki frameworków oraz edycje Pro i Enterprise zależą od tych typów, a nigdy od konkretnych klas.

Okno terminala
composer require nextpdf/core:^3

Silnik udostępnia dwie powierzchnie. Konkretne klasy w katalogach src/Core/, src/Html/ i src/Writer/ nie są objęte obietnicą zgodności i mogą się zmieniać między wydaniami pomocniczymi. Przestrzeń nazw Contracts działa inaczej: to wyselekcjonowany zbiór typów z sygnaturami zamrożonymi dla deklarowanego poziomu stabilności. Wszystko, co znajduje się poza silnikiem, zależy od tej przestrzeni nazw i od niczego głębiej. Dotyczy to mostków Laravel, Symfony i CodeIgniter, warstwy zgodności compat-tcpdf, serwera NextPDF Server oraz edycji Pro i Enterprise.

Każdy kontrakt deklaruje w swoim bloku PHPDoc jeden z czterech poziomów. Kontrakt stable nie dopuszcza żadnej zmiany łamiącej zgodność w wydaniu pomocniczym ani poprawkowym. Nowe metody pojawiają się wyłącznie z implementacjami domyślnymi. Kontrakt experimental może się zmienić w wydaniu pomocniczym wraz z informacją o wycofaniu. Kontrakt deprecated wskazuje swój zamiennik. Niewielką część stanowią typy wyłącznie kontraktowe, takie jak StreamingWriterInterface i CursorInterface. Taki typ jest opublikowany i zamrożony, ale nie ma jeszcze żadnej implementacji produkcyjnej.

Autorytatywną listą poziomów jest docs/extension-points.json (wersja manifestu 3.0.0, 67 opublikowanych punktów w przestrzeniach Contracts i Event). Maszynowo weryfikowalny test, tests/Unit/Contracts/StabilityContractTest.php, odczytuje ten manifest i przerywa budowanie w pięciu przypadkach. Po pierwsze, gdy brakuje wymienionego typu. Po drugie, gdy odzwierciedlony rodzaj jest niezgodny z manifestem. Po trzecie, gdy znacznik PHPDoc @stability jest rozbieżny z manifestem. Po czwarte, gdy kontraktu z katalogu src/Contracts/ brakuje w manifeście. Po piąte, gdy trafi do niego typ @internal. Powierzchnia kontraktów nie może więc niezauważenie odbiec od manifestu.

Kontrakty dzielą się na dziewięć domen, z których każda ma dedykowaną stronę: konstrukcja dokumentu, podpisywanie, kodowanie kodów kreskowych, typografia, polityka bezpieczeństwa, ekstrakcja, obserwowalność i strumieniowanie. Podział odzwierciedla sposób, w jaki wdraża się silnik w praktyce. Do generowania plików w formacie Portable Document Format (PDF) służy kontrakt dokumentu. Do dodania podpisu służą kontrakty podpisywania. Do ograniczenia niezaufanego kodu HTML służą kontrakty polityki bezpieczeństwa.

Rozwiązywanie opcjonalnych implementacji przebiega w całym silniku według jednego wzorca. Pakiet Core sprawdza obecność konkretnej klasy za pomocą class_exists() i rzutuje ją na kontrakt. W ten sposób LtvManagerInterface i PdfAManagerInterface są rozwiązywane do implementacji z edycji Pro. Pakiet Core pozostaje na licencji Apache-2.0, bez twardej zależności od kodu komercyjnego.

KontraktRodzajStabilnośćOd wersjiDomena
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

Tabela wymienia podstawowe kontrakty. Pozostałe typy, w tym obiekty transferu danych (DTO) typu value object (TextSegment, TextPreprocessResult), podprzestrzeń nazw EInvoice, typy wyliczeniowe opisujące zachowanie (DegradationPolicy, UnderlineStyle) oraz kontrakty importu (ImportedFormObjectInterface, EmbeddedPdfObjectInterface, ChromeRenderResultInterface), są udokumentowane na stronach domen w sekcji Zobacz też. Pełna lista w formacie odczytywalnym maszynowo to docs/extension-points.json, odzwierciedlona w .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() zwraca konkretny obiekt Document, który spełnia PdfDocumentInterface. W usługach deklaruj typ interfejsu, aby wewnętrzne elementy silnika pozostały wymienne.

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 implementuje DocumentFactoryInterface. Przechowuje singletony FontRegistryInterface i ImageRegistryInterface o cyklu życia procesu i wstrzykuje je do każdego jednorazowego obiektu Document, dzięki czemu proces roboczy parsuje każdą czcionkę tylko raz na tysiące żądań.

  • Typ wyłącznie kontraktowy przechodzi kompilację, ale nie ma żadnego oparcia w czasie wykonania. Użycie new wobec StreamingWriterInterface lub CursorInterface nie może się powieść, ponieważ żadna klasa jeszcze ich nie implementuje. Traktuj je jako wstępnie zadeklarowany interfejs programowania aplikacji (API).
  • Znacznik PHPDoc @stability jest źródłem prawdy dla pojedynczego typu. docs/extension-points.json jest źródłem prawdy dla całego zbioru. Gdy staną się rozbieżne, StabilityContractTest kończy się niepowodzeniem. Nie ukrywaj tej rozbieżności, edytując tylko jedną stronę.
  • W praktyce experimental nie oznacza niestabilności. Oznacza, że obietnica zgodności jest słabsza. Zanim zaczniesz na nim polegać, przeczytaj pole bc_promise każdego kontraktu w .ai/contracts-map.md.
  • Klasa @internal nigdy nie jest kontraktem, nawet jeśli inne pakiety technicznie mogą się do niej odwoływać. Test stabilności odrzuca każdy typ @internal, który pojawia się w manifeście.
  • Dodanie metody do interfejsu stable jest zmianą łamiącą zgodność dla implementujących go klas, chyba że metoda jest dostarczana z implementacją domyślną. Silnik dodaje możliwości poprzez nowe interfejsy, a nie przez rozszerzanie istniejących.

Programowanie oparte na Contracts nie dodaje mierzalnego kosztu w czasie wykonania: podpowiedź typu dla interfejsu jest rozwiązywana na etapie łączenia, a nie przy każdym wywołaniu. performance_budget dla przykładu z procesem roboczym na tej stronie wynosi 1500 ms czasu rzeczywistego i 64 MB szczytowego zużycia pamięci dla trzech dokumentów. W tym budżecie dominuje parsowanie czcionek przy pierwszym żądaniu. Późniejsze żądania ponownie wykorzystują pamięć podręczną rejestru, a czas przypisywany pracy kontraktów spada do jednocyfrowej liczby milisekund. Model kosztu to O(1) na każde wywołanie kontraktu; właściwa praca odbywa się w konkretnej implementacji, udokumentowanej na każdej stronie domeny.

SPI jest również granicą bezpieczeństwa. HtmlSecurityPolicyInterface i ExternalResourcePolicyInterface to kontrakty działające na zasadzie odmowy domyślnej, które ograniczają to, co może zrobić niezaufany kod HTML, zanim dotrze do mechanizmu renderującego. CryptoPolicyInterface kontroluje wybór algorytmu i siły klucza dla podpisywania oraz szyfrowania. Ponieważ są to kontrakty, można dostarczyć bardziej restrykcyjną politykę bez rozgałęziania silnika. W przypadku każdej polityki istotnej dla bezpieczeństwa opieraj się na poziomie stable. Eksperymentalne kontrakty polityki mogą zmieniać kształt API między wydaniami pomocniczymi. Strony domen podpisywania i polityki bezpieczeństwa zawierają pełny model zagrożeń oraz odniesienia normatywne.

Niniejszy przegląd nie formułuje żadnego bezpośredniego twierdzenia normatywnego; każda strona domeny renderuje własny blok citations. Kontrakty podpisywania odpowiadają ISO 32000-2 §12.8 (podpisy cyfrowe) oraz ETSI EN 319 142 (profile bazowe PAdES). Menedżer PDF/A odpowiada ISO 19005-4. Tabele zgodności na poziomie klauzul znajdziesz na stronach podpisywania, polityki bezpieczeństwa i ekstrakcji.

Edycje Pro i Enterprise dostarczają kod produkcyjny obsługujący kilka kontraktów z pakietu Core: LtvManagerInterface (walidacja długoterminowa), PdfAManagerInterface (egzekwowanie PDF/A), sprzętowy moduł bezpieczeństwa (HSM) i odroczone podpisy, kodery kodów kreskowych oraz kontrakty osadzania i indeksu wektorowego. Pakiet Core publikuje i zamraża interfejs; pakiet Premium dostarcza implementację. Dzięki temu silnik open source pozostaje na licencji Apache-2.0, a wdrożenia komercyjne otrzymują aktualizację drop-in bez zmiany API.