Skip to content

SPI stability rules

The NextPDF service provider interface follows Semantic Versioning. Each public contract carries a @stability tag and a backward-compatibility promise. Use these rules to decide which contracts you can depend on.

Terminal window
composer require nextpdf/core:^3

The service provider interface includes the public contracts in the NextPDF\Contracts and NextPDF\Event namespaces. A type is part of the interface only when its source PHPDoc carries an @stability tag. The tag defines the boundary. A type without the tag is internal, even when PHP exposes it as public.

NextPDF follows Semantic Versioning 2.0.0. A breaking change to a stable contract requires a major version increment. A new contract or non-breaking addition increments the minor version. A bug fix increments the patch version.

Each contract declares one of three stability values:

TagMeaningChange rule
stableReady for production. Safe to depend on.No breaking change in a minor or patch release. New methods are added only with a default behavior or on a new contract.
experimentalUsable, but not yet frozen.The interface may change in a minor release, with a deprecation notice first.
deprecatedScheduled for removal.The contract states the replacement and the removal version.

NextPDF records each per-contract promise in the generated contracts map and regenerates it from source on every release. The promise text gives the exact rule for that contract. Treat the contract source PHPDoc as the single source of truth.

The contracts map records each promise in one of four classes:

  1. Interface promise. “No breaking change in a minor or patch release. New methods only with a default implementation.” Applies to most stable interfaces, including FontRegistryInterface, SignerInterface, HsmSignerInterface, and HtmlSecurityPolicyInterface.
  2. Enum promise. “No removal of cases. New cases may be added in a minor version.” Applies to stable enums such as Alignment, Orientation, and OutputDestination.
  3. Frozen value-object promise. “Constructor signature and public properties are frozen. New methods may be added.” Applies to value objects such as TextPreprocessResult, TextSegment, and the event payloads bound to it.
  4. Experimental promise. “The interface may change in a minor version with a deprecation notice.” Applies to experimental contracts such as DeferredSignerInterface, TimestampProviderInterface, CursorInterface, and StreamingWriterInterface.

A final class such as EventDispatcher or ListenerProvider freezes its public method signatures. Use composition to extend a final class. Do not subclass it.

CursorInterface and StreamingWriterInterface are experimental (since 3.1.0). NextPDF ships final, tested engine implementations for both contracts; the implementation classes are internal and are not part of the public surface. You consume streaming behavior through the public experimental contract. In most cases, you do not implement the contract yourself.

Because the contract is experimental, its signature may change in a minor release, with a deprecation notice first (the experimental promise). Pin tightly or wrap it behind your own adapter before you depend on it in production. Treat the streaming contract as a stabilizing extension point, not a frozen point.

There is no runtime application programming interface (API) on this page. The relevant surface is the @stability PHPDoc tag on every public contract and the regenerated contracts map that aggregates the per-contract promise.

Read a contract’s stability from source before you depend on it.

<?php
declare(strict_types=1);
use ReflectionClass;
$doc = (new ReflectionClass(\NextPDF\Contracts\FontRegistryInterface::class))
->getDocComment();
// Look for the "@stability stable" line in the contract PHPDoc.
\assert(\is_string($doc) && \str_contains($doc, '@stability stable'));

A Composer version constraint pins the major version, which is where breaking changes occur for a stable contract.

{
"require": {
"nextpdf/core": "^3.0"
}
}

Use ^3.0 to receive minor and patch releases without a breaking change to any stable contract. Pin more tightly when you depend on an experimental contract, because an experimental contract may change in a minor release.

  • Tag, not visibility. A PHP method marked public is not part of the service provider interface unless its declaring type carries an @stability tag.
  • Experimental drift. An experimental contract may change in a minor release. Pin tightly or wrap it behind your own adapter. This applies to the streaming contracts even though they have shipped implementations.
  • New default methods. A stable interface may gain a method that has a default behavior. Implement the new method when you upgrade so your own implementation stays explicit.
  • Edition parity. NextPDF Pro and NextPDF Enterprise follow the same rules. A contract you target in Core stays valid against a Premium implementation of the same contract.

A contract moves through a defined lifecycle:

  1. Mark. The owner sets @stability deprecated in the source PHPDoc and records the replacement and the removal version.
  2. Notice. The deprecation is announced in the changelog for the release that marks it.
  3. Overlap. The deprecated contract and its replacement coexist for at least one minor release.
  4. Remove. The contract is removed in the stated major release. Removal never happens in a minor or patch release.

Plan an upgrade as soon as a contract is marked deprecated. The replacement is always stated.

This page defines policy. It has no runtime cost.

Signing contracts are stable and follow the interface promise. A new method on a signing contract arrives only with a default behavior or on a new contract, so a hardware-backed implementation does not break on a minor upgrade. Review the changelog before a major upgrade, because a major version may change a stable contract.

These versioning rules conform to Semantic Versioning 2.0.0. Changelog generation follows Conventional Commits 1.0.0.

NextPDF Pro and NextPDF Enterprise follow the same service provider interface stability rules as Core. A contract you target in Core remains valid against the Premium implementation of that contract, so your extension code is portable across editions.

The glossary defines stability tag and backward-compatibility promise. See the published glossary for the canonical definitions.