Перейти к содержимому

Контракты: 41 публичный интерфейс (SPI)

NextPDF\Contracts — публичный интерфейс поставщика служб (SPI): 41 интерфейс и перечисление в каталоге src/Contracts/ с явным тегом @stability и обещанием обратной совместимости. Пакеты расширений, мосты для фреймворков, а также редакции Pro и Enterprise зависят от этих типов, а не от конкретных классов.

Окно терминала
composer require nextpdf/core:^3

Движок предоставляет две API-поверхности. На конкретные классы в каталогах src/Core/, src/Html/ и src/Writer/ обещание совместимости не распространяется, поэтому они могут меняться между минорными версиями. Пространство имён Contracts работает иначе. Это тщательно отобранный набор типов: их сигнатуры заморожены на уровне стабильности, который они объявляют. Всё за пределами движка зависит от этого пространства имён и не обращается к внутренним слоям. Сюда относятся мосты для Laravel, Symfony и CodeIgniter, прослойка compat-tcpdf, NextPDF Server, а также редакции Pro и Enterprise.

Каждый контракт объявляет в своём PHPDoc один из четырёх уровней. Контракт уровня stable не допускает несовместимых изменений в минорных или патч-выпусках. Новые методы появляются только с реализациями по умолчанию. Контракт уровня experimental может измениться в минорном выпуске с уведомлением об устаревании. Контракт уровня deprecated указывает свою замену. Некоторые типы существуют только как контракты, например StreamingWriterInterface и CursorInterface. Такой тип опубликован и заморожен, но рабочей реализации пока нет.

Авторитетный список уровней — это docs/extension-points.json (версия манифеста 3.0.0, 67 опубликованных точек в Contracts и Event). Автоматический тест tests/Unit/Contracts/StabilityContractTest.php читает этот манифест и проваливает сборку в пяти случаях. Во-первых, перечисленный тип отсутствует. Во-вторых, отражённый вид не совпадает с манифестом. В-третьих, тег PHPDoc @stability расходится с манифестом. В-четвёртых, контракт из src/Contracts/ отсутствует в манифесте. В-пятых, в манифест попадает тип с пометкой @internal. Так поверхность контрактов не может незаметно разойтись с манифестом.

Контракты распределяются по девяти доменам, каждый из которых описан на отдельной странице: построение документа, подписание, кодирование штрихкодов, типографика, политика безопасности, извлечение, наблюдаемость и потоковая обработка. Это разделение отражает, как вы внедряете движок. Для создания файлов в формате Portable Document Format (PDF) вы опираетесь на контракт документа. Для добавления подписи — на контракты подписания. Для ограничения недоверенного HTML — на контракты политики безопасности.

Необязательные реализации разрешаются по единому шаблону во всём движке. Ядро проверяет наличие конкретного класса с помощью class_exists() и приводит его к контракту. LtvManagerInterface и PdfAManagerInterface именно так находят свои реализации Pro. Ядро остаётся под Apache-2.0 без жёсткой зависимости от коммерческого кода.

КонтрактВидСтабильностьС версииДомен
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

В таблице перечислены основные контракты. Остальные типы, включая объекты передачи данных (DTO) на основе объектов-значений (TextSegment, TextPreprocessResult), вложенное пространство имён EInvoice, перечисления поведения (DegradationPolicy, UnderlineStyle) и контракты импорта (ImportedFormObjectInterface, EmbeddedPdfObjectInterface, ChromeRenderResultInterface), описаны на доменных страницах в разделе См. также. Полный машиночитаемый список находится в 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. Она хранит синглтоны FontRegistryInterface и ImageRegistryInterface в течение всего жизненного цикла процесса и внедряет их в каждый одноразовый Document, поэтому воркер разбирает каждый шрифт один раз для тысяч запросов.

  • Тип, существующий только как контракт, компилируется, но не имеет реализации во время выполнения. Выражение new для StreamingWriterInterface или CursorInterface не выполнится успешно, потому что их пока не реализует ни один класс. Считайте их предварительно объявленным программным интерфейсом приложения (API).
  • Тег PHPDoc @stability — это источник истины для отдельного типа. docs/extension-points.json — это источник истины для всего набора. Когда они расходятся, StabilityContractTest завершается с ошибкой. Не маскируйте расхождение, правя только одну сторону.
  • На практике experimental не означает нестабильность. Это означает, что обещание совместимости слабее. Прочитайте поле bc_promise каждого контракта в .ai/contracts-map.md, прежде чем на него опираться.
  • Класс с пометкой @internal никогда не является контрактом, даже если другие пакеты технически могут на него ссылаться. Тест стабильности отклоняет любой тип с пометкой @internal, появившийся в манифесте.
  • Добавление метода в интерфейс уровня stable — это несовместимое изменение для реализующих сторон, если только метод не поставляется с реализацией по умолчанию. Движок добавляет возможности через новые интерфейсы, а не за счёт расширения существующих.

Программирование на основе Contracts не добавляет измеримых затрат во время выполнения: тип-хинт интерфейса разрешается на этапе связывания, а не при каждом вызове. performance_budget для примера с воркером на этой странице составляет 1500 мс реального времени и 64 МБ пикового потребления на три документа. Разбор шрифтов при первом запросе занимает основную часть этого бюджета. Последующие запросы повторно используют кеш реестра, и работа, относимая к контракту, снижается до единиц миллисекунд. Модель затрат — O(1) на каждую диспетчеризацию контракта; основная работа выполняется в конкретной реализации, описанной на каждой доменной странице.

SPI также является границей безопасности. HtmlSecurityPolicyInterface и ExternalResourcePolicyInterface — это контракты с запретом по умолчанию, которые ограничивают возможности недоверенного HTML до того, как он попадёт в средство отрисовки. CryptoPolicyInterface контролирует выбор алгоритма и стойкости ключа для подписания и шифрования. Поскольку это контракты, вы можете задать более строгую политику, не делая форк движка. Для любой политики, важной с точки зрения безопасности, опирайтесь на уровень stable. Экспериментальные контракты политик могут менять форму между минорными выпусками. Доменные страницы о подписании и политике безопасности содержат полную модель угроз и нормативные ссылки.

В этом обзоре нет прямых нормативных утверждений; каждая доменная страница формирует собственный блок citations. Контракты подписания соотносятся с ISO 32000-2 §12.8 (цифровые подписи) и ETSI EN 319 142 (базовые профили PAdES). Менеджер PDF/A соотносится с ISO 19005-4. Таблицы соответствия на уровне пунктов см. на страницах о подписании, политике безопасности и извлечении.

Редакции Pro и Enterprise предоставляют рабочий код для нескольких контрактов ядра: LtvManagerInterface (долгосрочная проверка), PdfAManagerInterface (обеспечение соответствия PDF/A), аппаратный модуль безопасности (HSM) и отложенные подписанты, кодировщики штрихкодов, а также контракты эмбеддинга и векторного индекса. Ядро публикует и замораживает интерфейс; пакет Premium поставляет реализации. Это сохраняет движок с открытым исходным кодом под Apache-2.0 и даёт коммерческим развёртываниям прямой путь обновления без изменения API.

  • Contracts / Document — контракты PDF-документа, фабрики и реестра.
  • Contracts / Signing — контракты подписанта, HSM, метки времени и долгосрочной проверки (LTV).
  • Contracts / Security Policy — контракты криптографической политики, политики HTML и политики ресурсов.
  • Contracts / Typography — контракты реестра шрифтов и предобработки текста.
  • Contracts / Extraction — контракты инспектора, PDF/A, эмбеддинга и электронных счетов.
  • Core — конкретные классы, которые удовлетворяют этим контрактам.
  • Event — событийный аналог SPI, публикуемый вместе с Contracts.