pilotphp / runtime-native
Synchronous in-process runtime adapter for PilotPHP.
Requires
- php: ^8.5
- pilotphp/contracts: ^0.1.4
- pilotphp/runtime: ^0.1.4
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.75
- phpstan/phpstan: ^2.1
- phpstan/phpstan-phpunit: ^2.0
- phpunit/phpunit: ^11.5
- pilotphp/container: ^0.1.4
- pilotphp/core: ^0.1.4
README
A finite, synchronous, in-process runtime adapter for PilotPHP.
NativeRuntime boots one kernel, pulls transport-neutral invocations from a
finite source, delivers each outcome to a sink, and shuts the kernel down. It
is intended for development, tests, command-line jobs, and small controlled
integrations. It is not a production server or worker runtime: it has no
transport, blocking receive loop, concurrency, signals, retries, daemon
supervision, or terminal UI.
Installation
composer require pilotphp/runtime-native
The package requires PHP 8.5, pilotphp/contracts ~0.0.2, and currently
pilotphp/runtime dev-main. The development constraint is a deliberate
pre-release deviation until a compatible pilotphp/runtime release exists;
see Integration.
Package discovery
This package is discoverable but performs no discovery. It publishes the metadata chain that external PilotPHP build tooling walks:
composer.json
→ extra.pilotphp.manifest
→ pilot/package.json
→ entrypoint
→ PilotPHP\RuntimeNative\NativeRuntimePackage
→ descriptor()
pilot/package.json is the machine-readable manifest (pilotphp.package.v1).
It names NativeRuntimePackage as the entrypoint, and that class's
descriptor() publishes the package's dependencies (pilotphp/contracts,
pilotphp/runtime) and its capabilities (runtime.adapter.native,
runtime.in-process, runtime.synchronous).
Walking that chain is the build tooling's job, not this package's. Nothing in
src/ reads composer.json, the manifest, Composer's installed metadata, or
the filesystem — an architecture test enforces it.
Discovery is deliberately inert:
- Discovering the package constructs no
NativeRuntime. It selects no source or sink, builds no kernel, and starts noWorkerLifecycle. - Activation does not run anything. Passing a
NativeRuntimePackageinstance to a composition makes it an active graph node and nothing more. - Capabilities are claims, not choices. If several active packages provide a runtime capability, an explicit capability binding picks the provider. There is no priority, first-found, or last-wins rule.
- The application supplies the collaborators. A source, a sink, and a request scope come from the application bootstrap, because none of them is derivable from package metadata.
run()remains the only way to start the runtime.
See Integration for the full sequence and Public API for the per-type contract.
Runnable example
Save this as example.php in a Composer project that also provides
pilotphp/container, then run php example.php:
<?php
declare(strict_types=1);
require __DIR__ . '/vendor/autoload.php';
use PilotPHP\Container\Runtime\RequestScope;
use PilotPHP\Contracts\Kernel\KernelInterface;
use PilotPHP\Contracts\Runtime\InvocationInterface;
use PilotPHP\Runtime\Context\RequestContext;
use PilotPHP\Runtime\Invocation\Invocation;
use PilotPHP\RuntimeNative\Result\NullResultSink;
use PilotPHP\RuntimeNative\Runtime\NativeRuntime;
use PilotPHP\RuntimeNative\Source\SingleInvocationSource;
final readonly class Add
{
public function __construct(public int $left, public int $right) {}
}
final readonly class Sum
{
public function __construct(public int $value) {}
}
$kernel = new class implements KernelInterface {
public function boot(): void {}
public function reset(): void {}
public function shutdown(): void {}
public function invoke(InvocationInterface $invocation): object
{
/** @var Add $input */
$input = $invocation->input();
return new Sum($input->left + $input->right);
}
};
$invocation = new Invocation(
'math.add',
new Add(20, 22),
new RequestContext('example-1'),
);
$runtime = new NativeRuntime(
new SingleInvocationSource($invocation),
new NullResultSink(),
new RequestScope(),
);
exit($runtime->run($kernel));
The example intentionally prints nothing. NativeRuntime never writes to
STDOUT or STDERR; observe its integer return value, summary(), lastError(),
or provide a result sink.
Public API
NativeRuntimeimplementsRuntimeInterface;run()is one-shot.InvocationSourceInterface::next()returns the next invocation ornullwhen its finite, non-blocking input is exhausted.EmptyInvocationSource,SingleInvocationSource, andIterableInvocationSourceare supplied source implementations.InvocationResultSinkInterfacereceives success and failure outcomes.NullResultSinkdiscards them.InvocationFailurePolicy::Stopis the default;InvocationFailurePolicy::Continuecontinues only after ordinary failures on a still-healthy worker.NativeRuntimeSummaryreports the final exit code, counters, and worker poisoning.InvocationSuccessandInvocationFailureare optional compact result records for custom sinks; the runtime does not construct or store them.
Source and sink interfaces are the supported replacement points. They replace
where work comes from and where outcomes go; they do not replace lifecycle
orchestration. The runtime integrates with the kernel exclusively through
WorkerLifecycle.
Exit values
| Value | Enum case | Meaning |
|---|---|---|
| 0 | Success | Source exhausted and every delivered invocation succeeded |
| 1 | InvocationFailure | At least one ordinary invocation failed |
| 2 | BootFailure | Worker boot failed; shutdown was not attempted |
| 3 | Poisoned | Processing failed and worker health became poisoned |
| 4 | ShutdownFailure | Shutdown failed; this overrides the earlier exit value |
| 5 | SourceFailure | Reading the next invocation threw |
| 6 | ResultSinkFailure | Delivering a success or failure outcome threw |
With Continue, an ordinary failure still makes the final value 1 even if
later invocations succeed. Source, sink, poisoning, boot, and shutdown failures
always stop. Details are in Error policy.
Documentation
- Architecture
- Public API
- Lifecycle and state machine
- Sources, sinks, results, and counters
- Error policy
- Integration
- Performance
Development
make install
make check
make benchmark
make check validates Composer metadata, checks style, runs PHPStan, and runs
the tests. Benchmarks are measurements, not release gates.