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

Создание расширений: обзор публичного SPI

NextPDF предоставляет небольшой, продуманный набор публичных контрактов в пространствах имён NextPDF\Contracts и NextPDF\Event. Реализуйте эти контракты, чтобы добавлять шрифты, перехватывать текст, отслеживать жизненный цикл документа или подключать собственный сервер подписания, не форкая движок.

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

NextPDF отделяет свой публичный интерфейс поставщика услуг (SPI) от внутреннего кода. SPI — это набор типов, которые вы можете реализовывать или за которыми можете наблюдать. Всё остальное приватно и может измениться без предупреждения.

Публичный SPI существует в трёх формах:

  • Контракты реестров. Службы уровня процесса, которые вы заполняете перед созданием документов; FontRegistryInterface и ImageRegistryInterface — основные примеры. Зарегистрируйте ресурсы, и движок будет читать их оттуда.
  • Контракты стратегий. Точечные перехватчики, которые движок вызывает во время отрисовки. TextPreprocessorInterface отвечает за перехват текста на этапе макета, а HtmlSecurityPolicyInterface управляет доступом к возможностям HTML. Вы предоставляете поведение, а движок вызывает его.
  • Контракты подписания. Криптографические серверы. SignerInterface, HsmSignerInterface и DeferredSignerInterface позволяют вам отвечать за хранение ключа и формирование подписи. Движок строит структуру синтаксиса криптографических сообщений (CMS), а ваш код хранит ключ.

Отдельная система событий в NextPDF\Event, совместимая с рекомендацией PHP Standard Recommendation 14 (PSR-14), отвечает за наблюдение. События жизненного цикла позволяют реагировать на создание документа, новые страницы, загрузку шрифтов, подписание и запись результата. Поведение движка они не меняют.

У каждого контракта в исходном PHPDoc есть тег @stability: stable, experimental или deprecated. Этот тег и обещание обратной совместимости для конкретного контракта показывают, насколько серьёзных изменений можно ожидать. Полную политику смотрите в разделе Правила стабильности SPI.

ВозможностьПубличный контрактСтабильность
Регистрация и поиск шрифтовNextPDF\Contracts\FontRegistryInterfacestable (с версии 1.7.0)
Кэширование и декодирование изображенийNextPDF\Contracts\ImageRegistryInterfacestable (с версии 2.0.0)
Перехват текста на этапе макетаNextPDF\Contracts\TextPreprocessorInterfacestable (с версии 1.9.0)
Управление возможностями HTMLNextPDF\Contracts\HtmlSecurityPolicyInterfacestable (с версии 3.1.0)
Подключение фабрики документовNextPDF\Contracts\DocumentFactoryInterfacestable (с версии 1.7.0)
Синхронное подписаниеNextPDF\Contracts\SignerInterfacestable (с версии 1.0.0)
Подписание с аппаратной поддержкойNextPDF\Contracts\HsmSignerInterfacestable (с версии 1.0.0)
Отложенное и пакетное подписаниеNextPDF\Contracts\DeferredSignerInterfaceexperimental (с версии 3.0.0)
Метки времени RFC 3161NextPDF\Contracts\TimestampProviderInterfaceexperimental (с версии 3.0.0)
Наблюдение за жизненным цикломNextPDF\Event\* (совместимо с PSR-14)stable-диспетчер; experimental-полезные нагрузки

Следующие типы являются внутренними. Не импортируйте их, не наследуйте от них и не зависьте от них:

  • Любой класс за пределами пространств имён NextPDF\Contracts и NextPDF\Event, если только в его PHPDoc нет тега @stability.
  • Конкретный код движка, включая парсер HTML, модуль записи, конвейер макета и сабсеттер шрифтов.
  • Пакеты NextPDF Pro и NextPDF Enterprise. Их внутренние классы не входят в открытую поверхность с исходным кодом. Когда платная редакция поставляет реализацию SPI, используйте публичный контракт, а не её внутренний тип.

Сгенерированная карта контрактов является эталонной и пересобирается из исходного кода для каждого выпуска. Считайте тег PHPDoc @stability в каждом файле интерфейса единственным источником истины. Используйте приведённую выше таблицу как вспомогательное средство при чтении.

Зарегистрируйте шрифт, затем отследите момент его загрузки. Оба шага используют только публичные типы.

<?php
declare(strict_types=1);
use NextPDF\Contracts\FontRegistryInterface;
use NextPDF\Event\Content\FontLoadedEvent;
use NextPDF\Event\EventDispatcher;
use NextPDF\Event\ListenerProvider;
/** @var FontRegistryInterface $fonts */
$fonts->register('/srv/fonts/Inter-Regular.ttf', 'Inter');
$listeners = new ListenerProvider();
$listeners->addListener(
FontLoadedEvent::class,
static function (FontLoadedEvent $event): void {
\error_log("Font loaded: {$event->family} {$event->style}");
},
);
$dispatcher = new EventDispatcher($listeners);

В долгоживущем воркере соберите реестры один раз при запуске, заблокируйте их и передайте общий диспетчер через фабрику документов.

<?php
declare(strict_types=1);
use NextPDF\Contracts\DocumentFactoryInterface;
use NextPDF\Contracts\FontRegistryInterface;
use NextPDF\Event\EventDispatcher;
use NextPDF\Event\ListenerProvider;
use Psr\Log\LoggerInterface;
final class DocumentBootstrap
{
public function __construct(
private readonly FontRegistryInterface $fonts,
private readonly DocumentFactoryInterface $factory,
private readonly LoggerInterface $logger,
) {}
public function warmup(): EventDispatcher
{
$this->fonts->warmup([
'/srv/fonts/Inter-Regular.ttf',
'/srv/fonts/Inter-Bold.ttf',
]);
$this->fonts->lock();
$listeners = new ListenerProvider();
$listeners->addListener(
\NextPDF\Event\Security\SignatureAppliedEvent::class,
fn (object $event): mixed => $this->logger->info('Signature applied'),
);
return new EventDispatcher($listeners);
}
}
  • Блокировка реестра. После FontRegistryInterface::lock() методы изменения выбрасывают LogicException. Блокируйте реестр только после завершения прогрева.
  • Несоответствие стабильности. Контракт experimental может измениться в минорном выпуске. Проверьте заявленную стабильность, прежде чем использовать контракт в рабочей среде.
  • Дисциплина пространств имён. Тип за пределами NextPDF\Contracts или NextPDF\Event без тега @stability является внутренним, даже если технически он public.

SPI не создаёт накладных расходов, когда не используется. Если ни один слушатель не привязан к классу события, диспетчер событий сразу возвращает управление после единственной проверки hasListeners(). Реестры хранят чистые данные PHP и поддерживают прогрев при запуске, чтобы распределить задержку первого запроса.

Контракты подписания — это поверхность, критичная для безопасности. HsmSignerInterface требует, чтобы закрытый ключ никогда не покидал аппаратную границу. Ваша реализация обязана соблюдать это требование. Контракт стороннего сервера подписания и его модель угроз смотрите в разделе контракт поставщика системы управления ключами (KMS).

На этой обзорной странице нет нормативных утверждений. Соответствие для каждого контракта, включая усовершенствованные электронные подписи PDF (PAdES) и управление ключами, документировано на соответствующих страницах SPI.

NextPDF Pro и NextPDF Enterprise предоставляют готовые к эксплуатации реализации нескольких контрактов подписания и проверки, включая подписание с поддержкой системы управления ключами. Ваш код зависит от публичного контракта, а редакция поставляет реализацию, поэтому код остаётся переносимым между редакциями.

В глоссарии определены термины SPI, точка расширения, тег стабильности и обещание обратной совместимости; канонические определения смотрите в опубликованном глоссарии.