A PHP HTTP client for Temporal that works on any PHP host. One composer require, three lines of code, durable workflows. No PECL extensions, no RoadRunner, no persistent processes. If your PHP can make an HTTP request, it can talk to Temporal.
The killer pattern for LAMP: a Stripe webhook completes a long-running Temporal activity. Your PHP app is the cockpit; the workers live anywhere.
PHP — webhook handler (any framework):
<?php
// In any PHP framework's webhook route — Laravel, WordPress, Drupal, plain PHP
$temporal = Temporal\Http\Client::cloud(
namespace: $_ENV['TEMPORAL_NAMESPACE'],
apiKey: $_ENV['TEMPORAL_API_KEY'],
);
$event = json_decode(file_get_contents('php://input'), true);
if ($event['type'] === 'payment_intent.succeeded') {
$temporal->completeActivity(
workflowId: $event['data']['object']['metadata']['workflow_id'],
activityId: $event['data']['object']['metadata']['activity_id'],
result: ['paymentId' => $event['data']['object']['id']],
);
}
http_response_code(200);Go — worker side (shown for context):
func OrderWorkflow(ctx workflow.Context, orderID string) error {
ao := workflow.ActivityOptions{
ActivityID: "stripe-payment-" + orderID,
StartToCloseTimeout: 24 * time.Hour,
}
ctx = workflow.WithActivityOptions(ctx, ao)
var result PaymentResult
err := workflow.ExecuteActivity(ctx, WaitForStripePayment, orderID).Get(ctx, &result)
if err != nil {
return err
}
// ... continue workflow with result.PaymentId
return nil
}
func WaitForStripePayment(ctx context.Context, orderID string) (PaymentResult, error) {
activity.RecordHeartbeat(ctx, "waiting for stripe webhook")
return PaymentResult{}, activity.ErrResultPending // PHP will complete this via HTTP
}What's happening: The Go worker registers an activity that immediately returns
ErrResultPending, parking the workflow until an external caller completes it. Your PHP webhook handler callscompleteActivity()over HTTP when Stripe posts the payment event. No PHP worker process, no persistent connection — just a single HTTP POST from your existing LAMP stack.
Note: This package is not yet listed on Packagist. Once registered, installation will be:
composer require webchick/temporal-http-php
Until then, install directly from GitHub by adding this to your composer.json:
{
"repositories": [
{
"type": "vcs",
"url": "https://github.com/webchick/temporal-http-php"
}
],
"require": {
"webchick/temporal-http-php": "dev-main"
},
"minimum-stability": "dev",
"prefer-stable": true
}Then run composer install.
Requires PHP 8.1+. No PECL extensions. Guzzle is suggested but any PSR-18 HTTP client works.
You need a Temporal server to talk to. The fastest way to get one locally is the Temporal CLI.
Install the Temporal CLI:
# macOS
brew install temporal
# Linux / WSL
curl -sSf https://temporal.download/cli.sh | sh
# Then add ~/.temporalio/bin to your PATH
# Windows (PowerShell)
winget install Temporal.CLIStart the local dev server:
$ temporal server start-dev
CLI 1.0.0 (Server 1.24.0, UI 2.26.0)
Server: localhost:7233
UI: http://localhost:8233
When you see those lines, the server is ready. Data is in-memory and lost on restart — expected for local dev. The default namespace is created automatically.
Open http://localhost:8233 to see the Web UI, where you can inspect running workflows, view history, and debug.
If you're using Temporal Cloud instead of a local server, skip this section. You'll need a namespace and API key from cloud.temporal.io.
Local dev server:
<?php
require 'vendor/autoload.php';
$temporal = Temporal\Http\Client::selfHosted(
baseUri: 'http://localhost:7233',
namespace: 'default',
);
// Start a workflow
$handle = $temporal->startWorkflow(
workflowType: 'GreetingWorkflow',
workflowId: 'hello-' . uniqid(),
taskQueue: 'greeting',
args: [['name' => 'World']],
);
echo "Started: " . $handle->workflowId() . " / " . $handle->runId() . PHP_EOL;
// Wait for the result (long-polls until the workflow completes)
$result = $handle->result(timeoutSeconds: 30);
echo "Result: " . json_encode($result) . PHP_EOL;Temporal Cloud:
<?php
require 'vendor/autoload.php';
$temporal = Temporal\Http\Client::cloud(
namespace: getenv('TEMPORAL_NAMESPACE'), // e.g. "my-app.a1b2c"
apiKey: getenv('TEMPORAL_API_KEY'),
);
$handle = $temporal->startWorkflow(
workflowType: 'OrderFulfillmentWorkflow',
workflowId: 'order-' . $orderId,
taskQueue: 'fulfillment',
args: [['orderId' => $orderId, 'items' => $items]],
);
echo "Started: " . $handle->workflowId() . " / " . $handle->runId() . PHP_EOL;
$result = $handle->result(timeoutSeconds: 30);
echo "Result: " . json_encode($result) . PHP_EOL;Note: The quickstart assumes a worker is already registered for the workflow type and task queue you reference. The PHP library is a client — it drives workflows, but does not run them. See What this isn't for details.
A worker is a process that actually runs your workflow code. Think of it as the back end of Temporal: it polls for tasks, executes the workflow logic, and reports results back. Your PHP code is the front end — it triggers and observes workflows but never runs them directly.
To see a round-trip, you need a worker running alongside your PHP script. Here's a minimal one using the Temporal TypeScript SDK — if you've ever used npm for front-end tooling, you already have everything you need:
1. Create a new directory and install dependencies:
mkdir greeting-worker && cd greeting-worker
npm init -y
npm install @temporalio/worker @temporalio/workflow
npm install --save-dev typescript ts-node2. Save these two files:
workflows.ts — the workflow logic:
export async function GreetingWorkflow(input: { name: string }): Promise<string> {
return `Hello, ${input.name}!`;
}worker.ts — the process that runs it:
import { Worker } from '@temporalio/worker';
async function run(): Promise<void> {
const worker = await Worker.create({
workflowsPath: require.resolve('./workflows'),
taskQueue: 'greeting',
});
console.log('Worker running. Press Ctrl+C to stop.');
await worker.run();
}
run().catch(err => { console.error(err); process.exit(1); });The workflow lives in its own file because Temporal runs it in an isolated sandbox to guarantee determinism. The worker file is just the harness that connects it to the server.
3. Open three terminals:
| Terminal | Command | What it does |
|---|---|---|
| 1 | temporal server start-dev |
Runs the Temporal server |
| 2 | npx ts-node worker.ts (inside greeting-worker/) |
Executes your workflow logic |
| 3 | php quickstart.php |
Triggers the workflow from PHP |
Terminal 3 should print Result: "Hello, World!". Open http://localhost:8233 to see the full execution timeline in the Web UI.
Workers can be written in any language with a Temporal SDK — Go, Python, TypeScript, Java, .NET. Your PHP client talks to any of them without modification.
// TODO: placeholder — add Laravel controller example// TODO: placeholder — see "60-second example" above for full walkthrough// TODO: placeholder — add WordPress admin example using listWorkflows() / queryWorkflow()$temporal = Temporal\Http\Client::cloud(
namespace: $_ENV['TEMPORAL_NAMESPACE'], // e.g. "my-app.a1b2c"
apiKey: $_ENV['TEMPORAL_API_KEY'],
);Recommended env vars: TEMPORAL_NAMESPACE, TEMPORAL_API_KEY.
$temporal = Temporal\Http\Client::selfHosted(
baseUri: 'http://localhost:7233',
namespace: 'default',
);$temporal = Temporal\Http\Client::selfHostedMtls(
baseUri: 'https://temporal.internal:7233',
namespace: 'production',
certPath: '/etc/temporal/client.pem',
keyPath: '/etc/temporal/client.key',
caPath: '/etc/temporal/ca.pem', // optional
);Full method-level documentation is generated from docblocks. See the generated reference at:
TODO: link to generated API docs (e.g. GitHub Pages / php.watch)
For a quick overview, see src/ClientInterface.php.
This is a client library — it lets PHP applications trigger and interact with Temporal workflows. It does not:
- Let you author workflows in PHP. For that, use the official
temporalio/sdk-php(requiresext-grpcand RoadRunner). - Let you run PHP activity workers. Activities must live in a language with a full SDK (Go, Python, TypeScript, Java, .NET). Framework integration packages (
temporal-http-php-laravel, etc.) are planned but not yet built. - Cover the Operator Service (namespace administration, cluster management).
If you need to write workflow logic in PHP itself, temporalio/sdk-php is the right tool. If you're on a shared host or a traditional LAMP stack and just need to drive Temporal from PHP, this library is for you.
webchick/temporal-http-php |
temporalio/sdk-php |
|
|---|---|---|
| Works on shared hosting | Yes | No |
Requires ext-grpc |
No | Yes |
| Requires RoadRunner | No | Yes |
| Start / signal / query workflows | Yes | Yes |
| Author workflow logic in PHP | No | Yes |
| Run PHP activity workers | No | Yes |
| Async activity completion | Yes | Yes |
| Schedules | Yes | Yes |
| Dependencies | PSR-18 only | grpc, protobuf, RoadRunner |
| PHP version | 8.1+ | 8.1+ |
TODO: placeholder — add CONTRIBUTING.md link and development setup instructions.
MIT — see LICENSE.