Enterprise edition
Release — Deep Reference
At a glance
Section titled “At a glance”The NextPDF\Enterprise\Release namespace models a product release as immutable, typed value objects. Its public surface is seven final classes and five string-backed enums. ReleaseManifest aggregates per-artifact ArtifactManifest entries plus supply-chain evidence paths. BuildProfile is the single source of truth for one artifact variant. ArtifactNamer and PackageMapping derive deterministic filenames and Composer package identity from a profile. PublishingPlan::fromProfiles resolves every profile to typed PublishingTarget entries with the correct channel and access boundary. ReleaseStatus types the release lifecycle. The module performs no I/O, no network calls, and no cryptography; it is pure metadata modeling.
Availability & licensing
Section titled “Availability & licensing”This capability ships in NextPDF Enterprise (nextpdf/enterprise) and activates with an Enterprise-tier license envelope. A deployment without that entitlement does not load the capability’s classes. Compare editions and get a license.
This surface is licensed by the nextpdf/enterprise package boundary and has no separate per-feature capability code. Core and Pro do not ship a release-modeling layer.
Public API surface
Section titled “Public API surface”composer require nextpdf/enterprise:^3| Symbol | Parameters | Default behavior | Returns | Throws or fails with | Notes |
|---|---|---|---|---|---|
ReleaseManifest::__construct | version, sourceCommit, builtAt, artifacts = [], sbomPath = null, gpgSignature = null, checksumPath = null | Builds the immutable top-level release document | instance | — | final readonly; SCHEMA_VERSION is '1.0'; minor for additive fields, major for breaking; since 3.0.0 |
ReleaseManifest::createWithDefaults | string $version, string $sourceCommit, DateTimeImmutable $builtAt, array $artifacts = [] | Pre-wires the default SBOM, signature, and checksum filenames via ArtifactNamer | self | — | Canonical entry point for release builders; use the constructor to override individual paths |
ReleaseManifest::toArray / ::toJson | none | Serializes the manifest with all artifact entries | array / JSON string | toJson: JsonException on encoding failure | Pretty-printed, unescaped slashes; built_at in ATOM format |
ReleaseManifest::validate | none | Checks the artifact list is non-empty and every artifact matches the release version and commit | list<string> of errors; empty means valid | Never throws | Callers must treat a non-empty list as a defect |
ArtifactManifest::__construct | filename, version, sourceCommit, edition, deliveryMode, encodingTechnology, channel, phpTarget, sha256, ioncubeExpire, builtAt | Records one built artifact: what was built, how, and how to verify it | instance | — | final readonly; ioncubeExpire is null for non-expiring artifacts |
ArtifactManifest::fromProfile | BuildProfile $profile, string $version, string $sourceCommit, string $sha256, ?DateTimeImmutable $builtAt = null | Derives the filename and all profile dimensions | self | — | builtAt defaults to the current time |
ArtifactManifest::toArray | none | Serializes for JSON encoding | array<string, string|null> | — | Enum fields serialize to their backing string values |
BuildProfile::__construct / ::fromArray | profile fields / array $data | Defines one artifact variant; the single source of truth for naming, manifest, and plan | instance / self | fromArray: InvalidArgumentException on a missing or empty required field | Required keys: name, edition, delivery, encoding_technology, channel, php_target |
BuildProfile::requiresEncoding / ::isEvaluation | none | Predicate helpers over the profile dimensions | bool | — | Encoding requires DeliveryMode::Encoded and EncodingTechnology::IonCube together |
ArtifactNamer::format | BuildProfile $profile, string $version | Emits nextpdf-{edition}-{delivery}-{channel}-php{target}-{version}.zip | non-empty-string | — | Deterministic; dots are stripped from the PHP target (8.4 becomes php84) |
ArtifactNamer::checksumFile / ::signatureFile / ::sbomFile / ::manifestFile | string $version (manifestFile: none) | Emits SHA256SUMS-{v}.txt, SHA256SUMS-{v}.txt.asc, sbom-{v}.cdx.json, release-manifest.json | non-empty-string | — | The SBOM filename matches the CycloneDX document core’s SBOM generator writes |
PackageMapping::packageName / ::resolvePackageName | BuildProfile / LicenseEdition + LicenseChannel | Maps to nextpdf/pro or nextpdf/enterprise; the evaluation channel appends -evaluation | non-empty-string | InvalidArgumentException on an unknown edition | The separate evaluation namespace prevents mixing with paid artifacts |
PackageMapping::distUrlPattern | BuildProfile $profile, string $version | Resolves the artifact download URL on the canonical artifact repository | non-empty-string | — | Encoded and cleartext share a package name; their dist URLs differ |
PackageMapping::accessBoundary / ::artifactOrigin / ::consumptionChannel / ::allPackageNames | BuildProfile / none / none / none | Boundary follows the license channel; fixed origin and consumption channels; all customer-facing package names | AccessBoundary / DistributionChannel / DistributionChannel / list<non-empty-string> | — | Core is not a premium package and is excluded from the name list |
PublishingPlan::fromProfiles | array $profiles, string $version, PublishingEnvironment $environment = Staging | Resolves two targets per profile: one artifact origin, one consumption layer | self | — | The environment defaults to Staging; production is always an explicit choice |
PublishingPlan::targetsByBoundary / ::targetsByChannel / ::packageNames | AccessBoundary / DistributionChannel / none | Filter and enumeration helpers over the resolved targets | list<PublishingTarget> / list<PublishingTarget> / list<non-empty-string> | — | packageNames is sorted and de-duplicated |
PublishingPlan::validate | none | Checks targets are non-empty, versions match the plan, and no evaluation package sits in a paid boundary | list<string> of errors; empty means valid | Never throws | Run before any upload step |
PublishingPlan::toArray | none | Serializes for dry-run output and logging | array<string, mixed> | — | Includes target_count and the package list |
PublishingTarget::fromProfile | BuildProfile $profile, string $version, PublishingEnvironment $environment, DistributionChannel $channel | Resolves the package name, access boundary, and artifact filename for one channel | self | — | final readonly; toArray serializes for logging |
AccessBoundary | enum: Paid, Evaluation, Internal | isCustomerFacing is true for Paid and Evaluation only | backed by 'paid', 'evaluation', 'internal' | — | requiresAuthentication returns true for every boundary |
DistributionChannel | enum: GitHubReleases, PrivatePackagist, DirectDownload | isArtifactOrigin and isConsumptionLayer split the two roles | backed by 'github_releases', 'private_packagist', 'direct_download' | — | DirectDownload is reserved for a future signed-URL channel |
EncodingTechnology | enum: IonCube, None | Names the encoding tool, distinct from the delivery form | backed by 'ioncube', 'none' | — | A cleartext artifact always reports None |
PublishingEnvironment | enum: Staging, Production | isCustomerFacing is true only for Production | backed by 'staging', 'production' | — | Staging is an internal verification target |
ReleaseStatus | enum: Built, Audited, Staged, Published, Revoked, Superseded | canPromote, nextStatus, isCustomerVisible, isTerminal type the lifecycle | nextStatus returns ?self | — | Promotion chain Built → Audited → Staged → Published; Revoked and Superseded are terminal |
Entry-point signatures, verbatim from source:
public static function createWithDefaults(string $version, string $sourceCommit, DateTimeImmutable $builtAt, array $artifacts = []): selfpublic static function fromProfile(BuildProfile $profile, string $version, string $sourceCommit, string $sha256, ?DateTimeImmutable $builtAt = null): selfpublic static function fromArray(array $data): selfpublic static function format(BuildProfile $profile, string $version): stringpublic static function fromProfiles(array $profiles, string $version, PublishingEnvironment $environment = PublishingEnvironment::Staging): selfpublic static function fromProfile(BuildProfile $profile, string $version, PublishingEnvironment $environment, DistributionChannel $channel): selfBehavior contract
Section titled “Behavior contract”- Every class is
final; every value class isreadonly. Construction fixes all state. The module performs no filesystem, network, or cryptographic operations. ReleaseManifestis the immutable top-level document for a version: semantic version, source commit, build timestamp, artifact list, and optional supply-chain evidence paths (SBOM, GPG signature, checksums). Its schema version is minor-for-additive and major-for-breaking.BuildProfileis the single source of truth for what an artifact contains and how it is packaged. Naming, manifest, and plan all derive from the profile; there is no secondary configuration source.EncodingTechnologynames the encoding tool;DeliveryMode(from the Licensing module) names the delivered form. The two are deliberately separate, and a cleartext artifact always reportsEncodingTechnology::None.DistributionChannelseparates an artifact origin (binary storage; the upload destination) from a package consumption layer (the registry acomposer requirereads). The origin stores the binary; the consumption layer serves the metadata that points to it.AccessBoundaryenumerates Paid, Evaluation, and Internal. Every boundary requires authentication. Internal artifacts serve CI, QA, and staging and are never customer-facing.PublishingPlan::fromProfilesresolves each profile to exactly two targets: an artifact-origin target and a consumption-layer target, with the access boundary derived from the profile’s license channel so paid and evaluation artifacts route correctly.- The plan is descriptive of intended targets, not transport. The actual upload is performed by the surrounding release tooling, and the manifest records — but does not itself produce — supply-chain evidence.
ReleaseStatustypes the lifecycle: Built → Audited → Staged → Published, withRevokedandSupersededas terminal exits fromPublished. OnlyPublishedis customer-visible;canPromoteisfalsefromPublishedonward.ReleaseManifest::validateandPublishingPlan::validatereport consistency errors as string lists and never throw. An empty list is the only valid outcome.
Edge cases & failure modes
Section titled “Edge cases & failure modes”BuildProfile::fromArraywith a missing, non-string, or empty required field:InvalidArgumentExceptionnaming the field.BuildProfile::fromArraywith an empty or non-stringioncube_expire: normalized tonull(non-expiring). Non-string or empty entries inencode_pathsandexclude_pathsare silently dropped.PackageMapping::resolvePackageNamewith an edition outside the known map:InvalidArgumentException.ReleaseManifest::toJsonon unencodable data:JsonException. Serialization usesJSON_THROW_ON_ERROR; there is no silent fallback.ReleaseManifest::validatereports an empty artifact list, and any artifact whose version or source commit differs from the release, as errors.PublishingPlan::validatereports an empty target list, any target version diverging from the plan, and any evaluation package resolved into a paid boundary.ReleaseStatus::nextStatusreturnsnullfromPublished,Revoked, andSuperseded. Callers must handlenull; there is no wrap-around.- The origin and consumption-layer channels are distinct; do not point a
composer requireat the artifact origin. - Internal-boundary artifacts are never customer-facing; routing them to a customer channel is an explicit modeling error.
- This module performs no cryptographic operations. GPG signing and checksum generation are external and only referenced here.
Conformance
Section titled “Conformance”This module is a release-modeling layer. The default SBOM filename (sbom-{version}.cdx.json) matches the CycloneDX document that core’s SBOM generator writes; the manifest references that evidence and does not validate it. GPG signatures and checksums are produced and verified by the surrounding release pipeline.
Development notes
Section titled “Development notes”- Prefer
ReleaseManifest::createWithDefaultsso the evidence filenames always agree withArtifactNamer. Reserve the raw constructor for deliberate per-path overrides. - Run both
validate()gates in CI before any publication step and fail the pipeline on a non-empty error list. PublishingPlan::fromProfilesdefaults toPublishingEnvironment::Staging. PassProductionexplicitly; nothing in this module promotes an environment implicitly.- Serialized manifests carry enum backing values (
paid,encoded,ioncube, and so on) and ATOM timestamps; treat those strings as the wire contract. - Evaluation artifacts live in a separate package namespace (
-evaluationsuffix). Keep paid and evaluation registries distinct in your Composer configuration. - Per-channel routing, credentials, and storage are the operator’s responsibility. The plan tells the tooling where an artifact belongs, not how to authenticate to it.
Publication boundary
Section titled “Publication boundary”This page documents externally observable behavior and the supported public API surface only. Internal namespace paths, helper classes, mechanism tables, runbook filenames, and ticket prefixes are out of scope.
See also
Section titled “See also”- Release (capability page)
- Licensing — Deep Reference — the
LicenseEdition,LicenseChannel, andDeliveryModeenums the profiles consume. - Enterprise overview
- License activation