Skip to main content

Temporal Clients - PHP SDK

This guide introduces Temporal Clients. It explains the role and use of Clients and shows you how to configure your PHP Client code to connect to the Temporal Service.

The pages shows how to do the following:

How to connect a Temporal Client to a Temporal Service

A Temporal Client enables you to communicate with the Temporal Service. Communication with a Temporal Service includes, but isn't limited to, the following:

  • Scheduling Workflow Executions.
  • Starting Workflow Executions.
  • Sending Signals to Workflow Executions.
  • Sending Queries to Workflow Executions.
  • Getting the results of a Workflow Execution.
  • Providing an Activity Task Token.
caution

A Temporal Client cannot be initialized and used inside a Workflow. However, it is acceptable and common to use a Temporal Client inside an Activity to communicate with a Temporal Service.

When you are running a Temporal Service locally (such as the Temporal CLI), the number of connection options you must provide is minimal. Many SDKs default to the local host or IP address and port that Temporalite and Docker Compose serve (127.0.0.1:7233).

In the PHP SDK, different client classes are responsible for different functional areas. The ServiceClient is responsible for the low-level API and connection to the Temporal Service. It is also used in higher-level clients: WorkflowClient and ScheduleClient.

note

RoadRunner is not required to work only with the client API; however, the gRPC extension is necessary.

Use create() factory methods to create clients.

use Temporal\Client\GRPC\ServiceClient;
use Temporal\Client\WorkflowClient;

$serviceClient = ServiceClient::create('localhost:7233');
$workflowClient = WorkflowClient::create($serviceClient);

// Use $workflowClient to work with Workflows ...

How to connect a Temporal Client to a Temporal Cloud

When you connect to Temporal Cloud, you need to provide additional connection and client options that include the following:

For more information about managing and generating client certificates for Temporal Cloud, see How to manage certificates in Temporal Cloud.

For more information about configuring TLS to secure inter- and intra-network communication for a Temporal Service, see Temporal Customization Samples.

Use the ServiceClient::createSSL() method to configure a client connection to the Temporal Service. The $clientKey argument must be combined with the $clientPem to authenticate the Client.

use Temporal\Client\ClientOptions;
use Temporal\Client\GRPC\ServiceClient;
use Temporal\Client\WorkflowClient;

$serviceClient = \Temporal\Client\GRPC\ServiceClient::createSSL(
address: '<your-custom-namespace>.tmprl.cloud:7233',
// crt: 'certs/server-root-ca-cert.pem', # ROOT CA to validate the server cert
clientKey: 'certs/client-private-key.pem',
clientPem: 'certs/client-cert.pem',
// overrideServerName: 'tls-sample',
);

$workflowClient = WorkflowClient::create(
serviceClient: $serviceClient,
options: (new ClientOptions())
->withNamespace('<your-custom-namespace>.<id>'),
);

To run Worker processes managed by Temporal Cloud, configure RoadRunner in the same way.

temporal:
# ...
tls:
# root_ca: 'certs/server-root-ca-cert.pem'
key: 'certs/client-private-key.pem'
cert: 'certs/client-cert.pem'
client_auth_type: require_and_verify_client_cert
# server_name: 'tls-sample'

How to start a Workflow Execution

Workflow Execution semantics rely on several parameters—that is, to start a Workflow Execution you must supply a Task Queue that will be used for the Tasks (one that a Worker is polling), the Workflow Type, language-specific contextual data, and Workflow Function parameters.

In the examples below, all Workflow Executions are started using a Temporal Client. To spawn Workflow Executions from within another Workflow Execution, use either the Child Workflow or External Workflow APIs.

See the Customize Workflow Type section to see how to customize the name of the Workflow Type.

A request to spawn a Workflow Execution causes the Temporal Service to create the first Event (WorkflowExecutionStarted) in the Workflow Execution Event History. The Temporal Service then creates the first Workflow Task, resulting in the first WorkflowTaskScheduled Event.

Workflows can be started both synchronously and asynchronously. You can use typed or untyped Workflows stubs available via Temporal\Client\WorkflowClient. To create a Workflow Client:

use Temporal\Client\GRPC\ServiceClient;
use Temporal\Client\WorkflowClient;

$workflowClient = WorkflowClient::create(ServiceClient::create('localhost:7233'));

Synchronous start

A synchronous start initiates a Workflow and then waits for its completion. The started Workflow will not rely on the invocation process and will continue executing even if the waiting process crashes or stops.

Be sure to acquire the Workflow interface or class name you want to start. For example:

#[WorkflowInterface]
interface AccountTransferWorkflowInterface
{
#[WorkflowMethod(name: "MoneyTransfer")]
#[ReturnType('int')]
public function transfer(
string $fromAccountId,
string $toAccountId,
string $referenceId,
int $amountCents
);
}

To start the Workflow in sync mode:

$accountTransfer = $workflowClient->newWorkflowStub(
AccountTransferWorkflowInterface::class
);

$result = $accountTransfer->transfer(
'fromID',
'toID',
'refID',
1000
);

Asynchronous start

An asynchronous start initiates a Workflow Execution and immediately returns to the caller without waiting for a result. This is the most common way to start Workflows in a live environment.

To start a Workflow asynchronously, pass the Workflow stub instance and start parameters into the WorkflowClient->start method.

$accountTransfer = $workflowClient->newWorkflowStub(
AccountTransferWorkflowInterface::class
);

$run = $this->workflowClient->start($accountTransfer, 'fromID', 'toID', 'refID', 1000);

After the Workflow is started, you can receive the Workflow Id via the WorkflowRun object returned by the start method:

$run = $workflowClient->start($accountTransfer, 'fromID', 'toID', 'refID', 1000);

var_dump($run->getExecution()->getID());

Recurring start

You can start a Workflow Execution on a regular schedule with the CronSchedule option.

Advanced connection options

In PHP, it is common practice to work with resources in blocking mode. Long blocks can quickly exhaust the pool of available workers and lead to application failure.

This section introduces features and configuration examples of the PHP SDK when working with the Temporal Client API.

Connection

gRPC connections in the PHP SDK are lazy by default, meaning they are not established until the first call. To force establishing the connection to the Temporal Service, you can call the ConnectionInterface::connect() or ServiceClient::getServerCapabilities() method.

// ...
$serviceClient->getConnection()->connect(timeout: 10);

// or
$serviceClient->getServerCapabilities();

If, for some reason, the established connection is broken, the SDK will automatically attempt to restore it, taking into account the configured retry policy.

Retry policy

Whenever the client fails to connect to the server, an error with a status code is generated. If the status code is UNKNOWN, UNAVAILABLE, or RESOURCE_EXHAUSTED, the client will make another connection attempt. By default, the number of attempts is unlimited, and the interval between them will range from 0.5 to 100 seconds with a backoff coefficient of 2. This means that the client will likely be blocked until it establishes a connection to the server through infinite attempts.

If you want to change the default behavior, use the withRetryPolicy() method when creating a client service:

use Temporal\Client\Common\RpcRetryOptions;
use Temporal\Client\GRPC\ServiceClient;
use Temporal\Client\WorkflowClient;

$serviceClient = ServiceClient::create('localhost:7233');
$workflowClient = WorkflowClient::create($serviceClient)
->withRetryOptions(
RpcRetryOptions::new()
->withMaximumAttempts(10)
->withInitialInterval('1 second') // The first retry will be in 1 second
->withBackoffCoefficient(2.5) // Each next retry time will be multiplied by 2.5
->withMaximumInterval('20 seconds') // The maximum interval between attempts
->withMaximumJitterCoefficient(0.2) // Actual retry time can be +/- 20% of the calculated time
);

RPC timeout

When the client calls the service's RPC, there is no default time limit for waiting for a response. This can result in the code call $result = $workflowHandle->getResult(); blocking the PHP worker until the Workflow completes. In some cases, this is not the desired behavior, and there may be a need to set a reasonable timeout for waiting for the RPC to complete.

Use the withTimeout() method to build a client with a timeout for all RPC calls.

use Temporal\Client\GRPC\ServiceClient;
use Temporal\Client\WorkflowClient;

$serviceClient = ServiceClient::create('localhost:7233');
$workflowClient = WorkflowClient::create($serviceClient)
->withTimeout(5.75);

// Create a Workflow stub
$stub = $workflowClient->newWorkflowStub(AccountTransferWorkflowInterface::class);

// If the Workflow does not complete within 5.75 seconds, a TimeoutException will be thrown
$result = $accountTransfer->transfer('fromID', 'toID', 'refID', 1000);
note

The withTimeout() method is immutable. If you need to change the timeout for individual operations, create a new client from the existing one with a specific timeout: $newClient = $workflowClient->withTimeout(0); (0 means no timeout).