Cli: command handlers and external-validator adapters
At a glance
Section titled “At a glance”The command-line interface (CLI) module provides the engine’s diagnostics and
conformance command surface. It includes handlers for benchmark, diff, init,
verify, and capabilities commands, plus adapters that wrap external Portable
Document Format (PDF) validators, including veraPDF and the Arlington PDF model,
behind one interface. A single verify command can run any adapter.
Install
Section titled “Install”composer require nextpdf/core:^3Conceptual overview
Section titled “Conceptual overview”Each command uses a handler class whose execute() method returns a process
exit code. BenchmarkHandler runs benchmark scenarios. DiffHandler compares
documents. InitHandler scaffolds a project. VerifyHandler runs conformance
verification. CapabilitiesHandler reports runtime support. CliOutput is the
thin stdout/stderr writer shared by the handlers, so output stays testable
instead of coupled to globals. BinaryFinder resolves an external tool path on
the host (@since 2.5.0).
The validation surface is the key architectural boundary.
AlternateValidatorAdapter is the interface an external validator implements.
validate() takes a PDF path and a ComplianceFlavour and returns a
ComplianceValidationResult. isAvailable() reports whether the backing tool
is installed. toolIdentifier() names it. VeraPdfCliAdapter,
ArlingtonValidatorAdapter, and AsyncValidatorAdapter implement it. The
engine does not re-implement third-party validators. It runs the reference
tools and normalizes their verdict. A validator that is not installed reports
isAvailable() === false instead of failing the run, so verification records
the missing tool explicitly. The adapters are @since 3.0.0; the core handlers are
@since 2.3.0–@since 2.5.0.
API surface
Section titled “API surface”| Class | Key members | Role |
|---|---|---|
BenchmarkHandler | execute(string $format = 'pretty', ?string $scenario = null): int | Runs benchmark scenarios (@since 2.4.0) |
VerifyHandler | execute(): int | Drives conformance verification |
DiffHandler / InitHandler | execute(): int | Document diff / project scaffold |
CapabilitiesHandler | execute(string $format = 'pretty'): int | Reports runtime capabilities (@since 2.3.0) |
AlternateValidatorAdapter (interface) | validate(), isAvailable(), toolIdentifier() | External-validator contract (@since 3.0.0) |
VeraPdfCliAdapter | implements the adapter | Wraps the veraPDF CLI (@since 3.0.0) |
ArlingtonValidatorAdapter | implements the adapter | Wraps the Arlington PDF model (@since 3.0.0) |
AsyncValidatorAdapter | implements the adapter | Async-capable validator wrapper (@since 3.0.0) |
CliOutput | write(), writeln(), error() | Testable stdout/stderr writer (@since 2.3.0) |
BinaryFinder | external-tool path resolution | Locates host tools (@since 2.5.0) |
For the full PHPDoc table, run composer docs:generate-api-php -- --module=Cli.
Code sample — Quick start
Section titled “Code sample — Quick start”Source: examples/33-validate-conformance.php. Select a validator adapter and
check availability before use:
<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Cli\VeraPdfCliAdapter;use NextPDF\Compliance\ComplianceFlavour;
$validator = new VeraPdfCliAdapter(/* binary path / process factory */);
if (!$validator->isAvailable()) { fwrite(STDERR, "veraPDF is not installed; conformance verification skipped.\n"); exit(2);}
$result = $validator->validate('/srv/out/report.pdf', ComplianceFlavour::PdfA4);echo $result->isCompliant() ? "PASS\n" : "FAIL\n";Code sample — Production
Section titled “Code sample — Production”Run verification through whichever validators are present. Treat an absent tool as a skipped check, not as a failure.
<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Cli\AlternateValidatorAdapter;use NextPDF\Compliance\ComplianceFlavour;use Psr\Log\LoggerInterface;
final readonly class ConformanceGate{ /** @param list<AlternateValidatorAdapter> $validators */ public function __construct( private array $validators, private LoggerInterface $logger, ) {}
public function verify(string $pdfPath, ComplianceFlavour $flavour): bool { $ran = false;
foreach ($this->validators as $validator) { if (!$validator->isAvailable()) { $this->logger->info('Validator absent; skipped.', ['tool' => $validator->toolIdentifier()]); continue; }
$ran = true;
if (!$validator->validate($pdfPath, $flavour)->isCompliant()) { $this->logger->error('Conformance failed.', ['tool' => $validator->toolIdentifier()]);
return false; } }
// No validator available is not a pass — surface it. return $ran; }}Edge cases & gotchas
Section titled “Edge cases & gotchas”- An unavailable validator returns
isAvailable() === falseand does not throw. “No validator available” is not a pass; handle it as a separate state, as the production sample does. - The adapters run external binaries. Their verdict is the external tool’s normalized verdict, not an independent re-implementation. Keep the tools current.
- Handler
execute()returns a process exit code. A non-zero code is a failure. Propagate it from your wrapper rather than discarding it. BinaryFinderresolves a tool path on the host. A different host can resolve a different tool version. Pin the environment for reproducible verification.- The reproducibility profile is
structural: verification reports include timestamps and tool versions, so those fields differ across runs.
Performance
Section titled “Performance”Handler overhead is negligible. External validator processes dominate cost, and
large documents can be slow. AsyncValidatorAdapter lets you overlap that
latency. The performance_budget of 1500 ms wall / 64 MB peak is the engine
reference, not a limit for an external validator. Benchmark output has a
deterministic structure, but it necessarily includes timing data.
Security notes
Section titled “Security notes”The adapters launch external processes for a PDF path. Treat the PDF as
untrusted input, and run verification in a constrained environment. External
validators parse hostile data. Validate and canonicalize any file path before
you pass it to a handler, so path traversal cannot reach an unintended file. Do
not pass unsanitized user input as command arguments. The adapters build process
arguments, not shell strings. The input file itself remains attacker-controlled.
See the engine threat model in /modules/core/security/.
Conformance
Section titled “Conformance”This module makes no PDF-specification normative claim of its own. It
orchestrates conformance verification by delegating to reference validators:
veraPDF for PDF/A and PDF/UA, and the Arlington PDF model for ISO 32000-2
structural rules. The external tool provides the authoritative conformance
verdict. This module normalizes and reports that verdict. End-to-end
conformance and the golden baselines are described in /modules/core/conformance/.
See also
Section titled “See also”- Inspect module — programmatic introspection exposed through the CLI.
- Conformance overview — the verification model and golden suites.
- Compliance module — the
ComplianceFlavourprofiles checked by validators. - Engine security model