Skip to content

Releases: temporalio/sdk-php

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

v2.7.4

18 Jan 13:49
v2.7.4
9de474f
Compare
Choose a tag to compare

What's Changed

Full Changelog: v2.7.3...v2.7.4

v2.7.3

15 Jan 19:22
v2.7.3
89a1b0b
Compare
Choose a tag to compare

What's Changed

  • Fixed hydrating of null value in the JsonConverter by @root-aza in #386
  • Fixed bug with Workflow Start Delay option set when it wasn't specified by @roxblnfk in #388

Full Changelog: v2.7.2...v2.7.3

v2.7.2

09 Jan 15:57
v2.7.2
06639b2
Compare
Choose a tag to compare

What's Changed

Fixed marshaller when ext-protobuf is installed by @roxblnfk in #384
Also:

  • CI: added tests with PHP + ext-protobuf
  • Tests: improved test names
    Before renaming: "Tests: 332, Assertions: 442"
    After renaming: "Tests: 511, Assertions: 1122"

Full Changelog: v2.7.1...v2.7.2

v2.7.1

05 Jan 14:26
v2.7.1
bab54b6
Compare
Choose a tag to compare

What's Changed

Full Changelog: v2.7.0...v2.7.1

v2.7.0

23 Dec 16:39
v2.7.0
403baf4
Compare
Choose a tag to compare

Schedule API

Temporal Schedules are a replacement for traditional cron jobs for task scheduling because the Schedules provide a more durable way to execute tasks, allow insight into their progress, enable observability of schedules and workflow runs, and let you start, stop, and pause them.

Starting with this release, the Temporal PHP SDK begins to provide new primitives for Schedule CRUD operations.

use Temporal\Client\Schedule;

// Start from creating the Schedule Client
// If you use a Temporal integration package with your framework, you can
// get the client from the container using the `ScheduleClientInterface` interface.
$client = \Temporal\Client\ScheduleClient::create(
    \Temporal\Client\GRPC\ServiceClient::create('localhost:7233'),
);

// Create a Schedule
$handle = $client->createSchedule(
    Schedule\Schedule::new()->withAction(
        Schedule\Action\StartWorkflowAction::new('testWorkflow')
            ->withTaskQueue('testTaskQueue')
            ->withRetryPolicy(\Temporal\Common\RetryOptions::new()->withMaximumAttempts(3))
            ->withHeader(['foo' => 'bar'])
            ->withWorkflowExecutionTimeout('40m')
    )->withSpec(
        Schedule\Spec\ScheduleSpec::new()
            ->withIntervalList(5 * 60) // every 5 minutes
            ->withJitter(60) // with jitter of 1 minute
    ),
    scheduleId: 'my-schedule-id',
);

// Pause the Schedule
$handle->pause();

// Trigger the Action to be taken immediately
$handle->trigger(Schedule\Policy\ScheduleOverlapPolicy::CancelOther);

// Delete the Schedule
$handle->delete();

To get a handle to an existing Schedule, use the ScheduleClient::getHandle() method.

$client = \Temporal\Client\ScheduleClient::create(
    \Temporal\Client\GRPC\ServiceClient::create('localhost:7233'),
);

// Get a handle to an existing Schedule
$handle = $client->getHandle(scheduleID: 'my-schedule-id');

// Describe the Schedule
$description = $handle->describe();

// Update the Schedule
$handle->update(
    $description->schedule->withSpec(
        \Temporal\Client\Schedule\Spec\ScheduleSpec::new()
            ->withCronStringList('0 12 * * 5', '0 12 * * 1')
    ),
    conflictToken: $description->conflictToken, // To avoid race condition
);

Workflow Start Delay

Use the Workflow Start Delay functionality if you need to delay the execution of the Workflow without the need for regular launches.
Here you simply specify the time to wait before dispatching the first Workflow task.
If the Workflow gets a Signal before the delay, a Workflow task will be dispatched and the rest of the delay will be ignored. A Signal from WorkflowClientInterface::startWithSignal() won't trigger a workflow task.
Cannot be set the same time as a WorkflowOptions::withCronSchedule().

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

$workflow = $client->newWorkflowStub(
    GreeterWorkflowInterface::class,
    \Temporal\Client\WorkflowOptions::new()
        ->withWorkflowStartDelay(\DateInterval::createFromDateString('1 minute'))
);
$client->start($workflow, 'Hello world!');

Interceptors

Temporal Interceptors are a mechanism for modifying inbound and outbound SDK calls. They are commonly used to add tracing and authorization to the scheduling and execution of Workflows and Activities. You can compare these to "middleware" in other frameworks.

The following types of Interceptors have been implemented:

Please note that some interceptor interfaces will be expanded in the future. To avoid compatibility issues, always use the corresponding traits in your implementations. The traits will prevent implementations from breaking when new methods are added to the interfaces.
A reminder of this and a link to the corresponding trait are placed in the comments of the interceptor interfaces.

In this release, we have also added support for headers, which are an integral part of interceptors. Headers, being metadata, are intended for sharing context and auxiliary information. Headers sent at the start of a workflow will be propagated to all subsequent calls from the Workflow and can only be modified in interceptors.

You can find examples of how to use interceptors in the samples repository (PR)

Full Changelog: v2.6.2...v2.7.0

v2.7.0-RC1

18 Dec 16:47
4538cdb
Compare
Choose a tag to compare
v2.7.0-RC1 Pre-release
Pre-release

What's Changed

  • Maintenance and polishing by @roxblnfk in #377, #378
    • Schedule API has been fixed, covered with tests, and polished. Added ScheduleClientInterface.
    • Added Symfony 7 packages, removed Symfony 4.
    • Repository cleaning.
    • Fixed HistoryLength update when a Signal method is called.

Full Changelog: v2.7.0-BETA3...v2.7.0-RC1

v2.7.0-BETA3

30 Nov 12:04
6504057
Compare
Choose a tag to compare
v2.7.0-BETA3 Pre-release
Pre-release

What's Changed

Schedule API

Concept
Temporal Schedules: Reliable, Scalable, and More Flexible than Cron Jobs (blog post)

Example

use Temporal\Client\Schedule;

// Create a Schedule Client
$client = \Temporal\Client\ScheduleClient::create(
    \Temporal\Client\GRPC\ServiceClient::create('localhost:7233'),
);

// Create a Schedule
$handle = $client->createSchedule(
    Schedule\Schedule::new()->withAction(
        Schedule\Action\StartWorkflowAction::new('testWorkflow')
            ->withTaskQueue('testTaskQueue')
            ->withRetryPolicy(\Temporal\Common\RetryOptions::new()->withMaximumAttempts(3))
            ->withHeader(['foo' => 'bar'])
            ->withWorkflowExecutionTimeout('40m')
    )->withSpec(
        Schedule\Spec\ScheduleSpec::new()
            ->withCronStringList('0 * * * *')
    ),
    scheduleId: 'testSchedule',
);

// Describe the Schedule
$description = $handle->describe();


// Update the Schedule
$handle->update(
    $description->schedule->withSpec(
        Schedule\Spec\ScheduleSpec::new()
            ->withCronStringList('0 12 * * 5', '0 12 * * 1')
    ),
    conflictToken: $description->conflictToken, // To avoid race condition
);

// Pause the Schedule
$handle->pause();

// Trigger the Action to be taken immediately
$handle->trigger(Schedule\Policy\ScheduleOverlapPolicy::CancelOther);

// Delete the Schedule
$handle->delete();

Full Changelog: v2.7.0-BETA2...v2.7.0-BETA3