Skip to content

Releases: temporalio/sdk-php

v2.9.1

14 May 09:41
v2.9.1
774ca97
Compare
Choose a tag to compare

What's Changed

Full Changelog: v2.9.0...v2.9.1

v2.9.0

13 May 13:47
v2.9.0
d4bd412
Compare
Choose a tag to compare

Connection Management Layer

Issue #224

In the Service Client, a method getConnection() has been added, which returns a ConnectionInterface.
ConnectionInterface includes public methods isConnected(), disconnect(), connect(float $timeout), allowing
control over the connection to the Temporal server.

A gRPC connection is lazy in PHP and is established only when the first gRPC method is called.
Using the connect() method, users can immediately establish and verify connection credentials without the need for gRPC method calls.
Users can sequentially call connect() and disconnect() without causing errors related to closed gRPC channels, as the channels are recreated under the hood.

/** @var \Temporal\Client\WorkflowClient $workflowClient */

// Establish a connection with the server.
// An exception will be thrown if the connection is not established within 5 seconds.
$workflowClient->getServiceClient()->getConnection()->connect(5);

Server Capabilities info now is cached in the Connection object and will be updated on each reconnect.

/** @var \Temporal\Client\WorkflowClient $workflowClient */

// Establish a connection with the server and call getSystemInfo() RPC method.
$capabilities = $workflowClient->getServiceClient()->getServerCapabilities();

if (!$capabilities?->supportsSchedules) } {
    throw new \Temporal\Exception\TemporalException('Server does not support schedules');
}

Service Client with Secure Connection

A deprecation error will now be triggered when a ServiceClient is created directly through the constructor. Static factories are the only recommended way to create a ServiceClient.

One of such static methods has changes:
in ServiceClient::createSSL(), the root certificate parameter has been made optional because it should be skipped when connecting to the Temporal Cloud.
All keys can be passed as a string payload (previously, only by file name was allowed). If the provided file cannot be read, a clear exception will be thrown.

Service Client with API auth key

The ClientService now can accept an API key for authentication.

$serviceClient = \Temporal\Client\GRPC\ServiceClient::createSSL(
    'temporal.my-project.com:7233',
    __DIR__ . '/my-project.key',
    __DIR__ . '/my-project.crt',
)->withAuthKey($key);   // $key is a string or a \Stringable object

$workflowClient = new \Temporal\Client\WorkflowClient($serviceClient);

You may pass your own Stringable implementation as the $key argument to be able to change the key dynamically.

New methods for all the Clients (Workflow and Schedule)

Issue #338

Added new methods to WorkflowClient, ScheduleClient and ScheduleHandle:

  • withTimeout(float $timeout)
  • withDeadline()
  • withRetryOptions()
  • withMetadata()

They may be used before calling any method that sends a gRPC request to the server.

/** @var \Temporal\Client\ScheduleClient $scheduleClient */

$list = $scheduleClient->withTimeout(5)->listSchedules();

All the new methods are immutable and return a new instance of the client that will use the same connection as the original client, but with the specified timeout, deadline or retry options.

/** @var \Temporal\Client\WorkflowClient $workflowClient */

// All the calls $workflow->* will be executed with a 5-second timeout.
$workflow = $workflowClient->withTimeout(5)->newWorkflowStub(MyWorkflow::class);

// Will be called with a 10-second timeout.
$workflowClient->withTimeout(10)->start($workflow, 'foo', 'bar')

// Will be called with a 5-second timeout because the stub was created with a 5-second timeout client.
$workflow->signal();

Note: WorkflowClientInterface and ScheduleClientInterface have been updated with the new methods.

RPC Retry Policy

Issue #421

Client RPC requests have a new algorithm for calculating the timeout until the next retry attempt:

  • Added Jitter, which introduces a random variation to the calculated time (default is 10%).
  • InitialInterval has been changed from 500ms to 50ms. For the RESOURCE_EXHAUSTED error, the interval is 1000ms.

All settings are configurable:

$workflowClient->withRetryOptions(
    \Temporal\Client\Common\RpcRetryOptions::new()
        ->withInitialInterval('500 milliseconds')
        ->withCongestionInitialInterval('5 seconds')
        ->withMaximumInterval('5 minutes')
        ->withBackoffCoefficient(5)
        ->withMaximumAttempts(4)
        ->withJitter(0.25)
);

Namespace Inheritance in Client methods

A mistake was made in the implementation of several client functions last time: instead of using the Namespace value from ClientOptions, a parameter with the default value "default" was used.
This complicates the use of Temporal Cloud, where user's Namespace differs from "default".

Affected methods are:

  • WorkflowClient::listWorkflowExecutions()
  • WorkflowClient::countWorkflowExecutions()
  • WorkflowClient::getWorkflowHistory()
  • ScheduleClient::getHandle()
  • ScheduleClient::listSchedules()

The $namespace parameter is now null by default. If a method receives null, the Namespace from ClientOptions will be used.

Describe a Workflow

Use the API to obtain comprehensive information about a started Workflow.

$stub = $workflowClient->newWorkflowStub(SimpleWorkflow::class);
$run = $workflowClient->start($stub, 'Hello World!');

/** @var WorkflowExecutionDescription $description */
$description = $run->describe();

You can use the Workflow Describe feature to get the status of a running Workflow.

$stub = $workflowClient->newUntypedRunningWorkflowStub($wfId);

/** @var WorkflowExecutionStatus $status */
$status = $stub->describe()->info->status;

Other changes

  • Fixed Namespace inheritance when a Child Workflow is started (#415)
  • Added enum WorkflowIdConflictPolicy that can be passed to WorkflowOptions in the client API (#417)
  • The @psalm-immutable attribute has been removed from all interceptor interfaces.
  • Fixed SystemInfoInterceptor constructor parameter: ConnectionInterface instead of ServiceClient.
  • In SystemInfoInterceptor, new features related to caching Server Capabilities inside the Connection are considered.
  • ServiceClient::setServerCapabilities() method has been removed from the ServiceClientInterface. The implementing method just triggers a deprecation error.
  • ServiceClient::getServerCapabilities() method now loads the Server Capabilities from the Connection object instead of just returning the cached value.
  • Updated ServerCapabilities DTO: added all the new fields; the flags are available as public properties.
  • Move Temporal\Client\WorkflowExecutionHistory, Workflow\Client\CountWorkflowExecutions, Workflow\Client\Paginator and Workflow\Client\ServerCapabilities into other namespaces.
  • WorkerVersionStamp::$bundleId is deprecated now (#417)
  • Update description for WorkflowStubInterface::startUpdate() method (#429)

Pull requests

  • Client improvements by @roxblnfk in #411
  • Expose API to describe Workflow by @roxblnfk in #414
  • Inherit Namespace from the parent Workflow in a Child Workflow by @roxblnfk in #415
  • Expose API key client option by @roxblnfk in #418
  • Expose WorkflowIdConflictPolicy by @roxblnfk in #417
  • Add RpcRetryOption and use longer retry interval on RESOURCE_EXHAUSTED by @roxblnfk in #425
  • Update description for WorkflowStubInterface::startUpdate() method by @roxblnfk in #429
  • Use namespace from the Service Client in ScheduleClient::listSchedules() by @roxblnfk in #430

Full Changelog: v2.8.3...v2.9.0

v2.8.3

17 Apr 15:55
v2.8.3
f9d4756
Compare
Choose a tag to compare

What's Changed

  • Fixed DateInterval to protobuf Duration conversion by @Zylius in #424

Full Changelog: v2.8.2...v2.8.3

v2.8.2

05 Apr 15:04
v2.8.2
03c654c
Compare
Choose a tag to compare

What's Changed

  • Remove experimental flag from StartDelay by @tlalfano in #409
  • Fix JSON interval unmarshalling via DurationJsonType::parse() fix by @Zylius in #412
  • Add GH action to generate API documentation by @msmakouz in #407

New Contributors

Full Changelog: v2.8.1...v2.8.2

v2.8.1

11 Mar 16:34
d230c85
Compare
Choose a tag to compare

What's Changed

  • Fixed FailureConverter when an exception Call Stack contains an incomplete set of keys by @actuallymab in #406

New Contributors

  • @actuallymab made their first contribution in #406

Full Changelog: v2.8.0...v2.8.1

v2.8.0

07 Mar 18:56
v2.8.0
41c9c3b
Compare
Choose a tag to compare

What's Changed

  • Expose Workflow Updates by @roxblnfk in #398
    Sample
  • Add Carbon v3 and remove unnecessary packages by @devvitalii in #404
    Now SDK is compatible with Symfony 7
  • A few life quality improvements by @roxblnfk in #405
    • Make exception messages shorter and more elegant: hide internal stack trace and deduplicate the error reason
    • Throw an exception with clear message if Client is used without gRPC extension; add ext-grpc into suggested packages

Warning

RoadRunner >=2023.3.12 is required

How to develop with Updates

An Update is an operation that can mutate the state of a Workflow Execution and return a response.

Note

To enable the Workflow Update feature, you have to set the frontend.enableUpdateWorkflowExecution Temporal option to true.

How to define Updates

An Update handler has a name, arguments, response, and an optional validator.

  • The name, also called an Update type, is a string.
  • The arguments and response must be serializable.

The #[UpdateMethod] attribute indicates that the method is used to handle and respond to update requests.

#[UpdateMethod]
public function myUpdate(string $signalName);

How to handle Updates in a Workflow

Workflows listen for Update by the update's name.

The handler method can accept multiple serializable input parameters, but it's recommended using only a single parameter.
The function can return a serializable value or void.

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

    #[UpdateMethod]
    public function pauseProcessing(): void;
}

Update handlers, unlike Query handlers, can change Workflow state.

The Updates type defaults to the name of the method. To overwrite this default naming and assign a custom Update type, use the #[UpdateMethod] attribute with the name parameter.

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

    #[UpdateMethod(name: "pause")]
    public function pauseProcessing();
}

How to validate an Update in a Workflow

Validate certain aspects of the data sent to the Workflow using an Update Validator method. For instance, a counter Workflow might never want to accept a non-positive number. Use the #[UpdateValidatorMethod] attribute and set name to the name of your Update handler. Your Update Validator should accept the same input parameters as your Update Handler and return void.

#[WorkflowInterface]
interface GreetingWorkflow {
    #[WorkflowMethod]
    public function getGreetings(): array;

    #[UpdateMethod]
    public function addGreeting(string $name): int;

    #[UpdateValidatorMethod(forUpdate: "addGreeting")]
    public function addGreetingValidator(string $name): void;
}

How to send an Update from a Client

To send an Update to a Workflow Execution from a Client, call the Update method, annotated with #[UpdateMethod] in the Workflow interface, from the Client code.

In the following Client code example, start the Workflow getGreetings and call the Update method addGreeting that is handled in the Workflow.

/** @var \Temporal\Client\WorkflowClientInterface $client */

// Create a typed Workflow stub for GreetingsWorkflow
$workflow = $client->newWorkflowStub(GreetingWorkflow::class, $workflowOptions);

// Start the Workflow
$run = $client->start($workflow);

// Send an update to the Workflow. addGreeting returns
// the number of greetings our workflow has received.
$workflow->addGreeting("World");

Async accept

Note

To enable the async Workflow Update feature, you have to set the frontend.enableUpdateWorkflowExecutionAsyncAccepted Temporal option to true.

In Workflow Update methods, all Workflow features are available, such as executing Activities and child Workflows, and waiting on timers/conditions.
In cases where it's known that the update will take a long time to execute, or you are not interested in the outcome of its execution, you can use the stub method startUpdate and move on immediately after receiving the validation result.

/** @var \Temporal\Client\WorkflowClientInterface $client */
use Ramsey\Uuid\UuidInterface;
use Temporal\Client\Update\UpdateOptions;
use Temporal\Client\Update\WaitPolicy;
use Temporal\Client\Update\LifecycleStage;

// Create a typed Workflow stub for GreetingsWorkflow
$stub = $client->newUntypedWorkflowStub('GreetingWorkflow', $workflowOptions);

// Start the Workflow
$run = $client->start($stub);

// Send an update to the Workflow. UpdateHandle returns
$handle = $stub->startUpdate('addGreeting', 'World');

// Use the UpdateHandle to get the update result with timeout 2.5 seconds
$result = $handle->getResult(timeout: 2.5);

// You can get more control using UpdateOptions
$resultUuid = $stub->startUpdate(
    UpdateOptions::new('storeGreetings')
        ->withWaitPolicy(WaitPolicy::new()->withLifecycleStage(LifecycleStage::StageCompleted))
        ->withResultType(UuidInterface::class)
 )->getResult();

New Contributors

Full Changelog: v2.7.6...v2.8.0

v2.8.0-BETA2

29 Feb 19:32
v2.8.0-BETA2
5e3307b
Compare
Choose a tag to compare
v2.8.0-BETA2 Pre-release
Pre-release

How to develop with Updates

An Update is an operation that can mutate the state of a Workflow Execution and return a response.

Note

RoadRunner 2023.3.12 is required

Note

To enable the Workflow Update feature, you have to set the frontend.enableUpdateWorkflowExecution Temporal option to true.

How to define Updates

An Update handler has a name, arguments, response, and an optional validator.

  • The name, also called an Update type, is a string.
  • The arguments and response must be serializable.

The #[UpdateMethod] attribute indicates that the method is used to handle and respond to update requests.

#[UpdateMethod]
public function myUpdate(string $signalName);

How to handle Updates in a Workflow

Workflows listen for Update by the update's name.

The handler method can accept multiple serializable input parameters, but it's recommended using only a single parameter.
The function can return a serializable value or void.

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

    #[UpdateMethod]
    public function pauseProcessing(): void;
}

Update handlers, unlike Query handlers, can change Workflow state.

The Updates type defaults to the name of the method. To overwrite this default naming and assign a custom Update type, use the #[UpdateMethod] attribute with the name parameter.

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

    #[UpdateMethod(name: "pause")]
    public function pauseProcessing();
}

How to validate an Update in a Workflow

Validate certain aspects of the data sent to the Workflow using an Update Validator method. For instance, a counter Workflow might never want to accept a non-positive number. Use the #[UpdateValidatorMethod] attribute and set name to the name of your Update handler. Your Update Validator should accept the same input parameters as your Update Handler and return void.

#[WorkflowInterface]
interface GreetingWorkflow {
    #[WorkflowMethod]
    public function getGreetings(): array;

    #[UpdateMethod]
    public function addGreeting(string $name): int;

    #[UpdateValidatorMethod(forUpdate: "addGreeting")]
    public function addGreetingValidator(string $name): void;
}

How to send an Update from a Client

To send an Update to a Workflow Execution from a Client, call the Update method, annotated with #[UpdateMethod] in the Workflow interface, from the Client code.

In the following Client code example, start the Workflow getGreetings and call the Update method addGreeting that is handled in the Workflow.

/** @var \Temporal\Client\WorkflowClientInterface $client */

// Create a typed Workflow stub for GreetingsWorkflow
$workflow = $client->newWorkflowStub(GreetingWorkflow::class, $workflowOptions);

// Start the Workflow
$run = $client->start($workflow);

// Send an update to the Workflow. addGreeting returns
// the number of greetings our workflow has received.
$workflow->addGreeting("World");

Async accept

[!NOTE]
To enable an async accept for the Workflow Update feature, you have to set the frontend.enableUpdateWorkflowExecutionAsyncAccepted Temporal option to true.

In Workflow Update methods, all Workflow features are available, such as executing Activities and child Workflows, and waiting on timers/conditions.
In cases where it's known that the update will take a long time to execute, or you are not interested in the outcome of its execution, you can use the stub method startUpdate and move on immediately after receiving the validation result.

use Ramsey\Uuid\UuidInterface;
use Temporal\Client\Update\UpdateOptions;
use Temporal\Client\Update\WaitPolicy;
use Temporal\Client\Update\LifecycleStage;
/** @var \Temporal\Client\WorkflowClientInterface $client */

// Create a typed Workflow stub for GreetingsWorkflow
$stub = $client->newUntypedWorkflowStub('GreetingWorkflow', $workflowOptions);

// Start the Workflow
$run = $client->start($stub);

// Send an update to the Workflow. UpdateHandle returns
$handle = $stub->startUpdate('addGreeting', 'World');

// Use the UpdateHandle to get the update result with timeout 2.5 seconds
$result = $handle->getResult(timeout: 2.5);

// You can get more control using UpdateOptions
$resultUuid = $stub->startUpdate(
    UpdateOptions::new('storeGreetings')
        ->withWaitPolicy(WaitPolicy::new()->withLifecycleStage(LifecycleStage::StageCompleted))
        ->withResultType(UuidInterface::class)
 )->getResult();

Full Changelog: v2.7.6...v2.8.0-BETA2

v2.8.0-BETA1

16 Feb 18:24
v2.8.0-BETA1
6234a71
Compare
Choose a tag to compare
v2.8.0-BETA1 Pre-release
Pre-release

Note

RoadRunner 2023.3.11 is required

How to develop with Updates

An Update is an operation that can mutate the state of a Workflow Execution and return a response.

How to define Updates

An Update handler has a name, arguments, response, and an optional validator.

  • The name, also called an Update type, is a string.
  • The arguments and response must be serializable.

The #[UpdateMethod] attribute indicates that the method is used to handle and respond to update requests.

#[UpdateMethod]
public function myUpdate(string $signalName);

How to handle Updates in a Workflow

Workflows listen for Update by the update's name.

The handler method can accept multiple serializable input parameters, but it's recommended using only a single parameter.
The function can return a serializable value or void.

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

    #[UpdateMethod]
    public function pauseProcessing(): void;
}

Update handlers, unlike Query handlers, can change Workflow state.

The Updates type defaults to the name of the method. To overwrite this default naming and assign a custom Update type, use the #[UpdateMethod] attribute with the name parameter.

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

    #[UpdateMethod(name: "pause")]
    public function pauseProcessing();
}

How to validate an Update in a Workflow

Validate certain aspects of the data sent to the Workflow using an Update Validator method. For instance, a counter Workflow might never want to accept a non-positive number. Use the #[UpdateValidatorMethod] attribute and set name to the name of your Update handler. Your Update Validator should accept the same input parameters as your Update Handler and return void.

#[WorkflowInterface]
interface GreetingWorkflow {
    #[WorkflowMethod]
    public function getGreetings(): array;

    #[UpdateMethod]
    public function addGreeting(string $name): int;

    #[UpdateValidatorMethod(forUpdate: "addGreeting")]
    public function addGreetingValidator(string $name): void;
}

How to send an Update from a Client

To send an Update to a Workflow Execution from a Client, call the Update method, annotated with #[UpdateMethod] in the Workflow interface, from the Client code.

In the following Client code example, start the Workflow getGreetings and call the Update method addGreeting that is handled in the Workflow.

/** @var \Temporal\Client\WorkflowClientInterface $client */

// Create a typed Workflow stub for GreetingsWorkflow
$workflow = $client->newWorkflowStub(GreetingWorkflow::class, $workflowOptions);

// Start the Workflow
$run = $client->start($workflow);

// Send an update to the Workflow. addGreeting returns
// the number of greetings our workflow has received.
$workflow->addGreeting("World");

v2.7.6

13 Feb 19:09
v2.7.6
ce3a461
Compare
Choose a tag to compare

What's Changed

Full Changelog: v2.7.5...v2.7.6

v2.7.5

06 Feb 15:05
v2.7.5
e726dda
Compare
Choose a tag to compare

What's Changed

// Workflow context

public function handle(string $userId, string $email) {
    // ...

    $activityStub = Workflow::newActivityStub(SubscriptionActivity::class, ActivityOptions::new()->withStartToCloseTimeout(10));

    // Called method signature:
    // public function subscribe(string $email, string $userId, string $prefix = 'Dear', array $channels = ['main']): void

    yield $activityStub->subscribe(user: $userId, email: $email, channels: ['news']);
    // Arguments in correct order will be sent: $email, $userId, 'Dear', ['news'] 

    // ...
}

Full Changelog: v2.7.4...v2.7.5