Skip to content
getnextpdf.com

Enterprise edition

Release — Deep Reference

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.

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.

Terminal window
composer require nextpdf/enterprise:^3
SymbolParametersDefault behaviorReturnsThrows or fails withNotes
ReleaseManifest::__constructversion, sourceCommit, builtAt, artifacts = [], sbomPath = null, gpgSignature = null, checksumPath = nullBuilds the immutable top-level release documentinstancefinal readonly; SCHEMA_VERSION is '1.0'; minor for additive fields, major for breaking; since 3.0.0
ReleaseManifest::createWithDefaultsstring $version, string $sourceCommit, DateTimeImmutable $builtAt, array $artifacts = []Pre-wires the default SBOM, signature, and checksum filenames via ArtifactNamerselfCanonical entry point for release builders; use the constructor to override individual paths
ReleaseManifest::toArray / ::toJsonnoneSerializes the manifest with all artifact entriesarray / JSON stringtoJson: JsonException on encoding failurePretty-printed, unescaped slashes; built_at in ATOM format
ReleaseManifest::validatenoneChecks the artifact list is non-empty and every artifact matches the release version and commitlist<string> of errors; empty means validNever throwsCallers must treat a non-empty list as a defect
ArtifactManifest::__constructfilename, version, sourceCommit, edition, deliveryMode, encodingTechnology, channel, phpTarget, sha256, ioncubeExpire, builtAtRecords one built artifact: what was built, how, and how to verify itinstancefinal readonly; ioncubeExpire is null for non-expiring artifacts
ArtifactManifest::fromProfileBuildProfile $profile, string $version, string $sourceCommit, string $sha256, ?DateTimeImmutable $builtAt = nullDerives the filename and all profile dimensionsselfbuiltAt defaults to the current time
ArtifactManifest::toArraynoneSerializes for JSON encodingarray<string, string|null>Enum fields serialize to their backing string values
BuildProfile::__construct / ::fromArrayprofile fields / array $dataDefines one artifact variant; the single source of truth for naming, manifest, and planinstance / selffromArray: InvalidArgumentException on a missing or empty required fieldRequired keys: name, edition, delivery, encoding_technology, channel, php_target
BuildProfile::requiresEncoding / ::isEvaluationnonePredicate helpers over the profile dimensionsboolEncoding requires DeliveryMode::Encoded and EncodingTechnology::IonCube together
ArtifactNamer::formatBuildProfile $profile, string $versionEmits nextpdf-{edition}-{delivery}-{channel}-php{target}-{version}.zipnon-empty-stringDeterministic; dots are stripped from the PHP target (8.4 becomes php84)
ArtifactNamer::checksumFile / ::signatureFile / ::sbomFile / ::manifestFilestring $version (manifestFile: none)Emits SHA256SUMS-{v}.txt, SHA256SUMS-{v}.txt.asc, sbom-{v}.cdx.json, release-manifest.jsonnon-empty-stringThe SBOM filename matches the CycloneDX document core’s SBOM generator writes
PackageMapping::packageName / ::resolvePackageNameBuildProfile / LicenseEdition + LicenseChannelMaps to nextpdf/pro or nextpdf/enterprise; the evaluation channel appends -evaluationnon-empty-stringInvalidArgumentException on an unknown editionThe separate evaluation namespace prevents mixing with paid artifacts
PackageMapping::distUrlPatternBuildProfile $profile, string $versionResolves the artifact download URL on the canonical artifact repositorynon-empty-stringEncoded and cleartext share a package name; their dist URLs differ
PackageMapping::accessBoundary / ::artifactOrigin / ::consumptionChannel / ::allPackageNamesBuildProfile / none / none / noneBoundary follows the license channel; fixed origin and consumption channels; all customer-facing package namesAccessBoundary / DistributionChannel / DistributionChannel / list<non-empty-string>Core is not a premium package and is excluded from the name list
PublishingPlan::fromProfilesarray $profiles, string $version, PublishingEnvironment $environment = StagingResolves two targets per profile: one artifact origin, one consumption layerselfThe environment defaults to Staging; production is always an explicit choice
PublishingPlan::targetsByBoundary / ::targetsByChannel / ::packageNamesAccessBoundary / DistributionChannel / noneFilter and enumeration helpers over the resolved targetslist<PublishingTarget> / list<PublishingTarget> / list<non-empty-string>packageNames is sorted and de-duplicated
PublishingPlan::validatenoneChecks targets are non-empty, versions match the plan, and no evaluation package sits in a paid boundarylist<string> of errors; empty means validNever throwsRun before any upload step
PublishingPlan::toArraynoneSerializes for dry-run output and loggingarray<string, mixed>Includes target_count and the package list
PublishingTarget::fromProfileBuildProfile $profile, string $version, PublishingEnvironment $environment, DistributionChannel $channelResolves the package name, access boundary, and artifact filename for one channelselffinal readonly; toArray serializes for logging
AccessBoundaryenum: Paid, Evaluation, InternalisCustomerFacing is true for Paid and Evaluation onlybacked by 'paid', 'evaluation', 'internal'requiresAuthentication returns true for every boundary
DistributionChannelenum: GitHubReleases, PrivatePackagist, DirectDownloadisArtifactOrigin and isConsumptionLayer split the two rolesbacked by 'github_releases', 'private_packagist', 'direct_download'DirectDownload is reserved for a future signed-URL channel
EncodingTechnologyenum: IonCube, NoneNames the encoding tool, distinct from the delivery formbacked by 'ioncube', 'none'A cleartext artifact always reports None
PublishingEnvironmentenum: Staging, ProductionisCustomerFacing is true only for Productionbacked by 'staging', 'production'Staging is an internal verification target
ReleaseStatusenum: Built, Audited, Staged, Published, Revoked, SupersededcanPromote, nextStatus, isCustomerVisible, isTerminal type the lifecyclenextStatus returns ?selfPromotion 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 = []): self
public static function fromProfile(BuildProfile $profile, string $version, string $sourceCommit, string $sha256, ?DateTimeImmutable $builtAt = null): self
public static function fromArray(array $data): self
public static function format(BuildProfile $profile, string $version): string
public static function fromProfiles(array $profiles, string $version, PublishingEnvironment $environment = PublishingEnvironment::Staging): self
public static function fromProfile(BuildProfile $profile, string $version, PublishingEnvironment $environment, DistributionChannel $channel): self
  • Every class is final; every value class is readonly. Construction fixes all state. The module performs no filesystem, network, or cryptographic operations.
  • ReleaseManifest is 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.
  • BuildProfile is 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.
  • EncodingTechnology names the encoding tool; DeliveryMode (from the Licensing module) names the delivered form. The two are deliberately separate, and a cleartext artifact always reports EncodingTechnology::None.
  • DistributionChannel separates an artifact origin (binary storage; the upload destination) from a package consumption layer (the registry a composer require reads). The origin stores the binary; the consumption layer serves the metadata that points to it.
  • AccessBoundary enumerates Paid, Evaluation, and Internal. Every boundary requires authentication. Internal artifacts serve CI, QA, and staging and are never customer-facing.
  • PublishingPlan::fromProfiles resolves 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.
  • ReleaseStatus types the lifecycle: Built → Audited → Staged → Published, with Revoked and Superseded as terminal exits from Published. Only Published is customer-visible; canPromote is false from Published onward.
  • ReleaseManifest::validate and PublishingPlan::validate report consistency errors as string lists and never throw. An empty list is the only valid outcome.
  • BuildProfile::fromArray with a missing, non-string, or empty required field: InvalidArgumentException naming the field.
  • BuildProfile::fromArray with an empty or non-string ioncube_expire: normalized to null (non-expiring). Non-string or empty entries in encode_paths and exclude_paths are silently dropped.
  • PackageMapping::resolvePackageName with an edition outside the known map: InvalidArgumentException.
  • ReleaseManifest::toJson on unencodable data: JsonException. Serialization uses JSON_THROW_ON_ERROR; there is no silent fallback.
  • ReleaseManifest::validate reports an empty artifact list, and any artifact whose version or source commit differs from the release, as errors.
  • PublishingPlan::validate reports an empty target list, any target version diverging from the plan, and any evaluation package resolved into a paid boundary.
  • ReleaseStatus::nextStatus returns null from Published, Revoked, and Superseded. Callers must handle null; there is no wrap-around.
  • The origin and consumption-layer channels are distinct; do not point a composer require at 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.

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.

  • Prefer ReleaseManifest::createWithDefaults so the evidence filenames always agree with ArtifactNamer. 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::fromProfiles defaults to PublishingEnvironment::Staging. Pass Production explicitly; 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 (-evaluation suffix). 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.

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.