Download the PHP package qcodr/restate-sdk-php without Composer
On this page you can find all versions of the php package qcodr/restate-sdk-php. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download qcodr/restate-sdk-php
More information about qcodr/restate-sdk-php
Files in qcodr/restate-sdk-php
Package restate-sdk-php
Short Description PHP SDK for Restate (restate.dev); Durable execution for services, virtual objects and workflows.
License Apache-2.0
Homepage https://github.com/qcodr/restate-sdk-php
Informations about the package restate-sdk-php
Restate PHP SDK
A pure-PHP SDK for Restate — durable execution for Services, Virtual Objects, and Workflows. It mirrors the Rust SDK surface with idiomatic PHP: attributes for service definitions, a typed context API, and a true bidirectional HTTP/2 streaming server.
The Restate service protocol (v5–v7) is implemented from scratch in pure PHP —
framing, protobuf messages, the journal/replay state machine, suspension, and
signals — so the SDK has no native-extension dependency: the default server
(AmpStreamingServer) runs on pure-PHP amphp/http-server, and
a request/response Swoole server, a PSR-15 adapter, and an AWS Lambda handler are
available as alternative transports.
Features
- Services — stateless handlers, unlimited concurrency.
- Virtual Objects — per-key state with single-writer (
#[Handler]) and concurrent read-only (#[Shared]) handlers. - Workflows — exactly-once
runhandler plus interaction handlers, with durable promises. - Durable building blocks —
run(side effects),sleep(durable timers), service/object/workflow calls and one-way sends (with delay), awakeables, deterministic randomness, andselect/awaitAllcombinators. - Deterministic replay — every interaction is journaled; handlers replay faithfully after failures.
Requirements
- PHP 8.2+ (
ext-json,ext-mbstring) amphp/http-serverto run the default bidirectional-streaming server (orext-swoolefor the request/response Swoole server)- Docker + Docker Compose for end-to-end testing
Installation
Quick start
Define services with attributes; the first parameter is always the context.
Serve them over true bidirectional HTTP/2 streaming (the default server):
Register the deployment with a running Restate server, then invoke through the ingress:
Drop
->protocolMode(ProtocolMode::BidiStream)(and add--use-http1.1when registering) to serve plain request/response over the same amphp host, or swap inSwooleServer/ the PSR-15 / Lambda adapters — see Transports below.
Workflows & durable promises
Context API
Service classes must be stateless. A bound service instance is shared across concurrent invocations within the server process — keep per-invocation data in local variables or Restate state (
$ctx->set(...)), never in mutable instance properties.
| Capability | Methods |
|---|---|
| Side effects | run(name, fn, ?RunOptions) — with optional per-run RetryPolicy |
| Timers | sleep(seconds), timer(seconds) → DurableFuture |
| Calls (await) | serviceCall, objectCall, workflowCall (+ idempotencyKey, headers) |
| Calls (async) | serviceCallAsync, objectCallAsync, workflowCallAsync → DurableFuture |
| Calls (handle) | serviceCallHandle, … → CallHandle (result(), invocationId()) |
| One-way sends | serviceSend, objectSend, workflowSend (optional delay) |
| Cancellation | cancel(invocationId) — peer cancel; observed as CancelledException (409) |
| Combinators | select, awaitAll, awaitAny (any), awaitAllSucceeded (all-or-fail) |
| Tracing | traceContext() — W3C trace context (bridge to OpenTelemetry) |
| Awakeables | awakeable(), resolveAwakeable, rejectAwakeable |
| State (objects) | get, set, clear, clearAll, stateKeys |
| Promises (wf) | promise, peekPromise, resolvePromise, rejectPromise |
| Request meta | key(), invocationId(), requestHeaders(), requestIdempotencyKey() |
| Randomness | random()->uuidV4(), randomInt, randomFloat |
| Logging | logger() — a replay-aware PSR-3 logger |
Errors: throw TerminalException for a non-retryable failure returned to the
caller; RetryableException (optionally pause: true or a retryDelayMillis) for a
tuned transient failure; any other throwable is a plain transient error (retried).
Logging & tracing
ctx->logger() returns a PSR-3 logger that suppresses records emitted during
replay, so each line is logged exactly once even though handlers re-run from the top
on every slice. Provide the underlying logger (e.g. Monolog) when constructing the
server: new AmpStreamingServer($endpoint, logger: $myLogger) (defaults to a null
logger).
For distributed tracing, mind the propagation boundary:
- Across the service graph (the services your handler calls or sends to) — the
Restate runtime propagates the trace. It stamps
traceparenton the request it sends the SDK and links child invocations itself. Do not manually forwardtraceparentonctx->serviceCall(...)headers; doing so forks the trace. - Inside one handler (spans around your own DB/HTTP/compute work) — that's yours.
ctx->traceContext()exposes the inbound W3C context (traceId,spanId(),isSampled(),toTraceparent()) so your spans nest under the incoming trace.
The SDK stays dependency-free and emits no spans itself. Install open-telemetry/sdk
and use the withIncomingTraceParent() bridge in examples/tracing.php to start spans
under the incoming trace.
Production configuration
Discovery options. Configure per-service / per-handler behavior the runtime reads from the manifest (negotiated up to schema v4):
Request identity verification (opt-in; requires ext-sodium). Reject requests
not signed by your Restate instance's key:
Transports. The default AmpStreamingServer (pure-PHP amphp) serves true
bidirectional HTTP/2 streaming. One amphp process is a single event loop; pass a worker
count to pre-fork N processes that share the port via SO_REUSEPORT (needs ext-pcntl)
and scale across cores like a Swoole worker pool:
Its connection, concurrency and idle ceilings default to values sized for the Restate
runtime as the only peer — many long-lived, deliberately idle streams from one IP, which
amphp's own defaults reject. That is not a general hardening posture: if the endpoint is
reachable by anything other than your runtime (especially without identityKey()),
tighten them:
The same framework-agnostic core is also hostable request/response via the Swoole
server (Qcodr\Restate\Sdk\Server\SwooleServer, needs ext-swoole), a PSR-15 adapter
(Qcodr\Restate\Sdk\Server\Psr15Handler) in any Slim/Mezzio stack, on AWS Lambda
(Qcodr\Restate\Sdk\Server\LambdaHandler — Function URL / API Gateway proxy), and directly
via RequestProcessor (bytes in → bytes out).
Typed clients. bin/restate-codegen <ServiceClass> [outDir] [namespace] generates
an IDE-autocompletable client so callers write
GreeterClient::fromContext($ctx)->greet('world') instead of stringly-typed
$ctx->serviceCall('Greeter','greet','world'). The discovery manifest also carries a
JSON Schema for each handler's input/output, derived from the PHP types.
Serde. JSON is the default; BytesSerde provides raw octet-stream passthrough.
Inject a custom Serde into the server/processor for other formats.
Examples
The examples/ directory ports the Rust SDK's examples to PHP. Each file is a
self-contained, runnable endpoint.
| Example | Shows |
|---|---|
greeter.php |
the simplest stateless service |
counter.php |
Virtual Object state (get / add / increment / reset) |
run.php |
durable side effects (ctx->run) around an HTTP call |
failures.php |
terminal (no-retry) vs transient (retried) errors |
fan_out.php |
concurrent durable timers via timer() + select() |
schema.php |
structured JSON input/output + scalars |
cron.php |
a periodic task that re-schedules itself with delayed sends |
services.php |
the canonical Service + Virtual Object + Workflow trio |
tracing.php |
replay-aware PSR-3 logging (run standalone: php examples/tracing.php) |
Run a single example with the bundled server (amphp; the per-example endpoints are
request/response, so --use-http1.1 is fine):
Or bring all of them up live (Docker) over true bidi HTTP/2, against a real runtime:
Testing
Unit tests run anywhere (no extensions, no Docker):
End-to-end verification is the official cross-SDK conformance suite
(restatedev/e2e) — the same
battery every Restate SDK runs. It boots a real Restate runtime + a PHP image of the
standard test-services and drives them (needs JDK ≥ 21 + an AVX2 host):
The default config passes 48 / 49 over the bidi (amphp) transport on a V7-enabled
runtime — Cancellation 6/6, KillInvocation 1/1, Signals 2/2 included. It also runs
in CI (conformance.yml); an AVX2-free offline
fallback is documented in conformance/README.md.
To try the example services live by hand:
Code quality
The project ships a strict static-analysis, coding-standard, and security gate — all of it runs fully offline (no cloud/SaaS):
- PHPStan at the max level over
srcandtests(ext-swoole is stubbed instubs/). - PHP-CS-Fixer with PSR-12 + risky strictness rules (
declare(strict_types=1), strict comparisons, strict params, namespaced native calls). - Psalm taint analysis as the offline SAST engine — traces untrusted input (request bytes, CLI args) to dangerous sinks (dynamic include, eval, exec, SQL). Type quality is owned by PHPStan, so Psalm's errorLevel is kept permissive and it focuses purely on security taint flows.
The Compose file pins
restatedev/restate:1.5.2(the last AVX2-free image, so it runs on older hardware); it already serves the bidi examples. Override withRESTATE_IMAGE=...for a newer runtime — V7 cancellation/signals over bidi needs ≥ 1.7 (which needs AVX2), as covered inconformance/README.md.
Architecture
The framework-agnostic RequestProcessor (bytes in → bytes out) is the testable
core; each server is one swappable transport. The default AmpStreamingServer
advertises BIDI_STREAM: the runtime keeps the invocation channel open in both
directions, streaming the journal and late completions/signals, so a parked await is
resumed on the next result instead of writing a suspension. The request/response
transports (SwooleServer, PSR-15, Lambda) advertise REQUEST_RESPONSE instead: the
SDK processes one slice and suspends when it awaits a result it does not yet have, and
the runtime re-invokes with a longer journal so the handler replays from the top.
License
Apache-2.0
All versions of restate-sdk-php with dependencies
ext-json Version *
ext-mbstring Version *
psr/http-factory Version ^1
psr/http-message Version ^2
psr/http-server-handler Version ^1
psr/log Version ^3.0