Skip to content

Releases: temporalio/sdk-php

v2.7.0-BETA2

23 Nov 16:30
d20614c
Compare
Choose a tag to compare
v2.7.0-BETA2 Pre-release
Pre-release

What's Changed

Workflow Start Delay

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!');

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

v2.7.0-BETA1

15 Nov 16:01
530308a
Compare
Choose a tag to compare
v2.7.0-BETA1 Pre-release
Pre-release

What's Changed

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:

WorkflowClientCallsInterceptor: Intercepts methods of the WorkflowClient, such as starting or signaling a Workflow.
WorkflowInboundCallsInterceptor: Intercepts inbound Workflow calls, including execution, Signals, and Queries.
WorkflowOutboundCallsInterceptor: Intercepts outbound Workflow calls to Temporal APIs, such as scheduling Activities and starting Timers.
ActivityInboundCallsInterceptor: Intercepts inbound calls to an Activity, such as execute.
GrpcClientInterceptor: Intercepts all service client gRPC calls (see ServiceClientInterface).
WorkflowOutboundRequestInterceptor: Intercepts all commands sent to the RoadRunner server (see RequestInterface implementations).

Please note that some interceptor interfaces will be expanded in the future. To avoid compatibility issues, always use the corresponding traits in your implementations. 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)
Over time, more samples will be provided.

Full Changelog: v2.6.2...v2.7.0-BETA1

v2.6.2

02 Nov 09:38
v2.6.2
b22625e
Compare
Choose a tag to compare

What's Changed

  • Fix return type annotation for the WorkflowClient::newRunningWorkflowStub() by @root-aza in #363
  • Update CI (include PHP 8.3), add google/common-protos v4 by @roxblnfk in #370

New Contributors

Full Changelog: v2.6.1...v2.6.2

v2.6.1

04 Oct 20:05
v2.6.1
e5b375f
Compare
Choose a tag to compare

What's Changed

  • Remove react/promise v3 from composer.json. By @roxblnfk in #358 (issue #357)
  • Add tests in which a Workflow operates incorrectly if react/promise v3 is installed. By @roxblnfk in #359

Full Changelog: v2.6.0...v2.6.1

v2.6.0

15 Sep 17:11
v2.6.0
3bbdb70
Compare
Choose a tag to compare

What's Changed

Full Changelog: v2.5.4...v2.6.0

ReactPHP Promise v3

The ReactPHP Promise dependency has been updated from version 2 to version 3.

Note: there are no BC breaks in the Temporal SDK, but there are BC breaks in the ReactPHP Promise library.
If you rely on the functions of the ReactPHP Promise library and use them directly, please familiarize yourself with the list of changes there.
We have not completely abandoned compatibility with Promise v2, and it will be available for some time — just add "react/promise": "^2.9" in your composer.json, if you are tied to Promise v2.

Despite the fact that some functions (some(), map(), reduce()) have been removed from React, they have remained in our Temporal\Promise helper with the same behavior.

Either way, welcome the new features:

  • the new convenient methods of PromiseInterface: catch() and finally();
  • @template annotation for the PromiseInterface, which allows you to specify the type of the promise result.
    So start specifying types in Activity and Workflow, and when this initiative is implemented, the Workflow code will become fully typed and understandable for IDE and static analysis.
#[\Temporal\Activity]
class MyActivityClass {
    /**
     * @return PromiseInterface<non-empty-string>
     */
    #[\Temporal\ActivityMethod('MyWorkflow')]
    public function myActivityMethod(): string {
        return 'some string';
    }
}

#[\Temporal\Workflow]
class MyWorkflowClass {
    #[\Temporal\WorkflowMethod]
    public function handle() {
        $activity = \Temporal\Workflow::newActivityStub(MyActivityClass::class);

        // IDE will know that the $uuid is a UuidInterface instance
        $uuid = yield \Temporal\Workflow::uuid();

        // IDE will know that the $result is a string
        $result = yield $activity->myActivityMethod();
    }
}

UUID support

The ramsey/uuid package is now a dependency of the PHP SDK and is supported at many levels:

  • In the Marshaller: use the `UuidInterface type in object properties - it will automatically be serialized and deserialized through a string representation.
  • In the DataConverter: specify UuidInterface in the method parameters of Workflow and Activity - this will work, in the returned types or in getResult().
    This also applies to SideEffect.

Added new methods:

$uuid  = yield \Temporal\Workflow::uuid();  // Generate a UUID (if you don't care about the UUID version)
$uuid4 = yield \Temporal\Workflow::uuid4(); // Generate a UUID v4
$uuid7 = yield \Temporal\Workflow::uuid7(); // Generate a UUID v7

Eager start

Eager Workflow Dispatch is a mechanism that minimizes the duration from starting a workflow to the processing of the first workflow task, making Temporal more suitable for latency sensitive applications.

Eager Workflow Dispatch can be enabled if the server supports it and a local worker is available the task is fed directly to the worker.

Note:
That this will require some extra work to allocate a workflow slot on a Temporal worker on the same task queue and delivering a workflow task to the workflow from the StartWorfklowExecutionResponse.

/** @var \Temporal\Client\WorkflowClientInterface $workflowClient */
$workflow = $workflowClient->newWorkflowStub(
    SimpleWorkflow::class,
    \Temporal\Client\WorkflowOptions::new()
        ->withEagerStart()  // <=
);

$workflowClient->start($workflow, 'hello');

Returning array of objects

Added the possibility to return a list of objects from activity/workflow.

/** @var \Temporal\Client\WorkflowClientInterface $workflowClient */
/** @var \Temporal\Internal\Workflow\ActivityStub $activity */

//When executing workflow:

/** @var Message[] $result */
$result = $workflowClient->start($workflow, 'John')->getResult(Type::arrayOf(Message::class));

//When executing activity within a workflow:

/** @var Message[] $result */
$result = yield $activity->execute(
    name: 'myActivity',
    args: [$input],
    returnType: Type::arrayOf(Message::class),
);

History length

Now you don't need to guess how many events your code produces to decide if it's time to call continue-as-new.
Just check Workflow::getInfo()->historyLength property.
This field will be updated with any response from the RoadRunner server.

Available since RoadRunner 2023.2.0 (stable).

Visibility: listWorkflowExecutions and listWorkflowExecutions

Added WorkflowClient::listWorkflowExecutions() and WorkflowClient::countWorkflowExecutions() methods that can help to get a list of workflow executions using a list filter query syntax.
As a result, a Paginator object is returned which can be used in a simple way:

/** @var \Temporal\Client\WorkflowClientInterface $client */
$paginator = $client->listWorkflowExecutions(
    query: "WorkflowType='MyWorkflow' and StartTime  between '2022-08-22T15:04:05+00:00' and  '2023-08-22T15:04:05+00:00'",
    namespace: 'default',
);

// Iterate all items (pages will be fetched automatically)
foreach ($paginator as $execution) {
    // ...
}

// Get current page items
$items = $paginator->getPageItems();

// Get next page
$nextPage = $paginator->getNextPage();

// Get items count (an RPC method will be called that may require a preconfigured advanced Visibility)
$count = $paginator->count();

Documentation about Visibility: https://docs.temporal.io/visibility

Replay API

PR: #336

Replay API is very useful tool for a workflow determinism testing.
It recreates the exact state of a Workflow Execution. You can replay a Workflow from the beginning of its Event History.
Replay succeeds only if the Workflow Definition is compatible with the provided history from a deterministic point of view.

Examples

To replay Workflow Executions, use the \Temporal\Testing\Replay\WorkflowReplayer class.

In the following example, Event Histories are downloaded from the server, and then replayed. Note that this requires Advanced Visibility to be enabled.

/**
 * We assume you already have a WorkflowClient instance in the scope.
 * @var \Temporal\Client\WorkflowClientInterface $workflowClient
 */

// Find all workflow executions of type "MyWorkflow" and task queue "MyTaskQueue".
$executions = $workflowClient->listWorkflowExecutions(
    "WorkflowType='MyWorkflow' AND TaskQueue='MyTaskQueue'"
);

$replayer = new \Temporal\Testing\Replay\WorkflowReplayer();
// Replay each workflow execution.
foreach ($executions as $executionInfo) {
    try {
        $replayer->replayFromServer(
            workflowType: $executionInfo->type->name,
            execution: $executionInfo->execution,
        );
    } catch (\Temporal\Testing\Replay\Exception\ReplayerException $e) {
        // Handle the replay error.
    }
}

You can download a workflow history manually from Temporal UI or using PHP, and then replay it from a memorized History object:

$history = $this->workflowClient->getWorkflowHistory(
    execution: $run->getExecution(),
)->getHistory();

(new WorkflowReplayer())->replayHistory($history);

To store a History JSON file, use the \Temporal\Testing\Replay\WorkflowReplayer::downloadHistory() method.

Available since RoadRunner 2023.3

Stability improvements

We have introduced a new way of identifying workflow operations. It's internal run_id, that helps to avoid
bugs related with async operations between Temporal, RoadRunner and SDK.
There will be no any "Got the response to undefined request" errors!

Available since RoadRunner 2023.3

Info:
If you want to install the pre-release version of RoadRunner 2023.3, then execute the command:

vendor/bin/rr get --stability=RC

v2.5.4

28 Jul 15:24
v2.5.4
f3e2a6e
Compare
Choose a tag to compare

What's Changed

  • Fix potential issue when signal coroutine won't trigger await due to failed activity call by @wolfy-j in #335

Full Changelog: v2.5.3...v2.5.4

v2.5.3

21 Jul 17:25
v2.5.3
42b6f9e
Compare
Choose a tag to compare

What's Changed

  • Fix marshalling/unmarshalling of nullable object type by @roxblnfk in #334
  • Fix exceptions propagation from Signal methods. Only arguments decoding exceptions will be ignored, all other exceptions will be propagated. By @roxblnfk in #331

Full Changelog: v2.5.0...v2.5.3

v2.5.2

10 Jul 13:39
v2.5.2
ea20d36
Compare
Choose a tag to compare

What's Changed

New Contributors

Full Changelog: v2.5.1...v2.5.2

v2.5.1

31 May 21:15
v2.5.1
6c85e88
Compare
Choose a tag to compare

What's Changed

  • Fix deprecated string interpolation - replace ${var} with {$var}. By @roxblnfk in #309

Full Changelog: v2.5.0...v2.5.1

v2.5.0

18 Apr 09:39
v2.5.0
479b0b4
Compare
Choose a tag to compare

What's Changed

  • PHP Attributes: replace implements NamedArgumentConstructorAttribute with the attribute NamedArgumentConstructor by @msmakouz in #292
    That required for a better compatibility with the doctrine/annotations v2
  • Update PHP RoadRunner packages versions by @roxblnfk in #297
    New major versions was of the packages are related to the new RoadRunner 2023.1 release
  • Expand list of retryable gRPC errors by @roxblnfk in #298
    Note: in the case when a Temporal server is unavailable and by the reason a Client call can't be completed, it might retry infinitely without a limited retry gRPC call policy.
  • Add supporting for UndefinedResponse command by @roxblnfk in #300
    The command will be sent instead of logic exception throwing. The command is available since RoadRunner 2023.1

Full Changelog: v2.4.1...v2.5.0