Skip to main content

Developer's guide - Foundations

The Foundations section of the Temporal Developer's guide covers the minimum set of concepts and implementation details needed to build and run a Temporal ApplicationLink preview iconWhat is a Temporal Application

A Temporal Application is a set of Workflow Executions.

Learn more—that is, all the relevant steps to start a Workflow Execution that executes an Activity.

WORK IN PROGRESS

This guide is a work in progress. Some sections may be incomplete or missing for some languages. Information may change at any time.

If you can't find what you are looking for in the Developer's guide, it could be in older docs for SDKs.

In this section you can find the following:

Run a development Cluster

The following sections list various methods of deploying your Temporal ClustersLink preview iconWhat is a Temporal Cluster?

A Temporal Cluster is the Temporal Server paired with persistence.

Learn more locally, so that you can use and interact with the Temporal ClientLink preview iconWhat is a Temporal Client

A Temporal Client, provided by a Temporal SDK, provides a set of APIs to communicate with a Temporal Cluster.

Learn more APIs and tctl commands to test and develop applications.

The following sections list methods for deploying your Temporal development Clusters.

  • Temporalite: This distribution of Temporal runs as a single process with zero runtime dependencies.
  • Docker: Using Docker Compose simplifies developing your Temporal Application.
  • Gitpod: One-click deployments are available for Go and TypeScript.

For information on deploying a production environment, see the Temporal Cloud documentation.

Temporalite

Temporalite is a distribution of Temporal that runs as a single process with zero runtime dependencies. It supports persistence to disk and in-memory mode through SQLite.

Prerequisites

Temporalite requires Go 1.18 or later.

Build and start Temporalite

The following steps start and run a Temporal Cluster.

  1. Build from source.
    git clone https://github.com/temporalio/temporalite.git
    cd temporalite
    go build ./cmd/temporalite
  2. Start Temporalite by using the start command.
    ./temporalite start --namespace default
    Replace default with your Namespace Name.

Results: You should have Temporal Cluster running at http://127.0.0.1:7233 and the Temporal Web UI at http://127.0.0.1:8233.

Docker Compose

Use Docker Compose and Temporal Cluster Docker images to quickly install and run a Temporal Cluster locally while developing Temporal Applications.

Prerequisites

Install Docker and Docker Compose.

Clone the repo and run Docker Compose

The following steps start and run a Temporal Cluster using the default configuration.

  1. Clone the temporalio/docker-compose repository.
    git clone https://github.com/temporalio/docker-compose.git
  2. Change to the directory for the project.
    cd docker-compose
  3. From your project directory, start your application.
    docker compose up

Results: You should have Temporal Cluster running at http://127.0.0.1:7233 and the Temporal Web UI at http://127.0.0.1:8080.

To try other configurations (different dependencies and databases), or to try a custom Docker image, follow the temporalio/docker-compose README.

Gitpod

Run a Temporal Cluster and develop Temporal Applications in your browser using Gitpod.

One-click deployments are available for the temporalio/samples-go repo and the temporalio/samples-typescript repo.

A one-click deployment starts a Temporal Cluster using a Temporal Cluster Docker image, starts a Worker Process, and starts one of the application's sample Workflows.

A one-click deployment can take up to a full minute to get fully up and running. When it is running, you can customize the application samples.

Install a Temporal SDK

A Temporal SDKLink preview iconWhat is a Temporal SDK?

A Temporal SDK is a language-specific library that offers APIs to construct and use a Temporal Client to communicate with a Temporal Cluster, develop Workflow Definitions, and develop Worker Programs.

Learn more provides a framework for Temporal ApplicationLink preview iconWhat is a Temporal Application

A Temporal Application is a set of Workflow Executions.

Learn more development.

An SDK provides you with the following:

CI Status Stable Release FOSSA Status

The Temporal PHP SDK is available as composer package and can be installed using the following command in a root of your project:

composer require temporal/sdk

The Temporal PHP SDK requires the RoadRunner 2.0 application server and supervisor to run Activities and Workflows in a scalable way.

Install RoadRunner manually by downloading its binary from the release page.

Or install RoadRunner through the CLI:

composer require spiral/roadrunner:v2.0 nyholm/psr7
./vendor/bin/rr get-binary

API reference

Each SDK has its own API reference. Select a programming language and follow the link to be taken to that reference page.

Content is planned but not yet available.

The information you are looking for may be found in the legacy docs.

Code samples

You can find a complete list of executable code samples in Temporal's GitHub repository.

Additionally, several of the Tutorials are backed by a fully executable template application.

Connect to a Cluster

A Temporal ClientLink preview iconWhat is a Temporal Client

A Temporal Client, provided by a Temporal SDK, provides a set of APIs to communicate with a Temporal Cluster.

Learn more enables you to communicate with the Temporal ClusterLink preview iconWhat is a Temporal Cluster?

A Temporal Cluster is the Temporal Server paired with persistence.

Learn more. Communication with a Temporal Cluster includes, but isn't limited to, the following:

  • 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 Cluster.

When you are running a Cluster locally (such as TemporaliteLink preview iconHow to quickly install a Temporal Cluster for testing and local development

There are four ways to quickly install and run a Temporal Cluster.

Learn more), 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 ComposeLink preview iconHow to quickly install a Temporal Cluster for testing and local development

There are four ways to quickly install and run a Temporal Cluster.

Learn more serve (127.0.0.1:7233).

When you are connecting to a production Cluster (such as Temporal Cloud), you will likely need to provide additional connection and client options that might include, but aren't limited to, 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 Cluster, see Temporal Customization Samples.

Create an instance of the $workflowClient class and use the create() method to connect a Temporal Client to a Temporal Cluster.

Specify the target host, localhost:7223, parameter as a string and provide the TLS configuration for connecting to a Temporal Cluster.

use Temporal\Client\GRPC\ServiceClient;
use Temporal\Client\WorkflowOptions;
# . . .
$workflowClient = Temporal\Client\WorkflowClient::create(
ServiceClient::createSSL(
'localhost:7233',
'certs/ca.cert',
'certs/client.key',
'certs/client.pem',
'tls-sample',
),
);

To provide the client options as an environmental variable, add the tls option to the RoadRunner configuration file and pass the path to the file.

temporal:
# . . .
tls:
key: "certs/client.key"
cert: "certs/client.pem"
root_ca: "certs/ca.cert"
client_auth_type: require_and_verify_client_cert
server_name: "tls-sample"

Then update your application and use the SSL connection for ServiceClient.

$workflowClient = Temporal\Client\WorkflowClient::create(
ServiceClient::createSSL(
'localhost:7233',
getenv('TEMPORAL_SERVER_ROOT_CA_CERT_PATH'),
getenv('TEMPORAL_CLIENT_KEY_PATH'),
getenv('TEMPORAL_CLIENT_CERT_PATH'),
getenv('TEMPORAL_SERVER_NAME_OVERRIDE')
),
);

Develop Workflows

Workflows are the fundamental unit of a Temporal Application, and it all starts with the development of a Workflow DefinitionLink preview iconWhat is a Workflow Definition?

A Workflow Definition is the code that defines the constraints of a Workflow Execution.

Learn more.

In the Temporal PHP SDK programming model, Workflows are a class method. Classes must implement interfaces that are annotated with #[WorkflowInterface]. The method that is the Workflow must be annotated with #[WorkflowMethod].

use Temporal\Workflow\YourWorkflowInterface;
use Temporal\Workflow\WorkflowMethod;

#[WorkflowInterface]
interface FileProcessingWorkflow
{
#[WorkflowMethod]
public function processFile(Argument $args);

}

Workflow parameters

Temporal Workflows may have any number of custom parameters. However, we strongly recommend that objects are used as parameters, so that the object's individual fields may be altered without breaking the signature of the Workflow. All Workflow Definition parameters must be serializable.

A method annotated with #[WorkflowMethod] can have any number of parameters.

We recommend passing a single parameter that contains all the input fields to allow for adding fields in a backward-compatible manner.

Note that all inputs should be serializable to a byte array using the provided DataConverter interface. The default implementation uses a JSON serializer, but an alternative implementation can be easily configured. You can create a custom object and pass it to the Workflow method, as shown in the following example:

#[WorkflowInterface]
interface FileProcessingWorkflow {
#[WorkflowMethod]
public function processFile(Argument $args);
}

Workflow return values

Workflow return values must also be serializable. Returning results, returning errors, or throwing exceptions is fairly idiomatic in each language that is supported. However, Temporal APIs that must be used to get the result of a Workflow Execution will only ever receive one of either the result or the error.

A Workflow method returns a Generator. To properly typecast the Workflow's return value in the client code, use the #[ReturnType()] annotation.

#[YourWorkflowInterface]
interface FileProcessingWorkflow {

#[WorkflowMethod]
#[ReturnType("string")]
public function processFile(Argument $args);
}

Workflow Type

Workflows have a Type that are referred to as the Workflow name.

The following examples demonstrate how to set a custom name for your Workflow Type.

To customize a Workflow Type, use the WorkflowMethod annotation to specify the name of Workflow.

#[WorkflowMethod(name)]

If a Workflow Type is not specified, then Workflow Type defaults to the interface name, which is YourWorkflowDefinitionInterface in this case.

#[WorkflowInterface]
interface YourWorkflowDefinitionInterface
{
#[WorkflowMethod]
public function processFile(Argument $args);
}

Workflow logic requirements

Workflow logic is constrained by deterministic execution requirementsLink preview iconWhat is a Workflow Definition?

A Workflow Definition is the code that defines the constraints of a Workflow Execution.

Learn more. Therefore, each language is limited to the use of certain idiomatic techniques. However, each Temporal SDK provides a set of APIs that can be used inside your Workflow to interact with external (to the Workflow) application code.

**Temporal uses the Microsoft Azure Event Sourcing pattern to recover the state of a Workflow object including its local variable values.

In essence, every time a Workflow state has to be restored, its code is re-executed from the beginning. When replaying, side effects (such as Activity invocations) are ignored because they are already recorded in the Workflow event history. When writing Workflow logic, the replay is not visible, so the code should be written since it executes only once. This design puts the following constraints on the Workflow implementation:

  • Do not use any mutable global variables because multiple instances of Workflows are executed in parallel.
  • Do not call any non-deterministic functions like non seeded random or UUID directly from the Workflow code.

Always do the following in the Workflow implementation code:

  • Don’t perform any IO or service calls as they are not usually deterministic. Use Activities for this.
  • Only use Workflow::now() to get the current time inside a Workflow.
  • Call yield Workflow::timer() instead of sleep().
  • Do not use any blocking SPL provided by PHP (i.e. fopen, PDO, etc) in Workflow code.
  • Use yield Workflow::getVersion() when making any changes to the Workflow code. Without this, any deployment of updated Workflow code might break already open Workflows.
  • Don’t access configuration APIs directly from a Workflow because changes in the configuration might affect a Workflow Execution path. Pass it as an argument to a Workflow function or use an Activity to load it.

Workflow method arguments and return values are serializable to a byte array using the provided DataConverter interface. The default implementation uses JSON serializer, but you can use any alternative serialization mechanism.

Make sure to annotate your WorkflowMethod using ReturnType to specify concrete return type.

You can not use the default return type declaration as Workflow methods are generators.

The values passed to Workflows through invocation parameters or returned through a result value are recorded in the execution history. The entire execution history is transferred from the Temporal service to Workflow workers with every event that the Workflow logic needs to process. A large execution history can thus adversely impact the performance of your Workflow. Therefore, be mindful of the amount of data that you transfer via Activity invocation parameters or return values. Otherwise, no additional limitations exist on Activity implementations.**

Develop Activities

One of the primary things that Workflows do is orchestrate the execution of Activities. An Activity is a normal function or method execution that's intended to execute a single, well-defined action (either short or long-running), such as querying a database, calling a third-party API, or transcoding a media file. An Activity can interact with world outside the Temporal Platform or use a Temporal Client to interact with a Cluster. For the Workflow to be able to execute the Activity, we must define the Activity DefinitionLink preview iconWhat is an Activity Definition?

An Activity Definition is the code that defines the constraints of an Activity Task Execution.

Learn more.

Activities are defined as methods of a plain PHP interface annotated with #[YourActivityInterface]. (You can also use PHP 8 attributes in PHP 7.)

Following is an example of an interface that defines four Activities:

#[YourActivityInterface]
// Defining an interface for the activities.
interface FileProcessingActivities
{
public function upload(string $bucketName, string $localName, string $targetName): void;

#[ActivityMethod("transcode_file")]
public function download(string $bucketName, string $remoteName): void;

public function processFile(): string;

public function deleteLocalFile(string $fileName): void;
}

Activity parameters

There is no explicit limit to the total number of parameters that an Activity DefinitionLink preview iconWhat is an Activity Definition?

An Activity Definition is the code that defines the constraints of an Activity Task Execution.

Learn more may support. However, there is a limit of the total size of the data ends up encoded into a gRPC message Payload.

A single argument is limited to a maximum size of 2 MB. And the total size of a gRPC message, which includes all the arguments, is limited to a maximum of 4 MB.

Also, keep in mind that all Payload data is recorded in the Workflow Execution Event HistoryLink preview iconWhat is an Event History?

An append log of Events that represents the full state a Workflow Execution.

Learn more and large Event Histories can affect Worker performance. This is because the entire Event History could be transferred to a Worker Process with a Workflow TaskLink preview iconWhat is a Workflow Task?

A Workflow Task is a Task that contains the context needed to make progress with a Workflow Execution.

Learn more.

Some SDKs require that you pass context objects, others do not. When it comes to your application data—that is, data that is serialized and encoded into a Payload—we recommend that you use a single object as an argument that wraps the application data passed to Activities. This is so that you can change what data is passed to the Activity without breaking a function or method signature.

Each method defines a single Activity type. A single Workflow can use more than one Activity interface and call more than one Activity method from the same interface.

The only requirement is that Activity method arguments and return values are serializable to a byte array using the provided DataConverter interface. The default implementation uses a JSON serializer, but an alternative implementation can be easily configured.

Activity return values

All data returned from an Activity must be serializable.

There is no explicit limit to the amount of data that can be returned by an Activity, but keep in mind that all return values are recorded in a Workflow Execution Event HistoryLink preview iconWhat is an Event History?

An append log of Events that represents the full state a Workflow Execution.

Learn more.

Return values must be serializable to a byte array using the provided DataConverter interface. The default implementation uses a JSON serializer, but an alternative implementation can be easily configured. Thus, you can return both primitive types:

class GreetingActivity implements GreetingActivityInterface
{
public function composeGreeting(string $greeting, string $name): string
{
return $greeting . ' ' . $name;
}
}

And objects:

class GreetingActivity implements GreetingActivityInterface
{
public function composeGreeting(string $greeting, string $name): Greeting
{
return new Greeting($greeting, $name);
}
}

Activity Type

Activities have a Type that are referred to as the Activity name. The following examples demonstrate how to set a custom name for your Activity Type.

An optional #[ActivityMethod] annotation can be used to override a default Activity name.

You can define your own prefix for all Activity names by adding the prefix option to the YourActivityInterface annotation. (The default prefix is empty.)

#[YourActivityInterface("file_activities.")]
interface FileProcessingActivities
{
public function upload(string $bucketName, string $localName, string $targetName);

#[ActivityMethod("transcode_file")]
public function download(string $bucketName, string $remoteName);

public function processFile(): string;

public function deleteLocalFile(string $fileName);
}

The #[YourActivityInterface("file_activities.")] is an annotation that tells the PHP SDK to generate a class to implement the FileProcessingActivities interface. The functions define Activities that are used in the Workflow.

Activity Execution

Calls to spawn Activity ExecutionsLink preview iconWhat is an Activity Execution?

An Activity Execution is the full chain of Activity Task Executions.

Learn more are written within a Workflow DefinitionLink preview iconWhat is a Workflow Definition?

A Workflow Definition is the code that defines the constraints of a Workflow Execution.

Learn more. The call to spawn an Activity Execution generates the ScheduleActivityTask Command. This results in the set of three Activity TaskLink preview iconWhat is an Activity Task?

An Activity Task contains the context needed to make an Activity Task Execution.

Learn more related Events (ActivityTaskScheduled, ActivityTaskStarted, and ActivityTask[Closed])in your Workflow Execution Event History.

A single instance of the Activities implementation is shared across multiple simultaneous Activity invocations. Therefore, the Activity implementation code must be stateless.

The values passed to Activities through invocation parameters or returned through a result value are recorded in the Execution history. The entire Execution history is transferred from the Temporal service to Workflow Workers when a Workflow state needs to recover. A large Execution history can thus adversely impact the performance of your Workflow.

Therefore, be mindful of the amount of data you transfer through Activity invocation parameters or Return Values. Otherwise, no additional limitations exist on Activity implementations.

Activity implementation is an implementation of an Activity interface. The following code example, uses a constructor that takes an Amazon S3 client and a local directory, and uploads a file to the S3 bucket. Then, the code uses a function to download a file from the S3 bucket passing a bucket name, remote name, and local name as arguments. Finally, it uses a function that takes a local file name as an argument and returns a string.

// An implementation of an Activity interface.
class FileProcessingActivitiesImpl implements FileProcessingActivities {

private S3Client $s3Client;

private string $localDirectory;

public function __construct(S3Client $s3Client, string $localDirectory) {
$this->s3Client = $s3Client;
$this->localDirectory = $localDirectory;
}

// Uploading a file to S3.
public function upload(string $bucketName, string $localName, string $targetName): void
{
$this->s3Client->putObject(
$bucketName,
$targetName,
fopen($this->localDirectory . $localName, 'rb+')
);
}

// Downloading a file from S3.
public function download(
string $bucketName,
string $remoteName,
string $localName
): void
{
$this->s3Client->downloadObject(
$bucketName,
$remoteName,
fopen($this->localDirectory .$localName, 'wb+')
);
}

// A function that takes a local file name as an argument and returns a string.
public function processFile(string $localName): string
{
// Implementation omitted for brevity.
return compressFile($this->localDirectory . $localName);
}

public function deleteLocalFile(string $fileName): void
{
unlink($this->localDirectory . $fileName);
}
}

Required timeout

Activity Execution semantics rely on several parameters. The only required value that needs to be set is either a Schedule-To-Close TimeoutLink preview iconWhat is a Start-To-Close Timeout?

A Start-To-Close Timeout is the maximum time allowed for a single Activity Task Execution.

Learn more or a Start-To-Close TimeoutLink preview iconWhat is a Start-To-Close Timeout?

A Start-To-Close Timeout is the maximum time allowed for a single Activity Task Execution.

Learn more. These values are set in the Activity Options.

Get Activity results

The call to spawn an Activity ExecutionLink preview iconWhat is an Activity Execution?

An Activity Execution is the full chain of Activity Task Executions.

Learn more generates the ScheduleActivityTask Command and provides the Workflow with an Awaitable. Workflow Executions can either block progress until the result is available through the Awaitable or continue progressing, making use of the result when it becomes available.

Workflow::newActivityStubreturns a client-side stub an implements an Activity interface. The client-side stub can be used within the Workflow code. It takes the Activity's type andActivityOptions as arguments.

Calling (via yield) a method on this interface invokes an Activity that implements this method. An Activity invocation synchronously blocks until the Activity completes, fails, or times out. Even if Activity Execution takes a few months, the Workflow code still sees it as a single synchronous invocation. It doesn't matter what happens to the processes that host the Workflow. The business logic code just sees a single method call.

class GreetingWorkflow implements GreetingWorkflowInterface
{
private $greetingActivity;

public function __construct()
{
$this->greetingActivity = Workflow::newActivityStub(
GreetingActivityInterface::class,
ActivityOptions::new()->withStartToCloseTimeout(\DateInterval::createFromDateString('30 seconds'))
);
}

public function greet(string $name): \Generator
{
// This is a blocking call that returns only after the activity has completed.
return yield $this->greetingActivity->composeGreeting('Hello', $name);
}
}

If different Activities need different options, like timeouts or a task queue, multiple client-side stubs can be created with different options.

$greetingActivity = Workflow::newActivityStub(
GreetingActivityInterface::class,
ActivityOptions::new()->withStartToCloseTimeout(\DateInterval::createFromDateString('30 seconds'))
);

$greetingActivity = Workflow::newActivityStub(
GreetingActivityInterface::class,
ActivityOptions::new()->withStartToCloseTimeout(\DateInterval::createFromDateString('30 minutes'))
);

Run Worker Processes

The Worker ProcessLink preview iconWhat is a Worker Process?

A Worker Process is responsible for polling a Task Queue, dequeueing a Task, executing your code in response to a Task, and responding to the Temporal Server with the results.

Learn more is where Workflow Functions and Activity Functions are executed.

A Worker EntityLink preview iconWhat is a Worker Entity?

A Worker Entity is the individual Worker within a Worker Process that listens to a specific Task Queue.

Learn more is the component within a Worker Process that listens to a specific Task Queue.

Although multiple Worker Entities can be in a single Worker Process, a single Worker Entity Worker Process may be perfectly sufficient. For more information, see the Worker tuning guide.

A Worker Entity contains both a Workflow Worker and an Activity Worker so that it can make progress for either a Workflow Execution or an Activity Execution.

The RoadRunner application server will launch multiple Temporal PHP Worker processes based on provided .rr.yaml configuration.

Each Worker might connect to one or multiple Task Queues. Worker poll Temporal service for tasks, performs those tasks, and communicates task execution results back to the Temporal service.

Worker code are developed, deployed, and operated by Temporal customers. To create a worker use Temporal\WorkerFactory:

<?php

declare(strict_types=1);

use Temporal\WorkerFactory;

ini_set('display_errors', 'stderr');
include "vendor/autoload.php";

// factory initiates and runs task queue specific activity and workflow workers
$factory = WorkerFactory::create();

// Worker that listens on a Task Queue and hosts both workflow and activity implementations.
$worker = $factory->newWorker();

// Workflows are stateful. So you need a type to create instances.
$worker->registerWorkflowTypes(App\DemoWorkflow::class);

// Activities are stateless and thread safe. So a shared instance is used.
$worker->registerActivity(App\DemoActivity::class);

// In case an activity class requires some external dependencies provide a callback - factory
// that creates or builds a new activity instance. The factory should be a callable which accepts
// an instance of ReflectionClass with an activity class which should be created.
$worker->registerActivity(App\DemoActivity::class, fn(ReflectionClass $class) => $container->create($class->getName()));

// start primary loop
$factory->run();

You can configure task queue name using first argument of WorkerFactory->newWorker:

$worker = $factory->newWorker('your-task-queue');

As mentioned above you can create as many Task Queue connections inside a single Worker Process as you need.

To configure additional WorkerOptions use Temporal\Worker\WorkerOptions:

use Temporal\Worker\WorkerOptions;

$worker = $factory->newWorker(
'your-task-queue',
WorkerOptions::new()
->withMaxConcurrentWorkflowTaskPollers(10)
);

Make sure to point the Worker file in application server configuration:

rpc:
listen: tcp://127.0.0.1:6001

server:
command: "php worker.php"

temporal:
address: "temporal:7233"
activities:
num_workers: 10

You can serve HTTP endpoints using the same server setup.

Register types

All Workers listening to the same Task Queue name must be registered to handle the exact same Workflows Types and Activity Types.

If a Worker polls a Task for a Workflow Type or Activity Type it does not know about, it fails that Task. However, the failure of the Task does not cause the associated Workflow Execution to fail.

Worker listens on a Task Queue and hosts both Workflow and Activity implementations:

// Workflows are stateful. So you need a type to create instances:
$worker->registerWorkflowTypes(App\DemoWorkflow::class);
// Activities are stateless and thread safe:
$worker->registerActivity(App\DemoActivity::class);

In case an activity class requires some external dependencies provide a callback - factory that creates or builds a new activity instance. The factory should be a callable which accepts an instance of ReflectionClass with an activity class which should be created.

$worker->registerActivity(
App\DemoActivity::class,
fn(ReflectionClass $class) => $container->create($class->getName())
);

If you want to clean up some resources after activity is done, you may register a finalizer. This callback is called after each activity invocation:

$worker->registerActivityFinalizer(fn() => $kernel->showtdown());

Start 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 Cluster to create the first Event (WorkflowExecutionStarted) in the Workflow Execution Event History. The Temporal Cluster 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 optionLink preview iconHow to use Temporal Cron Jobs

A Temporal Cron Job is the series of Workflow Executions that occur when a Cron Schedule is provided in the call to spawn a Workflow Execution.

Learn more.

Set Task Queue

In most SDKs, the only Workflow Option that must be set is the name of the Task QueueLink preview iconWhat is a Task Queue?

A Task Queue is a first-in, first-out queue that a Worker Process polls for Tasks.

Learn more.

For any code to execute, a Worker Process must be running that contains a Worker Entity that is polling the same Task Queue name.

In PHP, a Task Queue is represented in code by its name as a string. There are four places where the name of the Task Queue is supplied by the developer.

  1. When starting a Workflow, a Task Queue name must be provided in the StartWorkflowOptions.
// Create new Workflow Options and set the Task Queue
$workflowOptions = WorkflowOptions::new()
->withTaskQueue("Workflow-Task-Queue-1")
// ...

$yourWorkflow = $workflowClient->newWorkflowStub(
YourWorkflowInterface::class,
$workflowOptions
);

$result = $yourWorkflow->workflowMethod();
  1. A Task Queue name must be provided as a parameter when creating a Worker.
use Temporal\WorkerFactory;

// Create a Worker Factory
$factory = WorkerFactory::create();

// Set the Task Queue when creating the Worker
$worker = $factory->newWorker("Workflow-Task-Queue-1");

// Workflows are stateful. So you need a type to create instances.
$worker->registerWorkflowTypes(YourWorkflow::class);

// start primary loop
$factory->run();

A single Worker can listen to only one Task Queue. And, it is important to remember that the name of the Task Queue the Worker is listening to must match the name of the Task Queue provided in the options to any given Workflow or Activity.

All Workers listening to the same Task Queue name must be registered to handle the exact same Workflows Types and Activity Types.

If a Worker polls a Task for a Workflow Type or Activity Type it does not know about, it will fail that Task. However, the failure of the Task will not cause the associated Workflow Execution to fail.

  1. Optionally, the name of a Task Queue can be provided in the ActivityOptions when calling an Activity from a Workflow.
class YourWorkflow implements YourWorkflowInterface
{
private $yourActivity;

public function __construct()
{
// Create Activity options and set the Task Queue
$activityOptions = ActivityOptions::new()
->withTaskQueue("Activity-Task-Queue-1")
// ...

// Create a new Activity Stub and pass the options
$this->yourActivity = Workflow::newActivityStub(
YourActivityInterface::class,
$activityOptions
);
}

public function workflowMethod(): \Generator
{
// Call the Activity
return yield $this->yourActivity->activityMethod();
}
}

If a Task Queue name is not provided in the ActivityOptions, then the Activity Tasks are placed in the same Task Queue as the Workflow Task Queue.

  1. Optionally, the name of a Task Queue can be provided in the ChildWorkflowOptions when calling a Child Workflow.
//Create new Child Workflow Options and set the Task Queue
$childWorkflowOptions = ChildWorkflowOptions::new()
->withTaskQueue("Child-Workflow-Task-Queue-1")
// ...

// Create a new Child Workflow Stub and set the Task Queue
$childWorkflow = Workflow::newChildWorkflowStub(
ChildWorkflowInterface::class,
$childWorkflowOptions
);

// Call the Child Workflow method
$promise = $childWorkflow->workflowMethod();

If a Task Queue name is not provided in the ChildWorkflowOptions, then the Child Workflow Tasks are placed in the same Task Queue as the Parent Workflow Task Queue.

Workflow Id

Although it is not required, we recommend providing your own Workflow IdLink preview iconWhat is a Workflow Id?

A Workflow Id is a customizable, application-level identifier for a Workflow Execution that is unique to an Open Workflow Execution within a Namespace.

Learn more that maps to a business process or business entity identifier, such as an order identifier or customer identifier.

The following code example grabs the userID as an input and uses it to start the Workflow. The userID is used as Workflow Id. You can use this to cancel your Workflow later.

#[WorkflowInterface]
interface SubscriptionWorkflowInterface
{
#[WorkflowMethod]
public function subscribe(string $userID);
}

The following code example, uses the input parameter userID as the Workflow Id.

#[WorkflowInterface]
interface SubscriptionWorkflowInterface
{
#[WorkflowMethod]
public function subscribe(
string $userID
);
}

You can also set the Workflow Id as a constant, for example:

public const WORKFLOW_ID = Your-Workflow-Id

Get Workflow results

If the call to start a Workflow Execution is successful, you will gain access to the Workflow Execution's Run Id.

The Workflow Id, Run Id, and Namespace may be used to uniquely identify a Workflow Execution in the system and get its result.

It's possible to both block progress on the result (synchronous execution) or get the result at some other point in time (asynchronous execution).

In the Temporal Platform, it's also acceptable to use Queries as the preferred method for accessing the state and results of Workflow Executions.

If you need to wait for the completion of a Workflow after an asynchronous start, make a blocking call to the WorkflowRun->getResult method.

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

var_dump($run->getResult());