Skip to content

Releases: temporalio/sdk-php

v2.15.1

21 Jul 13:54
v2.15.1
eb956d6
Compare
Choose a tag to compare

What's Changed

  • Remove experimental note from updateWithStart() client method by @roxblnfk in #637
  • Bugfix: send a special request to RR if the calling Workflow was not found by @roxblnfk in #639

Full Changelog: v2.15.0...v2.15.1

v2.15.0

09 Jul 08:05
v2.15.0
8a42213
Compare
Choose a tag to compare

Warning

RoadRunner 2025.1.2 is required.

Task Queue Priority

Task Queue Priority allows you to control the execution order of workflows, activities, and child workflows based on assigned priority values within a single task queue. You can select a priority level in the integer range 1...5. A lower value implies higher priority. The default priority if unspecified is in the middle of the range, 3.

Note

As this feature is currently in Pre-release stage, it is not intended for production use at this time.
See product release stages for more information.

Pre-requisites

  • If using Temporal Cloud, please contact Temporal support or your Temporal account team to enable this feature for your cloud namespace(s).
  • If self-hosting Temporal, use the latest pre-release development server and set matching.useNewMatcher dynamic config on the relevant task queues (or namespaces).

Client API

# New Priority DTO
$priority = Priority::new(priorityKey: 1);

# Set Priority on a Workflow
$workflow = $workflowClient->newWorkflowStub(
    OrderWorkflowInterface::class,
    WorkflowOptions::new()
        ->withTaskQueue('task-queue')
        ->withPriority($priority),
);

Workflow API

# New Priority DTO
$priority = Priority::new(priorityKey: 1);

# Set Priority on an Activity
$activity = Workflow::newActivityStub(
    ActivityInterface::class,
    ActivityOptions::new()
        ->withTaskQueue('task-queue')
        ->withStartToCloseTimeout('5 minutes')
        ->withPriority($priority),
);

# Set Priority on a Child Workflow
$childWorkflow = Workflow::newChildWorkflowStub(
    ChildWorkflowInterface::class,
    ChildWorkflowOptions::new()
        ->withTaskQueue('task-queue')
        ->withPriority($priority),
);

Get Priority value in Workflow or Activity

// Get
$priority = Activity::getInfo()->priority;
$priority = Workflow::getInfo()->priority;

Note

  • Lower numbers = higher priority.
  • Tasks with the same priority are scheduled in FIFO order.
  • If priority is unsupported by the server, these settings are silently ignored.
  • Remember this feature is not production ready at this stage.

User Metadata

Handler Descriptions

You can now add descriptions to Query, Signal, and Update handlers. Descriptions are available through the description parameter in QueryMethod, SignalMethod, and UpdateMethod attributes, as well as in the Workflow::registerSignal(), Workflow::registerQuery(), and Workflow::registerUpdate() methods. These descriptions will be displayed in the Temporal UI for better handler documentation.

Using Attributes:

#[QueryMethod('get_counter', description: 'Get the current counter value')]
public function getCounter(): int
{
    return $this->counter;
}

#[SignalMethod('inc_counter', description: 'Increment the counter value')]
public function incCounter(): void
{
    ++$this->counter;
}

Using Registration Methods:

Workflow::registerQuery('get_counter', $this->getCounter(...), 'Get the current counter value');
Workflow::registerSignal('increment_counter', $this->incrementCounter(...), 'Increment the counter value');

Activity and Timer Summaries

You can now add custom metadata summaries to Activity and Timer executions. These summaries will be displayed in the Workflow history within the Temporal UI, providing better visibility into workflow execution details.

Activity Summary:

yield Workflow::executeActivity(
    type: 'activity_type',
    options: ActivityOptions::new()
        ->withScheduleToCloseTimeout(30)
        ->withSummary('Process user payment'),
);

Timer Summary:

yield Workflow::timer(
    interval: 30,
    options: TimerOptions::new()->withSummary('Wait for external service response'),
);

Activity Pause

When a heartbeating activity is paused, an ActivityPausedException will be thrown.
Added Activity::getCancellationDetails() that returns ActivityCancellationDetails DTO that provides the reasons for the activity's cancellation.

Pull Requests

Full Changelog: v2.14.1...v2.15.0

v2.14.1

07 May 11:49
b9b9d13
Compare
Choose a tag to compare

What's Changed

Full Changelog: v2.14.0...v2.14.1

v2.14.0

06 May 12:44
v2.14.0
c8c16c5
Compare
Choose a tag to compare

Warning

RoadRunner 2024.3.3+ is required.

Workflow Logger

Logging is a critical component for monitoring and troubleshooting your Temporal applications. The PHP SDK now provides a dedicated logger for use within Workflows that respects replay semantics and adds contextual information automatically.

To get a PSR-3 compatible logger in your Workflow code, use the Workflow::getLogger() method:

use Temporal\Workflow;

#[Workflow\WorkflowInterface]
class MyWorkflow
{
    #[Workflow\WorkflowMethod]
    public function execute(string $param): \Generator
    {
        Workflow::getLogger()->info('Workflow started', ['parameter' => $param]);

        // Your workflow implementation

        Workflow::getLogger()->info('Workflow completed');
        return 'Done';
    }
}

Replay Mode Behavior

An important feature of the Workflow logger is its replay-aware behavior. By default, logs are only emitted during the initial Workflow execution and are suppressed during replay to prevent duplicate log entries.

If you want to enable logging during replay (for debugging purposes), you can configure this with the enableLoggingInReplay option:

$factory = WorkerFactory::create();
$worker = $factory->newWorker('your-task-queue', WorkerOptions::new()
    ->withEnableLoggingInReplay(true)
);

Automatic Context Enrichment

The Workflow logger automatically enriches log entries with the current task queue information. Every log message will include a task_queue key in its context, making it easier to filter and correlate logs.

For example, if a log statement is:

$logger->info('Processing order', ['order_id' => 123]);

The actual logged context will be:

{ "task_queue": "your-task-queue", "order_id": 123 }

This happens automatically without any additional configuration.

Default Logger

By default, the PHP SDK uses a StderrLogger that outputs log messages to the standard error stream.
These messages are automatically captured by RoadRunner and incorporated into its logging system with the INFO level, ensuring proper log collection in both development and production environments.
For more details on RoadRunner's logging capabilities, see the RoadRunner Logger documentation.

Using a Custom Logger

You can configure your Temporal worker to use a custom PSR-3 compatible logger implementation:

$myLogger = new MyLogger();

$workerFactory = WorkerFactory::create(converter: $converter);
$worker = $workerFactory->newWorker(
    taskQueue: 'my-task-queue',
    logger: $myLogger,
);

Your custom logger will be used throughout the Temporal SDK, including for Workflow logging when accessed through Workflow::getLogger().

getInstance() in Context

Added Activity::getInstance() and Workflow::getInstance() methods to get the current Activity and Workflow instances.

Changed workflow execution flow:

  • First, the Workflow is initialized. The __construct() method is called.
    • If the #[WorkflowInit] attribute is present, the handler's arguments are resolved and passed to the constructor.
    • There you can't make calls to start Activity, ChildWorkflow, Timer, etc.
  • WorkflowInboundCallInterceptor::execute() is called
    • Arguments from the previous step are used, but they can be overridden for the handler call.
    • You can call Activity, ChildWorkflow, Timer, etc.
    • Workflow::getInstance() returns the initialized Workflow instance.
    • Now errors from this step are recorded in the Workflow history.
  • Workflow Handler is called.

Dynamic Handlers

Added methods to define dynamic handlers for Signals, Updates, and Queries that will be called if a handler for a specific name is not found.

// Dynamic Query Handler
\Temporal\Workflow::registerDynamicQuery(function (string $name, ValuesInterface $arguments): string {
    return \sprintf(
        'Got query `%s` with %d arguments',
        $name,
        $arguments->count(),
    );
});

// Dynamic Update Handler
\Temporal\Workflow::registerDynamicUpdate(
    static fn(string $name, ValuesInterface $arguments): string => \sprintf(
        'Got update `%s` with %d arguments',
        $name,
        $arguments->count(),
    ),
    static fn(string $name, ValuesInterface $arguments) => \str_starts_with(
        $name,
        'update_',
    ) or throw new \InvalidArgumentException('Invalid update name'),
);

User Metadata Support in Client API

Added support for user metadata in Workflow Start/Schedule methods, improving the ability to attach additional information to workflow executions.

  • Added ExecutionConfig with UserMetadata in Workflow Description
  • Added support for user metadata in Workflow Start/Schedule methods. Metadata in Timers and Activities require changes in RoadRunner and can be added in the future.

Client API

use Temporal\Client\GRPC\ServiceClient;
use Temporal\Client\ScheduleClient;
use Temporal\Client\Schedule\Action\StartWorkflowAction;
use Temporal\Client\WorkflowClient;
use Temporal\Client\WorkflowOptions;

$serviceClient = ServiceClient::create('127.0.0.1:7233');

// Start Workflow with user metadata
$workflowClient = WorkflowClient::create($serviceClient);
$stub = $workflowClient->newUntypedWorkflowStub(
    'SimpleWorkflow',
    (new WorkflowOptions())
        ->withStaticSummary('some text')
        ->withStaticDetails('details') 
);
$workflowClient->start($stub);

// Describe workflow
echo $stub->describe()->config->userMetadata->summary;
echo $stub->describe()->config->userMetadata->details;

// Schedule Workflow with user metadata
$scheduleClient = ScheduleClient::create($serviceClient);
$schedule = $scheduleClient->createSchedule(
    \Temporal\Client\Schedule\Schedule::new()
        ->withAction(StartWorkflowAction::new(SimpleWorkflow::class)
            ->withStaticSummary('some-summary')
            ->withStaticDetails('some-details'))
);

// Describe schedule
$action = $schedule->describe()->schedule->action;
assert($action instanceof StartWorkflowAction);

echo $action->userMetadata->details;
echo $action->userMetadata->summary;

Workflow context:

$stub = \Temporal\Workflow::newChildWorkflowStub(
    SimpleWorkflow::class,
    (new Workflow\ChildWorkflowOptions())
        ->withStaticSummary('some text')
        ->withStaticDetails('details')
);

Additional Improvements

  • Skip magic methods in Activity classes: Magic methods not marked by the ActivityMethod attribute will not be registered as activity methods
  • Enhanced Workflow description info: Added new fields into Workflow stub -> describe result:
    • rootExecution
    • firstRunId
    • executionDuration

Pull requests

New Contributors

Full Changelog: v2.13.0...v2.14.0

v2.13.4

08 Apr 12:36
v2.13.4
9d5fb5c
Compare
Choose a tag to compare

What's changed

  • Fixed memory leak on upsert Memo / Search Attributes / Typed Search Attributes by @roxblnfk in #590

Full Changelog: v2.13.3...v2.13.4

v2.13.3

15 Mar 05:40
v2.13.3
a11699a
Compare
Choose a tag to compare

What's Changed

  • Fixed interaction with Temporal Cloud in custom namespaces by @roxblnfk in #583

Full Changelog: v2.13.2...v2.13.3

v2.13.2

19 Feb 10:45
347e344
Compare
Choose a tag to compare

What's Changed

  • Fix decoding of non-nullable interval fields when null is gotten from Temporal Cloud. By @roxblnfk in #570

Full Changelog: v2.13.1...v2.13.2

v2.13.1

12 Feb 20:30
2b4202b
Compare
Choose a tag to compare

What's Changed

  • Add default implementation for upsertMemo() method in interceptors by @roxblnfk in #569

Full Changelog: v2.13.0...v2.13.1

v2.13.0

12 Feb 15:34
v2.13.0
7489256
Compare
Choose a tag to compare

Warning

RoadRunner 2024.3.3 is required.

Search Attributes

A new approach for working with Search Attributes has been implemented - Typed Search Attributes.
For this, new methods have been added to WorkflowOptions DTO and Workflow facade.

$keyDestinationTime = SearchAttributeKey::forDatetime('DestinationTime');
$keyOrderId = SearchAttributeKey::forKeyword('OrderId');

$workflow = $workflowClient->newWorkflowStub(
    OrderWorkflowInterface::class,
    WorkflowOptions::new()
        ->withWorkflowExecutionTimeout('10 minutes')
        ->withTypedSearchAttributes(
            TypedSearchAttributes::empty()
                ->withValue($keyOrderId, $orderid)
                ->withValue($keyDestinationTime, new \DateTimeImmutable('2028-11-05T00:10:07Z'))
        ),
);

#[Workflow\WorkflowInterface]
class OrderWorkflowInterface {
    // ...

    #[Workflow\UpdateMethod]
    public function postponeDestinationTime(\DateInterval $interval)
    {
        // Get keys to work with
        $keyDestinationTime = SearchAttributeKey::forDatetime('DestinationTime');
        $keyToRemove = SearchAttributeKey::forKeyword('SomeFieldToRemove');

        /** @var DateTimeImmutable $destinationTime */
        $destinationTime = Workflow::getInfo()->typedSearchAttributes->get($keyDestinationTime);

        Workflow::upsertTypedSearchAttributes(
            $keyDestinationTime->valueSet($destinationTime->add($interval)),
            $keyToRemove->valueUnset(),
        );
    }
}

When starting the Temporal Dev Server, you can specify types for Search Attributes.

$testEnv->startTemporalServer(searchAttributes: [
    'testBool' => ValueType::Bool,
    'testInt' => ValueType::Int,
    'testFloat' => ValueType::Float,
    'testString' => ValueType::String,
    'testKeyword' => ValueType::Keyword,
    'testKeywordList' => ValueType::KeywordList,
    'testDatetime' => ValueType::Datetime,
]);

Workflow Init

The new #[WorkflowInit] attribute has been added for the Workflow class constructor
This attribute allows you to receive arguments in the constructor that were passed when the Workflow was started.
The Workflow input arguments are also passed to your #[WorkflowMethod] method -- that always happens, whether or not you use the #[WorkflowInit] attribute.
This is useful if you have message handlers that need access to Workflow input: see Initializing the Workflow first.

use Temporal\Workflow;

#[Workflow\WorkflowInterface]
class GreetingExample
{
    private readonly string $nameWithTitle;
    private bool $titleHasBeenChecked;

    // Note the attribute is on a public constructor
    #[Workflow\WorkflowInit]
    public function __construct(string $input)
    {
        $this->nameWithTitle = 'Sir ' . $input;
        $this->titleHasBeenChecked = false;
    }

    #[Workflow\WorkflowMethod]
    public function getGreeting(string $input)
    {
        yield Workflow::await(fn() => $this->titleHasBeenChecked);
        return "Hello " . $this->nameWithTitle;
    }

    #[Workflow\UpdateMethod]
    public function checkTitleValidity()
    {
        // 👉 The handler is now guaranteed to see the workflow input
        // after it has been processed by the constructor.
        $isValid = yield Workflow::executeActivity('activity.checkTitleValidity', [$this->nameWithTitle]);
        $this->titleHasBeenChecked = true;
        return $isValid;
    }
}

Warning

By default, the Workflow Handler runs before Signals and Updates in PHP SDK v2. This behavior is incorrect.
To avoid breaking already written Workflows, since PHP SDK v2.11.0, a feature flag was added to enhance the behavior of the Workflow Handler.
Make sure to set this flag to true to enable the correct behavior.

Memo

Added a method to update the Workflow's Memo Workflow::upsertMemo.

Workflow::upsertMemo([
    'key1' => 'foo',
    'key2' => 42,
    'key3' => ['subkey1' => 'value']
    'key4' => null, // remove key4
});

MetaStorm metadata

To improve Developer Experience, metadata for the MetaStorm plugin has been added.
If you use MetaStorm, the IDE will now suggest Workflow classes and types in the corresponding methods.

image

Pull Requests

Full Changelog: v2.12.3...v2.13.0

v2.12.3

14 Jan 09:12
fc67190
Compare
Choose a tag to compare

What's Changed

  • Activated Acceptance extra tests; bugfixes by @roxblnfk in #551

Full Changelog: v2.12.2...v2.12.3